diff --git a/.env.example b/.env.example index 1d03143..24e0b3d 100644 --- a/.env.example +++ b/.env.example @@ -7,3 +7,4 @@ RPC_URL="http://localhost:8545" # Private key used to deploy contracts on the local chain PRIVATE_KEY="0x5fb92d6e98884f76de468fa3f6278f8807c48bebc13595d45af5bdc4da702133" +PRIVATE_KEYS="0x5fb92d6e98884f76de468fa3f6278f8807c48bebc13595d45af5bdc4da702133","0xe5be9a5092b81bca64be81d212e7f2f9eba183bb7a90954f7b76361f6edb5c0a" diff --git a/.gitignore b/.gitignore index 41912bb..a757374 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,7 @@ codegen/*.json codegen/abi/* codegen/pvm/* codegen/evm/* +codegen/libs/* *.sha256.txt # Benchmark artifacts diff --git a/benchmark/DEVELOPMENT.md b/benchmark/DEVELOPMENT.md new file mode 100644 index 0000000..e3a4478 --- /dev/null +++ b/benchmark/DEVELOPMENT.md @@ -0,0 +1,100 @@ +# Benchmark Development Guide + +Compare smart contract performance across different languages (Solidity, ink!, Rust) and VMs (EVM, PolkaVM). + +## Prerequisites + +Install required toolchains: + +```sh +# Deno runtime +curl -fsSL https://deno.land/install.sh | sh + +# ink! contracts +rustup component add rust-src +cargo install --force --locked --version 6.0.0-beta.1 cargo-contract + +# Rust PolkaVM contracts +cargo install --git https://github.com/paritytech/revive.git cargo-pvm --locked --force +``` + +**Note**: If ink! compilation fails with `panic_immediate_abort` errors, use toolchain 1.91.1. + +## Running Benchmarks + +Before running, start a development node: + +```sh +# For Revive testing +revive_dev_stack + +# For Geth testing (set RPC_URL=http://localhost:8545 in .env) +geth-dev +``` + +And build the necessary metadata: + +``` +deno task build +``` + +```sh +# 1. Build all contracts (ink!, Rust, Solidity) +./benchmark/contracts.ts --build + +# 2. Deploy and execute benchmark operations (requires running node) +./benchmark/contracts.ts --execute + +# 3. Generate reports in benchmark/reports/ +./benchmark/contracts.ts --report + +# Or run all steps together +./benchmark/contracts.ts --build --execute --report +``` + +## Adding New Contracts + +Edit `benchmark/contracts.ts` and add a new artifact: + +```typescript +{ + id: 'MyContract', + srcs: [ + ink('my_contract'), // ink/my_contract/ + rust('my_contract'), // rust/contracts/my_contract.rs + ...solidity('my_contract.sol', 'MyContract'), // contracts/my_contract.sol + ], + deploy: (id, name, bytecode) => { + return deployContract({ + name: { id, name }, + bytecode, + args: [], // Constructor arguments + }) + }, + calls: [ + { + name: 'my_operation', + exec: async (address) => { + return await env.wallet.writeContract({ + address, + abi: abis.MyContract, + functionName: 'myFunction', + args: [arg1, arg2], + }) + }, + }, + ], +} +``` + +Create the contract source files and run the benchmark. + +## Generated Reports + +Reports are saved to `benchmark/reports/`: + +- **opcode_analysis.md**: Opcode usage breakdown per transaction +- **contract_comparison.md**: Performance comparison across implementations +- **bytecode_size_comparison.md**: Compiled bytecode size comparison + +Metrics are stored in `stats.db` (SQLite) for custom analysis. diff --git a/benchmark/contracts.ts b/benchmark/contracts.ts index 2e8efd4..935b447 100755 --- a/benchmark/contracts.ts +++ b/benchmark/contracts.ts @@ -1,102 +1,21 @@ #!/usr/bin/env -S deno run --env-file --allow-all -import { deploy as deployContract, env } from '../tools/lib/index.ts' -import { abis } from '../codegen/abis.ts' +import { env } from '../tools/lib/index.ts' import { logger } from '../utils/logger.ts' -import { - Artifacts, - build, - deleteChainData, - deploy, - execute, - ink, - rust, - solidity, -} from './lib.ts' +import { build, deleteChainData, deploy, execute } from './lib.ts' import { parseArgs } from '@std/cli' -import { parseEther } from 'viem' -export const contracts: Artifacts = [ - { - id: 'Fibonacci', - srcs: [ - ink('fibonacci'), - rust('fibonacci'), - rust('fibonacci_u128'), - rust('fibonacci_u256'), - ...solidity('fibonacci.sol', 'Fibonacci'), - ], - deploy: (id, name, bytecode) => { - return deployContract({ - name: { id, name }, - bytecode, - args: [], - }) - }, - calls: [ - { - name: 'fib_10', - exec: async (address) => { - return await env.wallet.writeContract({ - address, - abi: abis.Fibonacci, - functionName: 'fibonacci', - args: [10], - }) - }, - }, - ], - }, - { - id: 'SimpleToken', - srcs: [ - ink('simple_token'), - rust('simple_token_no_alloc'), - ...solidity('simple_token.sol', 'SimpleToken'), - ], - deploy: (id, name, bytecode) => { - return deployContract({ - name: { id, name }, - bytecode, - args: [], - }) - }, - calls: [ - { - name: 'mint', - exec: (address) => { - return env.wallet.writeContract({ - address, - abi: abis.SimpleToken, - functionName: 'mint', - args: [ - env.wallet.account.address, - 10_000_000_000_000_000_000_000_000n, - ], - }) - }, - }, - { - name: 'transfer', - exec: async (address) => { - // fund destination first - await env.wallet.sendTransaction({ - to: '0x3d26c9637dFaB74141bA3C466224C0DBFDfF4A63', - value: parseEther('1'), - }) +// Import contract definitions +import { testContracts } from './contracts/test-contracts.ts' +import { ethereumContracts } from './contracts/ethereum-contracts.ts' - return env.wallet.writeContract({ - address, - abi: abis.SimpleToken, - functionName: 'transfer', - args: [ - '0x3d26c9637dFaB74141bA3C466224C0DBFDfF4A63', - 10_000_000_000_000_000_000_000_000n, - ], - }) - }, - }, - ], - }, +/** + * Combined contracts array for benchmarking + * - testContracts: Simple test contracts (Fibonacci, SimpleToken) + * - ethereumContracts: Real Ethereum contracts (USDT, WETH, USDC, XEN) + */ +export const contracts = [ + ...testContracts, + ...ethereumContracts, ] const cli = parseArgs(Deno.args, { diff --git a/benchmark/contracts/ethereum-contracts.ts b/benchmark/contracts/ethereum-contracts.ts new file mode 100644 index 0000000..8160fdd --- /dev/null +++ b/benchmark/contracts/ethereum-contracts.ts @@ -0,0 +1,299 @@ +import { env } from '../../tools/lib/index.ts' +import { abis } from '../../codegen/abis.ts' +import { Artifacts, loadAddresses, solidity } from '../lib.ts' +import { deploy as deployContract } from '../../tools/lib/index.ts' +import { uploadCodePVM } from '../../tools/lib/pvm.ts' + +const externalRecipient = '0x3d26c9637dFaB74141bA3C466224C0DBFDfF4A63' + +/** + * Real Ethereum contracts adapted for benchmarking + * These are production contracts deployed on Ethereum mainnet + */ +export const ethereumContracts: Artifacts = [ + { + id: 'TetherToken', + srcs: [...solidity('tether.sol', 'TetherToken')], + deploy: (id, name, bytecode) => { + return deployContract({ + name: { id, name }, + bytecode, + args: [100000000000n, 'Tether USD', 'USDT', 6n], + }) + }, + calls: [ + { + name: 'transfer', + exec: async (address) => { + return await env.wallet.writeContract({ + address, + abi: abis.TetherToken, + functionName: 'transfer', + args: [env.wallet2.account.address, 10n], + }) + }, + }, + { + name: 'approve', + exec: async (address) => { + return await env.wallet.writeContract({ + address, + abi: abis.TetherToken, + functionName: 'approve', + args: [env.wallet2.account.address, 100n], + }) + }, + }, + { + name: 'transferFrom', + exec: async (address) => { + return await env.wallet2.writeContract({ + address, + abi: abis.TetherToken, + functionName: 'transferFrom', + args: [ + env.wallet.account.address, + env.wallet2.account.address, + 10n, + ], + }) + }, + }, + ], + }, + { + id: 'WETH9', + srcs: [...solidity('weth.sol', 'WETH9')], + deploy: (id, name, bytecode) => { + return deployContract({ + name: { id, name }, + bytecode, + args: [], + }) + }, + calls: [ + { + name: 'deposit', + exec: async (address) => { + return await env.wallet.writeContract({ + address, + abi: abis.WETH9, + functionName: 'deposit', + args: [], + value: 1000000000000000000n, + }) + }, + }, + { + name: 'transfer', + exec: async (address) => { + return await env.wallet.writeContract({ + address, + abi: abis.WETH9, + functionName: 'transfer', + args: [env.wallet2.account.address, 10n], + }) + }, + }, + { + name: 'withdraw', + exec: async (address) => { + return await env.wallet.writeContract({ + address, + abi: abis.WETH9, + functionName: 'withdraw', + args: [10n], + }) + }, + }, + ], + }, + { + id: 'FiatTokenV2_2', + srcs: [...solidity('usdc_implementation.sol', 'FiatTokenV2_2')], + deploy: (id, name, bytecode) => { + return deployContract({ + name: { id, name }, + bytecode, + args: [], + }) + }, + calls: [], + }, + { + id: 'FiatTokenProxy', + srcs: [...solidity('usdc_proxy.sol', 'FiatTokenProxy')], + deploy: async (id, name, bytecode) => { + const addresses = await loadAddresses() + const implementationKey = name.endsWith('_pvm') + ? 'FiatTokenV2_2_pvm' + : 'FiatTokenV2_2_evm' + const implementation = addresses[implementationKey] + if (!implementation) { + throw new Error(`Missing ${implementationKey} address`) + } + return deployContract({ + name: { id, name }, + bytecode, + args: [implementation], + }) + }, + calls: [ + { + name: 'changeAdmin', + exec: async (address) => { + return await env.wallet.writeContract({ + address, + abi: abis.FiatTokenProxy, + functionName: 'changeAdmin', + args: [externalRecipient], + }) + }, + }, + { + name: 'initialize', + exec: async (address) => { + return await env.wallet.writeContract({ + address, + abi: abis.FiatTokenV2_2, + functionName: 'initialize', + args: [ + 'USD Coin', + 'USDC', + 'USD', + 6, + env.wallet.account.address, + env.wallet.account.address, + env.wallet.account.address, + env.wallet.account.address, + ], + }) + }, + }, + { + name: 'configureMinter', + exec: async (address) => { + return await env.wallet.writeContract({ + address, + abi: abis.FiatTokenV2_2, + functionName: 'configureMinter', + args: [env.wallet.account.address, 1_000_000n], + }) + }, + }, + { + name: 'mint', + exec: async (address) => { + return await env.wallet.writeContract({ + address, + abi: abis.FiatTokenV2_2, + functionName: 'mint', + args: [env.wallet.account.address, 1_000_000n], + }) + }, + }, + { + name: 'transfer', + exec: async (address) => { + return await env.wallet.writeContract({ + address, + abi: abis.FiatTokenV2_2, + functionName: 'transfer', + args: [env.wallet2.account.address, 10n], + }) + }, + }, + { + name: 'approve', + exec: async (address) => { + return await env.wallet.writeContract({ + address, + abi: abis.FiatTokenV2_2, + functionName: 'approve', + args: [env.wallet2.account.address, 100n], + }) + }, + }, + { + name: 'transferFrom', + exec: async (address) => { + return await env.wallet2.writeContract({ + address, + abi: abis.FiatTokenV2_2, + functionName: 'transferFrom', + args: [ + env.wallet.account.address, + env.wallet2.account.address, + 10n, + ], + }) + }, + }, + ], + }, + { + id: 'XENCrypto', + srcs: [...solidity('xen_token.sol', 'XENCrypto')], + deploy: (id, name, bytecode) => { + return deployContract({ + name: { id, name }, + bytecode, + args: [], + }) + }, + calls: [], + }, + { + id: 'CoinTool_App', + srcs: [...solidity('xen_minter.sol', 'CoinTool_App')], + deploy: (id, name, bytecode) => { + return deployContract({ + name: { id, name }, + bytecode, + args: [], + }) + }, + setup: async () => { + // Upload CoinTool_Proxy code to PVM before executing calls + // This is needed because the 't' function creates proxies using CREATE2 + if (env.chain.name !== 'Geth') { + const code = env.getByteCode('CoinTool_Proxy', 'polkavm') + await uploadCodePVM(code) + } + }, + calls: [ + { + name: 't', + exec: async (address) => { + const addresses = await loadAddresses() + let tokenAddress + + if (address === addresses.CoinTool_App_pvm) { + tokenAddress = addresses.XENCrypto_pvm + } else if (address === addresses.CoinTool_App_evm) { + tokenAddress = addresses.XENCrypto_evm + } + + if (!tokenAddress) { + throw new Error('Missing XENCrypto address') + } + + const tokenHex = tokenAddress.slice(2) // drop 0x + const data = '0x59635f6f000000000000000000000000' + + tokenHex + + '000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000249ff054df000000000000000000000000000000000000000000000000000000000000004600000000000000000000000000000000000000000000000000000000' + return await env.wallet.writeContract({ + address, + abi: abis.CoinTool_App, + functionName: 't', + args: [ + 50n, + data, + '0x01', + ], + }) + }, + }, + ], + }, +] diff --git a/benchmark/contracts/test-contracts.ts b/benchmark/contracts/test-contracts.ts new file mode 100644 index 0000000..dd330f3 --- /dev/null +++ b/benchmark/contracts/test-contracts.ts @@ -0,0 +1,91 @@ +import { env } from '../../tools/lib/index.ts' +import { abis } from '../../codegen/abis.ts' +import { Artifacts, ink, rust, solidity } from '../lib.ts' +import { deploy as deployContract } from '../../tools/lib/index.ts' +import { parseEther } from 'viem' + +const externalRecipient = '0x3d26c9637dFaB74141bA3C466224C0DBFDfF4A63' + +export const testContracts: Artifacts = [ + { + id: 'Fibonacci', + srcs: [ + ink('fibonacci'), + rust('fibonacci'), + rust('fibonacci_u128'), + rust('fibonacci_u256'), + ...solidity('fibonacci.sol', 'Fibonacci'), + ], + deploy: (id, name, bytecode) => { + return deployContract({ + name: { id, name }, + bytecode, + args: [], + }) + }, + calls: [ + { + name: 'fib_10', + exec: async (address) => { + return await env.wallet.writeContract({ + address, + abi: abis.Fibonacci, + functionName: 'fibonacci', + args: [10], + }) + }, + }, + ], + }, + { + id: 'SimpleToken', + srcs: [ + ink('simple_token'), + rust('simple_token_no_alloc'), + ...solidity('simple_token.sol', 'SimpleToken'), + ], + deploy: (id, name, bytecode) => { + return deployContract({ + name: { id, name }, + bytecode, + args: [], + }) + }, + calls: [ + { + name: 'mint', + exec: (address) => { + return env.wallet.writeContract({ + address, + abi: abis.SimpleToken, + functionName: 'mint', + args: [ + env.wallet.account.address, + 10_000_000_000_000_000_000_000_000n, + ], + }) + }, + }, + { + name: 'transfer', + exec: async (address) => { + // fund destination first + await env.wallet.sendTransaction({ + to: externalRecipient, + value: parseEther('1'), + }) + + return env.wallet.writeContract({ + address, + abi: abis.SimpleToken, + functionName: 'transfer', + args: [ + externalRecipient, + 10_000_000_000_000_000_000_000_000n, + ], + }) + }, + }, + ], + }, +] diff --git a/benchmark/lib.ts b/benchmark/lib.ts index 2129f00..ce201f1 100644 --- a/benchmark/lib.ts +++ b/benchmark/lib.ts @@ -1,8 +1,8 @@ import { Hex, TransactionReceipt } from 'viem' -import { env } from '../tools/lib/index.ts' -import { Abis } from '../codegen/abis.ts' +import { deploy as deployContract, env } from '../tools/lib/index.ts' +import type { Abis } from '../codegen/abis.ts' import { readBytecode } from '../utils/index.ts' -import { compile } from '../utils/build.ts' +import { compile, LinkReferences } from '../utils/build.ts' import { join } from '@std/path' import { logger } from '../utils/logger.ts' import { Buffer } from 'node:buffer' @@ -78,45 +78,57 @@ export function ink(name: string): ContractInfo { export function solidity(fileName: string, name: string): ContractInfo[] { const bytecodes = { pvm: 'polkavm', evm: 'bin' } as const - let buildRun = false + const buildRun = { evm: false, pvm: false } + const libraryLinks = { + evm: undefined as LinkReferences | undefined, + pvm: undefined as LinkReferences | undefined, + } - return Object.entries(bytecodes).map(([type, ext]) => ({ - supportEvm() { - return type == 'evm' - }, - getName() { - return `${name}_${type}` - }, - getBytecode() { - return readBytecode(`./codegen/${type}/${name}.${ext}`) - }, - async build() { - if (buildRun) return - buildRun = true - - const rootDir = join(import.meta.dirname!, '..') - const contractsDir = join(rootDir, 'contracts') - const sourceFilePath = join(contractsDir, fileName) - const sourceContent = Deno.readTextFileSync(sourceFilePath) - - // Compile with resolc for PVM - await compile({ - fileName, - sourceContent, - rootDir, - compiler: 'resolc', - }) + return Object.entries(bytecodes).map(([type, ext]) => { + // Type-specific configuration + const buildType = type as 'evm' | 'pvm' + const isEvm = type === 'evm' + const compiler = isEvm ? 'solc' : 'resolc' - // Compile with solc for EVM - await compile({ - fileName, - sourceContent, - rootDir, - compiler: 'solc', - generateAbi: true, - }) - }, - })) + const contract = { + supportEvm() { + return isEvm + }, + getName() { + return `${name}_${type}` + }, + getBytecode() { + return readBytecode(`./codegen/${type}/${name}.${ext}`) + }, + async build() { + const libs = libraryLinks[buildType] + const shouldBuild = !buildRun[buildType] || libs + + if (!shouldBuild) return + + const rootDir = join(import.meta.dirname!, '..') + const contractsDir = join(rootDir, 'contracts') + const sourceFilePath = join(contractsDir, fileName) + const sourceContent = Deno.readTextFileSync(sourceFilePath) + + await compile({ + fileName, + sources: { [fileName]: { content: sourceContent } }, + rootDir, + compiler, + generateAbi: isEvm, + libraries: libs, + }) + + buildRun[buildType] = true + }, + + setLibraries(libs: LinkReferences) { + libraryLinks[buildType] = libs + }, + } + return contract + }) } export function rust(name: string): ContractInfo { @@ -158,6 +170,7 @@ export type Artifacts = Array<{ name: string exec: (address: Hex) => Promise }> + setup?: () => Promise }> export function deleteChainData(chainName: string) { @@ -188,7 +201,71 @@ export async function build(contracts: Artifacts) { } } +async function resolveLibraryLinks( + src: ContractInfo, + deployedAddresses: Record, +) { + // currently we only support library linking for solidity contracts + const solidityContract = src as ContractInfo & { + setLibraries?: (libs: LinkReferences) => void + } + if (!solidityContract.setLibraries) return + + const contractName = src.getName() + const match = contractName.match(/_(evm|pvm)$/) + + const suffix = match?.[0] ?? '' + const baseName = suffix + ? contractName.slice(0, -suffix.length) + : contractName + const type = src.supportEvm() ? 'evm' : 'pvm' + + // Load library dependencies + const { libs } = await import('../codegen/libs.ts') + if (!(baseName in libs)) return // contract does not require library linking + + const lib = libs[baseName as keyof typeof libs] + const libRefs: LinkReferences = {} + + for (const [libSrc, LibContracts] of Object.entries(lib)) { + for (const libName of LibContracts) { + let libAddress = deployedAddresses[`${libName}${suffix}`] + if (!libAddress) { + // not deployed library -> deploy it + const ext = src.supportEvm() ? 'bin' : 'polkavm' + const receipt = await deployContract({ + name: { + id: libName as keyof Abis, + name: `${libName}${suffix}`, + }, + args: [], + bytecode: readBytecode( + `./codegen/${type}/${libName}.${ext}`, + ), + }) + if (!receipt.contractAddress) { + throw new Error( + `${contractName} library link: Unable to deploy library contract ${libName}`, + ) + } + libAddress = receipt.contractAddress + // Track deployed library to avoid redeployment + deployedAddresses[`${libName}${suffix}`] = libAddress + } + libRefs[libSrc] ??= {} + libRefs[libSrc][libName] = libAddress + } + } + + // Set libraries and trigger recompilation + solidityContract.setLibraries(libRefs) + await src.build() +} + export async function deploy(contracts: Artifacts) { + // Track deployed libraries to avoid redeployment within this session + const deployedAddresses: Record = {} + for (const artifact of contracts) { const srcs = env.chain.name == 'Geth' ? artifact.srcs.filter((src) => src.supportEvm()) @@ -197,7 +274,7 @@ export async function deploy(contracts: Artifacts) { for (const src of srcs) { const contract = src.getName() logger.debug(`Deploying ${contract}...`) - + await resolveLibraryLinks(src, deployedAddresses) const receipt = await artifact.deploy( artifact.id as keyof Abis, contract, @@ -211,14 +288,17 @@ export async function deploy(contracts: Artifacts) { receipt.transactionHash, ) } + + if (artifact.setup) { + logger.debug(`Running setup for ${artifact.id}...`) + await artifact.setup() + } } } export async function execute(contracts: Artifacts) { - const addresses = await import('../codegen/addresses.ts') as Record< - string, - Hex - > + // Use loadAddresses to avoid module caching issues + const addresses = await loadAddresses() for (const artifact of contracts) { const srcs = env.chain.name == 'Geth' @@ -339,3 +419,9 @@ INSERT OR REPLACE INTO transaction_steps ( throw error } } + +export async function loadAddresses(): Promise> { + // Add cache busting to get fresh addresses + const mod = await import(`../codegen/addresses.ts?t=${Date.now()}`) + return mod as Record +} diff --git a/benchmark/reports.ts b/benchmark/reports.ts index ef9c098..f4e6172 100644 --- a/benchmark/reports.ts +++ b/benchmark/reports.ts @@ -45,7 +45,7 @@ async function generateOpcodeAnalysis() { GROUP BY t.hash, t.chain_name, s.op ORDER BY - t.chain_name, t.contract_id, t.contract_name, t.transaction_name, total_gas_cost DESC + t.chain_name, t.contract_id, t.contract_name, t.transaction_name, total_weight_cost_ref_time DESC, total_weight_cost_proof_size DESC, total_gas_cost DESC `).all() as Array<{ chain_name: string contract_id: string diff --git a/benchmark/reports/bytecode_size_comparison.md b/benchmark/reports/bytecode_size_comparison.md index 3658e28..319ee47 100644 --- a/benchmark/reports/bytecode_size_comparison.md +++ b/benchmark/reports/bytecode_size_comparison.md @@ -1,23 +1,65 @@ # Bytecode Size Comparison -Generated on: 2025-12-19 +Generated on: 2026-01-27 ## Fibonacci Implementations | Contract | VM Type | Size (bytes) | vs Smallest | | ------------------- | ------- | ------------ | ----------- | -| fibonacci_u32_rust | PVM | 220 | - | -| fibonacci_u128_rust | PVM | 296 | +34.5% | -| Fibonacci_evm | EVM | 391 | +77.7% | -| fibonacci_u256_rust | PVM | 698 | +217.3% | -| fibonacci_ink | PVM | 954 | +333.6% | -| Fibonacci_pvm | PVM | 1,890 | +759.1% | +| fibonacci_rust | PVM | 209 | - | +| fibonacci_u128_rust | PVM | 285 | +36.4% | +| Fibonacci_evm | EVM | 300 | +43.5% | +| fibonacci_u256_rust | PVM | 686 | +228.2% | +| fibonacci_ink | PVM | 1,102 | +427.3% | +| Fibonacci_pvm | PVM | 1,546 | +639.7% | ## SimpleToken Implementations | Contract | VM Type | Size (bytes) | vs Smallest | | -------------------------- | ------- | ------------ | ----------- | -| SimpleToken_evm | EVM | 588 | - | -| simple_token_no_alloc_rust | PVM | 4,370 | +643.2% | -| SimpleToken_pvm | PVM | 5,241 | +791.3% | -| simple_token_ink | PVM | 7,008 | +1091.8% | +| SimpleToken_evm | EVM | 487 | - | +| simple_token_no_alloc_rust | PVM | 4,360 | +795.3% | +| SimpleToken_pvm | PVM | 5,073 | +941.7% | +| simple_token_ink | PVM | 7,251 | +1388.9% | + +## TetherToken Implementations + +| Contract | VM Type | Size (bytes) | vs Smallest | +| --------------- | ------- | ------------ | ----------- | +| TetherToken_evm | EVM | 5,618 | - | +| TetherToken_pvm | PVM | 54,619 | +872.2% | + +## WETH9 Implementations + +| Contract | VM Type | Size (bytes) | vs Smallest | +| --------- | ------- | ------------ | ----------- | +| WETH9_evm | EVM | 2,012 | - | +| WETH9_pvm | PVM | 17,196 | +754.7% | + +## FiatTokenV2_2 Implementations + +| Contract | VM Type | Size (bytes) | vs Smallest | +| ----------------- | ------- | ------------ | ----------- | +| FiatTokenV2_2_evm | EVM | 15,467 | - | +| FiatTokenV2_2_pvm | PVM | 103,545 | +569.5% | + +## FiatTokenProxy Implementations + +| Contract | VM Type | Size (bytes) | vs Smallest | +| ------------------ | ------- | ------------ | ----------- | +| FiatTokenProxy_evm | EVM | 1,643 | - | +| FiatTokenProxy_pvm | PVM | 9,688 | +489.7% | + +## XENCrypto Implementations + +| Contract | VM Type | Size (bytes) | vs Smallest | +| ------------- | ------- | ------------ | ----------- | +| XENCrypto_evm | EVM | 10,489 | - | +| XENCrypto_pvm | PVM | 112,648 | +974.0% | + +## CoinTool_App Implementations + +| Contract | VM Type | Size (bytes) | vs Smallest | +| ---------------- | ------- | ------------ | ----------- | +| CoinTool_App_evm | EVM | 3,233 | - | +| CoinTool_App_pvm | PVM | 29,230 | +804.1% | diff --git a/benchmark/reports/contract_comparison.md b/benchmark/reports/contract_comparison.md index f1b15f2..1e498ff 100644 --- a/benchmark/reports/contract_comparison.md +++ b/benchmark/reports/contract_comparison.md @@ -1,99 +1,348 @@ # Revive Contract Comparison -Generated on: 2026-01-13 +Generated on: 2026-01-27 Comparison of gas usage across different contract implementations. ## Chain: Geth +### CoinTool_App - deploy + +| Implementation | gas_used | vs Best | % metered | +| ---------------- | -------- | ------- | --------- | +| CoinTool_App_evm | 756,027 | - | 3.0% | + +### CoinTool_App - t + +| Implementation | gas_used | vs Best | % metered | +| ---------------- | ---------- | ------- | --------- | +| CoinTool_App_evm | 11,604,727 | - | 4.5% | + +### FiatTokenProxy - approve + +| Implementation | gas_used | vs Best | % metered | +| ------------------ | -------- | ------- | --------- | +| FiatTokenProxy_evm | 55,331 | - | 100.8% | + +### FiatTokenProxy - changeAdmin + +| Implementation | gas_used | vs Best | % metered | +| ------------------ | -------- | ------- | --------- | +| FiatTokenProxy_evm | 28,326 | - | 15.4% | + +### FiatTokenProxy - configureMinter + +| Implementation | gas_used | vs Best | % metered | +| ------------------ | -------- | ------- | --------- | +| FiatTokenProxy_evm | 79,482 | - | 99.7% | + +### FiatTokenProxy - deploy + +| Implementation | gas_used | vs Best | % metered | +| ------------------ | -------- | ------- | --------- | +| FiatTokenProxy_evm | 394,714 | - | 6.3% | + +### FiatTokenProxy - initialize + +| Implementation | gas_used | vs Best | % metered | +| ------------------ | -------- | ------- | --------- | +| FiatTokenProxy_evm | 212,439 | - | 88.0% | + +### FiatTokenProxy - mint + +| Implementation | gas_used | vs Best | % metered | +| ------------------ | -------- | ------- | --------- | +| FiatTokenProxy_evm | 83,152 | - | 81.1% | + +### FiatTokenProxy - transfer + +| Implementation | gas_used | vs Best | % metered | +| ------------------ | -------- | ------- | --------- | +| FiatTokenProxy_evm | 38,065 | - | 46.1% | + +### FiatTokenProxy - transferFrom + +| Implementation | gas_used | vs Best | % metered | +| ------------------ | -------- | ------- | --------- | +| FiatTokenProxy_evm | 43,789 | - | 57.8% | + +### FiatTokenV2_2 - deploy + +| Implementation | gas_used | vs Best | % metered | +| ----------------- | --------- | ------- | --------- | +| FiatTokenV2_2_evm | 3,412,411 | - | 0.2% | + ### Fibonacci - deploy | Implementation | gas_used | vs Best | % metered | | -------------- | -------- | ------- | --------- | | Fibonacci_evm | 112,683 | - | 0.1% | - ### Fibonacci - fib_10 | Implementation | gas_used | vs Best | % metered | | -------------- | -------- | ------- | --------- | | Fibonacci_evm | 47,664 | - | 0.2% | - ### SimpleToken - deploy | Implementation | gas_used | vs Best | % metered | | --------------- | -------- | ------- | --------- | | SimpleToken_evm | 152,967 | - | 0.1% | - ### SimpleToken - mint | Implementation | gas_used | vs Best | % metered | | --------------- | -------- | ------- | --------- | | SimpleToken_evm | 68,105 | - | 35.3% | - ### SimpleToken - transfer | Implementation | gas_used | vs Best | % metered | | --------------- | -------- | ------- | --------- | | SimpleToken_evm | 46,257 | - | 51.9% | +### TetherToken - approve + +| Implementation | gas_used | vs Best | % metered | +| --------------- | -------- | ------- | --------- | +| TetherToken_evm | 48,276 | - | 49.7% | + +### TetherToken - deploy + +| Implementation | gas_used | vs Best | % metered | +| --------------- | --------- | ------- | --------- | +| TetherToken_evm | 1,208,745 | - | 0.3% | + +### TetherToken - transfer + +| Implementation | gas_used | vs Best | % metered | +| --------------- | -------- | ------- | --------- | +| TetherToken_evm | 37,754 | - | 5.6% | + +### TetherToken - transferFrom + +| Implementation | gas_used | vs Best | % metered | +| --------------- | -------- | ------- | --------- | +| TetherToken_evm | 43,290 | - | 4.9% | + +### WETH9 - deploy + +| Implementation | gas_used | vs Best | % metered | +| -------------- | -------- | ------- | --------- | +| WETH9_evm | 487,741 | - | 4.6% | + +### WETH9 - deposit + +| Implementation | gas_used | vs Best | % metered | +| -------------- | -------- | ------- | --------- | +| WETH9_evm | 44,978 | - | 52.5% | + +### WETH9 - transfer + +| Implementation | gas_used | vs Best | % metered | +| -------------- | -------- | ------- | --------- | +| WETH9_evm | 26,613 | - | 7.9% | + +### WETH9 - withdraw + +| Implementation | gas_used | vs Best | % metered | +| -------------- | -------- | ------- | --------- | +| WETH9_evm | 34,953 | - | 38.9% | + +### XENCrypto - deploy + +| Implementation | gas_used | vs Best | % metered | +| -------------- | --------- | ------- | --------- | +| XENCrypto_evm | 2,221,205 | - | 1.2% | ## Chain: eth-rpc +### CoinTool_App - deploy + +| Implementation | ref_time | vs Best | % metered | pov | +| ---------------- | ------------- | ------- | --------- | ------ | +| CoinTool_App_evm | 1,812,498,168 | - | 13.5% | 27,480 | +| CoinTool_App_pvm | 2,352,084,109 | +29.8% | 15.8% | 27,448 | + +### CoinTool_App - t + +| Implementation | ref_time | vs Best | % metered | pov | +| ---------------- | --------------- | ------- | --------- | ---------- | +| CoinTool_App_pvm | 119,832,667,856 | - | 99.2% | 10,828,951 | +| CoinTool_App_evm | 191,611,648,659 | +59.9% | 99.5% | 9,138,203 | + +### FiatTokenProxy - approve + +| Implementation | ref_time | vs Best | % metered | pov | +| ------------------ | ------------- | ------- | --------- | ------- | +| FiatTokenProxy_evm | 1,466,337,726 | - | 37.7% | 54,554 | +| FiatTokenProxy_pvm | 1,737,192,853 | +18.5% | 47.4% | 167,787 | + +### FiatTokenProxy - changeAdmin + +| Implementation | ref_time | vs Best | % metered | pov | +| ------------------ | ------------- | ------- | --------- | ------ | +| FiatTokenProxy_evm | 1,254,022,241 | - | 27.2% | 39,299 | +| FiatTokenProxy_pvm | 1,307,939,666 | +4.3% | 30.2% | 48,987 | + +### FiatTokenProxy - configureMinter + +| Implementation | ref_time | vs Best | % metered | pov | +| ------------------ | ------------- | ------- | --------- | ------- | +| FiatTokenProxy_evm | 1,779,882,018 | - | 48.7% | 85,389 | +| FiatTokenProxy_pvm | 2,051,259,329 | +15.2% | 55.5% | 198,590 | + +### FiatTokenProxy - deploy + +| Implementation | ref_time | vs Best | % metered | pov | +| ------------------ | ------------- | ------- | --------- | ------ | +| FiatTokenProxy_evm | 1,955,027,326 | - | 21.0% | 31,458 | +| FiatTokenProxy_pvm | 2,111,820,114 | +8.0% | 20.9% | 31,458 | + +### FiatTokenProxy - initialize + +| Implementation | ref_time | vs Best | % metered | pov | +| ------------------ | ------------- | ------- | --------- | ------- | +| FiatTokenProxy_evm | 3,467,091,226 | - | 73.5% | 230,401 | +| FiatTokenProxy_pvm | 3,904,751,659 | +12.6% | 76.5% | 343,346 | + +### FiatTokenProxy - mint + +| Implementation | ref_time | vs Best | % metered | pov | +| ------------------ | ------------- | ------- | --------- | ------- | +| FiatTokenProxy_evm | 2,414,524,981 | - | 62.2% | 147,123 | +| FiatTokenProxy_pvm | 2,825,972,306 | +17.0% | 67.7% | 260,293 | + +### FiatTokenProxy - transfer + +| Implementation | ref_time | vs Best | % metered | pov | +| ------------------ | ------------- | ------- | --------- | ------- | +| FiatTokenProxy_evm | 2,253,109,736 | - | 59.5% | 136,898 | +| FiatTokenProxy_pvm | 2,630,114,247 | +16.7% | 65.3% | 250,131 | + +### FiatTokenProxy - transferFrom + +| Implementation | ref_time | vs Best | % metered | pov | +| ------------------ | ------------- | ------- | --------- | ------- | +| FiatTokenProxy_evm | 2,736,718,638 | - | 66.6% | 178,150 | +| FiatTokenProxy_pvm | 3,239,499,195 | +18.4% | 71.8% | 291,383 | + +### FiatTokenV2_2 - deploy + +| Implementation | ref_time | vs Best | % metered | pov | +| ----------------- | ------------- | ------- | --------- | ------ | +| FiatTokenV2_2_evm | 2,418,189,787 | - | 27.1% | 58,283 | +| FiatTokenV2_2_pvm | 3,837,783,006 | +58.7% | 17.8% | 58,477 | + ### Fibonacci - deploy | Implementation | ref_time | vs Best | % metered | pov | | ------------------- | ------------- | ------- | --------- | ------ | -| fibonacci_rust | 1,520,994,099 | - | 0.0% | 6,934 | -| fibonacci_u128_rust | 1,522,196,875 | +0.1% | 0.0% | 6,934 | -| Fibonacci_evm | 1,525,205,124 | +0.3% | 0.1% | 6,934 | -| fibonacci_u256_rust | 1,528,558,927 | +0.5% | 0.0% | 6,934 | -| Fibonacci_pvm | 1,557,918,056 | +2.4% | 0.7% | 6,934 | -| fibonacci_ink | 1,718,162,422 | +13.0% | 10.8% | 17,191 | - +| fibonacci_rust | 1,520,820,013 | - | 0.0% | 6,934 | +| fibonacci_u128_rust | 1,522,022,789 | +0.1% | 0.0% | 6,934 | +| Fibonacci_evm | 1,523,685,771 | +0.2% | 0.1% | 6,934 | +| fibonacci_u256_rust | 1,528,369,015 | +0.5% | 0.0% | 6,934 | +| Fibonacci_pvm | 1,552,523,142 | +2.1% | 0.7% | 6,934 | +| fibonacci_ink | 1,720,504,670 | +13.1% | 10.8% | 17,191 | ### Fibonacci - fib_10 | Implementation | ref_time | vs Best | % metered | pov | | ------------------- | ------------- | ------- | --------- | ------ | -| fibonacci_rust | 1,003,971,839 | - | 9.1% | 8,652 | -| fibonacci_u128_rust | 1,096,536,651 | +9.2% | 16.7% | 8,728 | -| fibonacci_ink | 1,149,918,571 | +14.5% | 20.6% | 19,643 | -| Fibonacci_evm | 1,519,150,709 | +51.3% | 39.9% | 8,432 | -| Fibonacci_pvm | 1,587,091,712 | +58.1% | 42.5% | 10,322 | -| fibonacci_u256_rust | 2,512,824,851 | +150.3% | 63.7% | 9,130 | - +| fibonacci_rust | 1,003,955,988 | - | 9.0% | 8,641 | +| fibonacci_u128_rust | 1,096,520,800 | +9.2% | 16.7% | 8,717 | +| fibonacci_ink | 1,151,214,899 | +14.7% | 20.7% | 19,791 | +| Fibonacci_evm | 1,317,572,855 | +31.2% | 30.7% | 8,432 | +| Fibonacci_pvm | 1,365,307,158 | +36.0% | 33.1% | 9,978 | +| fibonacci_u256_rust | 2,512,807,559 | +150.3% | 63.7% | 9,118 | ### SimpleToken - deploy | Implementation | ref_time | vs Best | % metered | pov | | -------------------------- | ------------- | ------- | --------- | ------ | -| SimpleToken_evm | 1,528,362,837 | - | 0.1% | 6,934 | -| simple_token_no_alloc_rust | 1,586,671,999 | +3.8% | 0.0% | 6,934 | -| SimpleToken_pvm | 1,610,852,522 | +5.4% | 0.6% | 6,934 | -| simple_token_ink | 1,843,184,400 | +20.6% | 11.7% | 17,191 | - +| SimpleToken_evm | 1,526,683,194 | - | 0.1% | 6,934 | +| simple_token_no_alloc_rust | 1,586,513,739 | +3.9% | 0.0% | 6,934 | +| SimpleToken_pvm | 1,608,242,984 | +5.3% | 0.7% | 6,934 | +| simple_token_ink | 1,847,374,728 | +21.0% | 11.6% | 17,191 | ### SimpleToken - mint | Implementation | ref_time | vs Best | % metered | pov | | -------------------------- | ------------- | ------- | --------- | ------ | -| SimpleToken_evm | 1,431,551,949 | - | 36.2% | 49,623 | -| SimpleToken_pvm | 1,497,388,823 | +4.6% | 39.0% | 54,800 | -| simple_token_no_alloc_rust | 1,505,513,738 | +5.2% | 39.3% | 53,929 | -| simple_token_ink | 1,687,565,485 | +17.9% | 45.9% | 56,631 | - +| SimpleToken_evm | 1,428,980,355 | - | 36.1% | 49,623 | +| SimpleToken_pvm | 1,495,226,765 | +4.6% | 38.9% | 54,632 | +| simple_token_no_alloc_rust | 1,505,548,558 | +5.4% | 39.3% | 53,919 | +| simple_token_ink | 1,688,260,258 | +18.1% | 45.9% | 56,874 | ### SimpleToken - transfer | Implementation | ref_time | vs Best | % metered | pov | | -------------------------- | ------------- | ------- | --------- | ------ | -| SimpleToken_evm | 1,461,245,351 | - | 37.5% | 49,655 | -| SimpleToken_pvm | 1,554,253,981 | +6.4% | 41.2% | 54,961 | -| simple_token_no_alloc_rust | 1,562,060,164 | +6.9% | 41.5% | 53,961 | -| simple_token_ink | 2,012,500,642 | +37.7% | 54.6% | 77,209 | +| SimpleToken_evm | 1,460,054,677 | - | 37.4% | 49,655 | +| SimpleToken_pvm | 1,553,568,823 | +6.4% | 41.2% | 54,793 | +| simple_token_no_alloc_rust | 1,562,094,984 | +7.0% | 41.5% | 53,951 | +| simple_token_ink | 2,014,032,325 | +37.9% | 54.6% | 77,452 | + +### TetherToken - approve + +| Implementation | ref_time | vs Best | % metered | pov | +| --------------- | ------------- | ------- | --------- | ------ | +| TetherToken_evm | 1,307,318,598 | - | 30.1% | 39,366 | +| TetherToken_pvm | 1,477,151,338 | +13.0% | 38.2% | 93,921 | + +### TetherToken - deploy + +| Implementation | ref_time | vs Best | % metered | pov | +| --------------- | ------------- | ------- | --------- | ------- | +| TetherToken_evm | 3,536,168,883 | - | 54.5% | 150,692 | +| TetherToken_pvm | 4,547,576,316 | +28.6% | 47.6% | 150,951 | + +### TetherToken - transfer + +| Implementation | ref_time | vs Best | % metered | pov | +| --------------- | ------------- | ------- | --------- | ------- | +| TetherToken_evm | 1,850,629,748 | - | 50.6% | 101,132 | +| TetherToken_pvm | 2,055,388,174 | +11.1% | 55.6% | 155,623 | + +### TetherToken - transferFrom + +| Implementation | ref_time | vs Best | % metered | pov | +| --------------- | ------------- | ------- | --------- | ------- | +| TetherToken_evm | 2,150,947,725 | - | 57.5% | 121,806 | +| TetherToken_pvm | 2,415,750,101 | +12.3% | 62.2% | 176,297 | + +### WETH9 - deploy + +| Implementation | ref_time | vs Best | % metered | pov | +| -------------- | ------------- | ------- | --------- | ------ | +| WETH9_evm | 2,275,464,063 | - | 31.9% | 68,572 | +| WETH9_pvm | 2,549,508,821 | +12.0% | 29.8% | 68,476 | + +### WETH9 - deposit + +| Implementation | ref_time | vs Best | % metered | pov | +| -------------- | ------------- | ------- | --------- | ------ | +| WETH9_evm | 1,188,685,343 | - | 23.2% | 28,906 | +| WETH9_pvm | 1,259,534,400 | +6.0% | 27.5% | 46,070 | + +### WETH9 - transfer + +| Implementation | ref_time | vs Best | % metered | pov | +| -------------- | ------------- | ------- | --------- | ------ | +| WETH9_evm | 1,532,612,772 | - | 40.4% | 59,976 | +| WETH9_pvm | 1,655,399,731 | +8.0% | 44.8% | 77,172 | + +### WETH9 - withdraw + +| Implementation | ref_time | vs Best | % metered | pov | +| -------------- | ------------- | ------- | --------- | ------ | +| WETH9_evm | 1,723,148,219 | - | 47.0% | 44,893 | +| WETH9_pvm | 1,814,506,569 | +5.3% | 49.7% | 62,089 | +### XENCrypto - deploy +| Implementation | ref_time | vs Best | % metered | pov | +| -------------- | ------------- | ------- | --------- | ------ | +| XENCrypto_evm | 2,350,323,858 | - | 28.4% | 58,283 | +| XENCrypto_pvm | 4,190,424,438 | +78.3% | 21.2% | 58,219 | diff --git a/benchmark/reports/opcode_analysis.md b/benchmark/reports/opcode_analysis.md index de5b0d5..7308751 100644 --- a/benchmark/reports/opcode_analysis.md +++ b/benchmark/reports/opcode_analysis.md @@ -1,9 +1,306 @@ # Opcode Analysis -Generated on: 2026-01-13 +Generated on: 2026-01-27 ## Chain: Geth +### CoinTool_App_evm - deploy + +- **Total Gas Used:** 756,027 + +| Opcode | Total Gas | Call Count | Avg Gas/Call | % of opcodes | % of tx Gas | +| -------- | --------- | ---------- | ------------ | ------------ | ----------- | +| SSTORE | 20,000 | 1 | 20000.0 | 87.9% | 2.6% | +| SLOAD | 2,100 | 1 | 2100.0 | 9.2% | 0.3% | +| CODECOPY | 618 | 1 | 618.0 | 2.7% | 0.1% | +| ADD | 3 | 1 | 3.0 | 0.0% | 0.0% | +| AND | 3 | 1 | 3.0 | 0.0% | 0.0% | +| DUP1 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| DUP2 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| DUP3 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| MLOAD | 3 | 1 | 3.0 | 0.0% | 0.0% | +| MSTORE | 3 | 1 | 3.0 | 0.0% | 0.0% | +| NOT | 3 | 1 | 3.0 | 0.0% | 0.0% | +| OR | 3 | 1 | 3.0 | 0.0% | 0.0% | +| PUSH1 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| PUSH2 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| SHL | 3 | 1 | 3.0 | 0.0% | 0.0% | +| SUB | 3 | 1 | 3.0 | 0.0% | 0.0% | +| SWAP1 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| ADDRESS | 2 | 1 | 2.0 | 0.0% | 0.0% | +| ORIGIN | 2 | 1 | 2.0 | 0.0% | 0.0% | +| PUSH0 | 2 | 1 | 2.0 | 0.0% | 0.0% | + +### CoinTool_App_evm - t + +- **Total Gas Used:** 11,604,727 + +| Opcode | Total Gas | Call Count | Avg Gas/Call | % of opcodes | % of tx Gas | +| ------------ | --------- | ---------- | ------------ | ------------ | ----------- | +| CALL | 239,867 | 1 | 239867.0 | 45.6% | 2.1% | +| DELEGATECALL | 232,309 | 1 | 232309.0 | 44.1% | 2.0% | +| CREATE2 | 32,120 | 1 | 32120.0 | 6.1% | 0.3% | +| SSTORE | 20,000 | 1 | 20000.0 | 3.8% | 0.2% | +| LOG2 | 1,637 | 1 | 1637.0 | 0.3% | 0.0% | +| SLOAD | 100 | 1 | 100.0 | 0.0% | 0.0% | +| CODECOPY | 63 | 1 | 63.0 | 0.0% | 0.0% | +| KECCAK256 | 42 | 1 | 42.0 | 0.0% | 0.0% | +| JUMPI | 10 | 1 | 10.0 | 0.0% | 0.0% | +| JUMP | 8 | 1 | 8.0 | 0.0% | 0.0% | +| CALLDATACOPY | 6 | 1 | 6.0 | 0.0% | 0.0% | +| DIV | 5 | 1 | 5.0 | 0.0% | 0.0% | +| MUL | 5 | 1 | 5.0 | 0.0% | 0.0% | +| ADD | 3 | 1 | 3.0 | 0.0% | 0.0% | +| AND | 3 | 1 | 3.0 | 0.0% | 0.0% | +| CALLDATALOAD | 3 | 1 | 3.0 | 0.0% | 0.0% | +| DUP1 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| DUP10 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| DUP14 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| DUP2 | 3 | 1 | 3.0 | 0.0% | 0.0% | + +### FiatTokenProxy_evm - approve + +- **Total Gas Used:** 55,331 + +| Opcode | Total Gas | Call Count | Avg Gas/Call | % of opcodes | % of tx Gas | +| -------------- | --------- | ---------- | ------------ | ------------ | ----------- | +| DELEGATECALL | 29,648 | 1 | 29648.0 | 53.2% | 53.6% | +| SSTORE | 22,100 | 1 | 22100.0 | 39.7% | 39.9% | +| SLOAD | 2,100 | 1 | 2100.0 | 3.8% | 3.8% | +| LOG3 | 1,756 | 1 | 1756.0 | 3.2% | 3.2% | +| KECCAK256 | 42 | 1 | 42.0 | 0.1% | 0.1% | +| CALLDATACOPY | 12 | 1 | 12.0 | 0.0% | 0.0% | +| JUMPI | 10 | 1 | 10.0 | 0.0% | 0.0% | +| JUMP | 8 | 1 | 8.0 | 0.0% | 0.0% | +| CODECOPY | 6 | 1 | 6.0 | 0.0% | 0.0% | +| RETURNDATACOPY | 6 | 1 | 6.0 | 0.0% | 0.0% | +| ADD | 3 | 1 | 3.0 | 0.0% | 0.0% | +| AND | 3 | 1 | 3.0 | 0.0% | 0.0% | +| CALLDATALOAD | 3 | 1 | 3.0 | 0.0% | 0.0% | +| DUP1 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| DUP2 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| DUP3 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| DUP5 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| EQ | 3 | 1 | 3.0 | 0.0% | 0.0% | +| ISZERO | 3 | 1 | 3.0 | 0.0% | 0.0% | +| LT | 3 | 1 | 3.0 | 0.0% | 0.0% | + +### FiatTokenProxy_evm - changeAdmin + +- **Total Gas Used:** 28,326 + +| Opcode | Total Gas | Call Count | Avg Gas/Call | % of opcodes | % of tx Gas | +| ------------ | --------- | ---------- | ------------ | ------------ | ----------- | +| SSTORE | 2,900 | 1 | 2900.0 | 67.0% | 10.2% | +| LOG1 | 1,262 | 1 | 1262.0 | 29.2% | 4.5% | +| SLOAD | 100 | 1 | 100.0 | 2.3% | 0.4% | +| JUMPI | 10 | 1 | 10.0 | 0.2% | 0.0% | +| JUMP | 8 | 1 | 8.0 | 0.2% | 0.0% | +| CODECOPY | 6 | 1 | 6.0 | 0.1% | 0.0% | +| ADD | 3 | 1 | 3.0 | 0.1% | 0.0% | +| AND | 3 | 1 | 3.0 | 0.1% | 0.0% | +| CALLDATALOAD | 3 | 1 | 3.0 | 0.1% | 0.0% | +| DUP1 | 3 | 1 | 3.0 | 0.1% | 0.0% | +| DUP2 | 3 | 1 | 3.0 | 0.1% | 0.0% | +| DUP3 | 3 | 1 | 3.0 | 0.1% | 0.0% | +| EQ | 3 | 1 | 3.0 | 0.1% | 0.0% | +| ISZERO | 3 | 1 | 3.0 | 0.1% | 0.0% | +| LT | 3 | 1 | 3.0 | 0.1% | 0.0% | +| MLOAD | 3 | 1 | 3.0 | 0.1% | 0.0% | +| MSTORE | 3 | 1 | 3.0 | 0.1% | 0.0% | +| NOT | 3 | 1 | 3.0 | 0.1% | 0.0% | +| PUSH1 | 3 | 1 | 3.0 | 0.1% | 0.0% | +| PUSH2 | 3 | 1 | 3.0 | 0.1% | 0.0% | + +### FiatTokenProxy_evm - configureMinter + +- **Total Gas Used:** 79,482 + +| Opcode | Total Gas | Call Count | Avg Gas/Call | % of opcodes | % of tx Gas | +| -------------- | --------- | ---------- | ------------ | ------------ | ----------- | +| DELEGATECALL | 53,493 | 1 | 53493.0 | 67.6% | 67.3% | +| SSTORE | 22,100 | 1 | 22100.0 | 27.9% | 27.8% | +| SLOAD | 2,100 | 1 | 2100.0 | 2.7% | 2.6% | +| LOG2 | 1,381 | 1 | 1381.0 | 1.7% | 1.7% | +| KECCAK256 | 42 | 1 | 42.0 | 0.1% | 0.1% | +| CALLDATACOPY | 12 | 1 | 12.0 | 0.0% | 0.0% | +| JUMPI | 10 | 1 | 10.0 | 0.0% | 0.0% | +| JUMP | 8 | 1 | 8.0 | 0.0% | 0.0% | +| CODECOPY | 6 | 1 | 6.0 | 0.0% | 0.0% | +| RETURNDATACOPY | 6 | 1 | 6.0 | 0.0% | 0.0% | +| ADD | 3 | 1 | 3.0 | 0.0% | 0.0% | +| AND | 3 | 1 | 3.0 | 0.0% | 0.0% | +| CALLDATALOAD | 3 | 1 | 3.0 | 0.0% | 0.0% | +| DUP1 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| DUP2 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| DUP3 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| DUP4 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| DUP5 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| DUP6 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| EQ | 3 | 1 | 3.0 | 0.0% | 0.0% | + +### FiatTokenProxy_evm - deploy + +- **Total Gas Used:** 394,714 + +| Opcode | Total Gas | Call Count | Avg Gas/Call | % of opcodes | % of tx Gas | +| ----------- | --------- | ---------- | ------------ | ------------ | ----------- | +| SSTORE | 22,100 | 1 | 22100.0 | 88.3% | 5.6% | +| EXTCODESIZE | 2,600 | 1 | 2600.0 | 10.4% | 0.7% | +| CODECOPY | 259 | 1 | 259.0 | 1.0% | 0.1% | +| MSTORE | 12 | 1 | 12.0 | 0.0% | 0.0% | +| JUMPI | 10 | 1 | 10.0 | 0.0% | 0.0% | +| ADD | 3 | 1 | 3.0 | 0.0% | 0.0% | +| AND | 3 | 1 | 3.0 | 0.0% | 0.0% | +| DUP1 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| DUP2 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| DUP3 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| DUP4 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| DUP5 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| GT | 3 | 1 | 3.0 | 0.0% | 0.0% | +| ISZERO | 3 | 1 | 3.0 | 0.0% | 0.0% | +| LT | 3 | 1 | 3.0 | 0.0% | 0.0% | +| MLOAD | 3 | 1 | 3.0 | 0.0% | 0.0% | +| NOT | 3 | 1 | 3.0 | 0.0% | 0.0% | +| OR | 3 | 1 | 3.0 | 0.0% | 0.0% | +| PUSH1 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| PUSH2 | 3 | 1 | 3.0 | 0.0% | 0.0% | + +### FiatTokenProxy_evm - initialize + +- **Total Gas Used:** 212,439 + +| Opcode | Total Gas | Call Count | Avg Gas/Call | % of opcodes | % of tx Gas | +| ------------ | --------- | ---------- | ------------ | ------------ | ----------- | +| DELEGATECALL | 186,567 | 1 | 186567.0 | 99.9% | 87.8% | +| SLOAD | 100 | 1 | 100.0 | 0.1% | 0.0% | +| SSTORE | 100 | 1 | 100.0 | 0.1% | 0.0% | +| JUMPI | 10 | 1 | 10.0 | 0.0% | 0.0% | +| CALLDATACOPY | 9 | 1 | 9.0 | 0.0% | 0.0% | +| JUMP | 8 | 1 | 8.0 | 0.0% | 0.0% | +| CODECOPY | 6 | 1 | 6.0 | 0.0% | 0.0% | +| MSTORE | 6 | 1 | 6.0 | 0.0% | 0.0% | +| ADD | 3 | 1 | 3.0 | 0.0% | 0.0% | +| AND | 3 | 1 | 3.0 | 0.0% | 0.0% | +| CALLDATALOAD | 3 | 1 | 3.0 | 0.0% | 0.0% | +| DUP1 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| DUP16 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| DUP2 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| DUP3 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| DUP4 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| DUP5 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| EQ | 3 | 1 | 3.0 | 0.0% | 0.0% | +| GT | 3 | 1 | 3.0 | 0.0% | 0.0% | +| ISZERO | 3 | 1 | 3.0 | 0.0% | 0.0% | + +### FiatTokenProxy_evm - mint + +- **Total Gas Used:** 83,152 + +| Opcode | Total Gas | Call Count | Avg Gas/Call | % of opcodes | % of tx Gas | +| -------------- | --------- | ---------- | ------------ | ------------ | ----------- | +| DELEGATECALL | 62,519 | 1 | 62519.0 | 92.8% | 75.2% | +| SSTORE | 2,900 | 1 | 2900.0 | 4.3% | 3.5% | +| LOG3 | 1,756 | 1 | 1756.0 | 2.6% | 2.1% | +| SLOAD | 100 | 1 | 100.0 | 0.1% | 0.1% | +| KECCAK256 | 42 | 1 | 42.0 | 0.1% | 0.1% | +| CALLDATACOPY | 12 | 1 | 12.0 | 0.0% | 0.0% | +| JUMPI | 10 | 1 | 10.0 | 0.0% | 0.0% | +| JUMP | 8 | 1 | 8.0 | 0.0% | 0.0% | +| CODECOPY | 6 | 1 | 6.0 | 0.0% | 0.0% | +| RETURNDATACOPY | 6 | 1 | 6.0 | 0.0% | 0.0% | +| ADD | 3 | 1 | 3.0 | 0.0% | 0.0% | +| AND | 3 | 1 | 3.0 | 0.0% | 0.0% | +| CALLDATALOAD | 3 | 1 | 3.0 | 0.0% | 0.0% | +| DUP1 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| DUP2 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| DUP3 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| DUP4 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| DUP6 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| DUP8 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| EQ | 3 | 1 | 3.0 | 0.0% | 0.0% | + +### FiatTokenProxy_evm - transfer + +- **Total Gas Used:** 38,065 + +| Opcode | Total Gas | Call Count | Avg Gas/Call | % of opcodes | % of tx Gas | +| -------------- | --------- | ---------- | ------------ | ------------ | ----------- | +| DELEGATECALL | 15,421 | 1 | 15421.0 | 88.2% | 40.5% | +| LOG3 | 1,756 | 1 | 1756.0 | 10.0% | 4.6% | +| SLOAD | 100 | 1 | 100.0 | 0.6% | 0.3% | +| SSTORE | 100 | 1 | 100.0 | 0.6% | 0.3% | +| KECCAK256 | 42 | 1 | 42.0 | 0.2% | 0.1% | +| CALLDATACOPY | 12 | 1 | 12.0 | 0.1% | 0.0% | +| JUMPI | 10 | 1 | 10.0 | 0.1% | 0.0% | +| JUMP | 8 | 1 | 8.0 | 0.0% | 0.0% | +| CODECOPY | 6 | 1 | 6.0 | 0.0% | 0.0% | +| RETURNDATACOPY | 6 | 1 | 6.0 | 0.0% | 0.0% | +| ADD | 3 | 1 | 3.0 | 0.0% | 0.0% | +| AND | 3 | 1 | 3.0 | 0.0% | 0.0% | +| CALLDATALOAD | 3 | 1 | 3.0 | 0.0% | 0.0% | +| DUP1 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| DUP10 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| DUP2 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| DUP3 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| DUP4 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| DUP5 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| DUP7 | 3 | 1 | 3.0 | 0.0% | 0.0% | + +### FiatTokenProxy_evm - transferFrom + +- **Total Gas Used:** 43,789 + +| Opcode | Total Gas | Call Count | Avg Gas/Call | % of opcodes | % of tx Gas | +| -------------- | --------- | ---------- | ------------ | ------------ | ----------- | +| DELEGATECALL | 20,381 | 1 | 20381.0 | 80.7% | 46.5% | +| SSTORE | 2,900 | 1 | 2900.0 | 11.5% | 6.6% | +| LOG3 | 1,756 | 1 | 1756.0 | 7.0% | 4.0% | +| SLOAD | 100 | 1 | 100.0 | 0.4% | 0.2% | +| KECCAK256 | 42 | 1 | 42.0 | 0.2% | 0.1% | +| CALLDATACOPY | 18 | 1 | 18.0 | 0.1% | 0.0% | +| JUMPI | 10 | 1 | 10.0 | 0.0% | 0.0% | +| JUMP | 8 | 1 | 8.0 | 0.0% | 0.0% | +| CODECOPY | 6 | 1 | 6.0 | 0.0% | 0.0% | +| RETURNDATACOPY | 6 | 1 | 6.0 | 0.0% | 0.0% | +| ADD | 3 | 1 | 3.0 | 0.0% | 0.0% | +| AND | 3 | 1 | 3.0 | 0.0% | 0.0% | +| CALLDATALOAD | 3 | 1 | 3.0 | 0.0% | 0.0% | +| DUP1 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| DUP10 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| DUP2 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| DUP3 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| DUP4 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| DUP5 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| DUP7 | 3 | 1 | 3.0 | 0.0% | 0.0% | + +### FiatTokenV2_2_evm - deploy + +- **Total Gas Used:** 3,412,411 + +| Opcode | Total Gas | Call Count | Avg Gas/Call | % of opcodes | % of tx Gas | +| --------- | --------- | ---------- | ------------ | ------------ | ----------- | +| CODECOPY | 3,359 | 1 | 3359.0 | 43.5% | 0.1% | +| SSTORE | 2,200 | 1 | 2200.0 | 28.5% | 0.1% | +| SLOAD | 2,100 | 1 | 2100.0 | 27.2% | 0.1% | +| MSTORE | 12 | 1 | 12.0 | 0.2% | 0.0% | +| JUMPI | 10 | 1 | 10.0 | 0.1% | 0.0% | +| AND | 3 | 1 | 3.0 | 0.0% | 0.0% | +| DUP1 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| DUP2 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| DUP3 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| NOT | 3 | 1 | 3.0 | 0.0% | 0.0% | +| OR | 3 | 1 | 3.0 | 0.0% | 0.0% | +| PUSH1 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| PUSH2 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| SHL | 3 | 1 | 3.0 | 0.0% | 0.0% | +| SUB | 3 | 1 | 3.0 | 0.0% | 0.0% | +| SWAP1 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| CALLER | 2 | 1 | 2.0 | 0.0% | 0.0% | +| CALLVALUE | 2 | 1 | 2.0 | 0.0% | 0.0% | +| PUSH0 | 2 | 1 | 2.0 | 0.0% | 0.0% | +| RETURN | 0 | 1 | 0.0 | 0.0% | 0.0% | + ### Fibonacci_evm - deploy - **Total Gas Used:** 112,683 @@ -22,7 +319,6 @@ Generated on: 2026-01-13 | CALLVALUE | 2 | 1 | 2.0 | 2.0% | 0.0% | | RETURN | 0 | 1 | 0.0 | 0.0% | 0.0% | - ### Fibonacci_evm - fib_10 - **Total Gas Used:** 47,664 @@ -50,7 +346,6 @@ Generated on: 2026-01-13 | SUB | 3 | 1 | 3.0 | 3.7% | 0.0% | | SWAP1 | 3 | 1 | 3.0 | 3.7% | 0.0% | - ### SimpleToken_evm - deploy - **Total Gas Used:** 152,967 @@ -69,7 +364,6 @@ Generated on: 2026-01-13 | CALLVALUE | 2 | 1 | 2.0 | 1.4% | 0.0% | | RETURN | 0 | 1 | 0.0 | 0.0% | 0.0% | - ### SimpleToken_evm - mint - **Total Gas Used:** 68,105 @@ -97,7 +391,6 @@ Generated on: 2026-01-13 | LT | 3 | 1 | 3.0 | 0.0% | 0.0% | | MLOAD | 3 | 1 | 3.0 | 0.0% | 0.0% | - ### SimpleToken_evm - transfer - **Total Gas Used:** 46,257 @@ -125,68 +418,877 @@ Generated on: 2026-01-13 | PUSH1 | 3 | 1 | 3.0 | 0.0% | 0.0% | | PUSH2 | 3 | 1 | 3.0 | 0.0% | 0.0% | +### TetherToken_evm - approve + +- **Total Gas Used:** 48,276 + +| Opcode | Total Gas | Call Count | Avg Gas/Call | % of opcodes | % of tx Gas | +| ------------ | --------- | ---------- | ------------ | ------------ | ----------- | +| SSTORE | 20,000 | 1 | 20000.0 | 83.5% | 41.4% | +| SLOAD | 2,100 | 1 | 2100.0 | 8.8% | 4.3% | +| LOG3 | 1,756 | 1 | 1756.0 | 7.3% | 3.6% | +| KECCAK256 | 42 | 1 | 42.0 | 0.2% | 0.1% | +| JUMPI | 10 | 1 | 10.0 | 0.0% | 0.0% | +| MSTORE | 9 | 1 | 9.0 | 0.0% | 0.0% | +| JUMP | 8 | 1 | 8.0 | 0.0% | 0.0% | +| ADD | 3 | 1 | 3.0 | 0.0% | 0.0% | +| AND | 3 | 1 | 3.0 | 0.0% | 0.0% | +| CALLDATALOAD | 3 | 1 | 3.0 | 0.0% | 0.0% | +| DUP1 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| DUP2 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| DUP3 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| EQ | 3 | 1 | 3.0 | 0.0% | 0.0% | +| ISZERO | 3 | 1 | 3.0 | 0.0% | 0.0% | +| LT | 3 | 1 | 3.0 | 0.0% | 0.0% | +| MLOAD | 3 | 1 | 3.0 | 0.0% | 0.0% | +| NOT | 3 | 1 | 3.0 | 0.0% | 0.0% | +| PUSH1 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| PUSH2 | 3 | 1 | 3.0 | 0.0% | 0.0% | + +### TetherToken_evm - deploy + +- **Total Gas Used:** 1,208,745 + +| Opcode | Total Gas | Call Count | Avg Gas/Call | % of opcodes | % of tx Gas | +| --------- | --------- | ---------- | ------------ | ------------ | ----------- | +| SLOAD | 2,100 | 1 | 2100.0 | 65.1% | 0.2% | +| CODECOPY | 920 | 1 | 920.0 | 28.5% | 0.1% | +| SSTORE | 100 | 1 | 100.0 | 3.1% | 0.0% | +| KECCAK256 | 42 | 1 | 42.0 | 1.3% | 0.0% | +| JUMPI | 10 | 1 | 10.0 | 0.3% | 0.0% | +| MCOPY | 9 | 1 | 9.0 | 0.3% | 0.0% | +| JUMP | 8 | 1 | 8.0 | 0.2% | 0.0% | +| ADD | 3 | 1 | 3.0 | 0.1% | 0.0% | +| AND | 3 | 1 | 3.0 | 0.1% | 0.0% | +| DUP1 | 3 | 1 | 3.0 | 0.1% | 0.0% | +| DUP2 | 3 | 1 | 3.0 | 0.1% | 0.0% | +| DUP3 | 3 | 1 | 3.0 | 0.1% | 0.0% | +| DUP4 | 3 | 1 | 3.0 | 0.1% | 0.0% | +| DUP5 | 3 | 1 | 3.0 | 0.1% | 0.0% | +| DUP6 | 3 | 1 | 3.0 | 0.1% | 0.0% | +| DUP7 | 3 | 1 | 3.0 | 0.1% | 0.0% | +| EQ | 3 | 1 | 3.0 | 0.1% | 0.0% | +| GT | 3 | 1 | 3.0 | 0.1% | 0.0% | +| ISZERO | 3 | 1 | 3.0 | 0.1% | 0.0% | +| LT | 3 | 1 | 3.0 | 0.1% | 0.0% | + +### TetherToken_evm - transfer + +- **Total Gas Used:** 37,754 + +| Opcode | Total Gas | Call Count | Avg Gas/Call | % of opcodes | % of tx Gas | +| ------------ | --------- | ---------- | ------------ | ------------ | ----------- | +| LOG3 | 1,756 | 1 | 1756.0 | 84.9% | 4.7% | +| SLOAD | 100 | 1 | 100.0 | 4.8% | 0.3% | +| SSTORE | 100 | 1 | 100.0 | 4.8% | 0.3% | +| KECCAK256 | 42 | 1 | 42.0 | 2.0% | 0.1% | +| JUMPI | 10 | 1 | 10.0 | 0.5% | 0.0% | +| MSTORE | 9 | 1 | 9.0 | 0.4% | 0.0% | +| JUMP | 8 | 1 | 8.0 | 0.4% | 0.0% | +| DIV | 5 | 1 | 5.0 | 0.2% | 0.0% | +| MUL | 5 | 1 | 5.0 | 0.2% | 0.0% | +| ADD | 3 | 1 | 3.0 | 0.1% | 0.0% | +| AND | 3 | 1 | 3.0 | 0.1% | 0.0% | +| CALLDATALOAD | 3 | 1 | 3.0 | 0.1% | 0.0% | +| DUP1 | 3 | 1 | 3.0 | 0.1% | 0.0% | +| DUP2 | 3 | 1 | 3.0 | 0.1% | 0.0% | +| DUP3 | 3 | 1 | 3.0 | 0.1% | 0.0% | +| DUP4 | 3 | 1 | 3.0 | 0.1% | 0.0% | +| DUP5 | 3 | 1 | 3.0 | 0.1% | 0.0% | +| EQ | 3 | 1 | 3.0 | 0.1% | 0.0% | +| GT | 3 | 1 | 3.0 | 0.1% | 0.0% | +| ISZERO | 3 | 1 | 3.0 | 0.1% | 0.0% | + +### TetherToken_evm - transferFrom + +- **Total Gas Used:** 43,290 + +| Opcode | Total Gas | Call Count | Avg Gas/Call | % of opcodes | % of tx Gas | +| ------------ | --------- | ---------- | ------------ | ------------ | ----------- | +| LOG3 | 1,756 | 1 | 1756.0 | 84.9% | 4.1% | +| SLOAD | 100 | 1 | 100.0 | 4.8% | 0.2% | +| SSTORE | 100 | 1 | 100.0 | 4.8% | 0.2% | +| KECCAK256 | 42 | 1 | 42.0 | 2.0% | 0.1% | +| JUMPI | 10 | 1 | 10.0 | 0.5% | 0.0% | +| MSTORE | 9 | 1 | 9.0 | 0.4% | 0.0% | +| JUMP | 8 | 1 | 8.0 | 0.4% | 0.0% | +| DIV | 5 | 1 | 5.0 | 0.2% | 0.0% | +| MUL | 5 | 1 | 5.0 | 0.2% | 0.0% | +| ADD | 3 | 1 | 3.0 | 0.1% | 0.0% | +| AND | 3 | 1 | 3.0 | 0.1% | 0.0% | +| CALLDATALOAD | 3 | 1 | 3.0 | 0.1% | 0.0% | +| DUP1 | 3 | 1 | 3.0 | 0.1% | 0.0% | +| DUP2 | 3 | 1 | 3.0 | 0.1% | 0.0% | +| DUP3 | 3 | 1 | 3.0 | 0.1% | 0.0% | +| DUP4 | 3 | 1 | 3.0 | 0.1% | 0.0% | +| DUP5 | 3 | 1 | 3.0 | 0.1% | 0.0% | +| DUP6 | 3 | 1 | 3.0 | 0.1% | 0.0% | +| DUP7 | 3 | 1 | 3.0 | 0.1% | 0.0% | +| DUP8 | 3 | 1 | 3.0 | 0.1% | 0.0% | + +### WETH9_evm - deploy + +- **Total Gas Used:** 487,741 + +| Opcode | Total Gas | Call Count | Avg Gas/Call | % of opcodes | % of tx Gas | +| -------- | --------- | ---------- | ------------ | ------------ | ----------- | +| SSTORE | 20,000 | 1 | 20000.0 | 88.9% | 4.1% | +| SLOAD | 2,100 | 1 | 2100.0 | 9.3% | 0.4% | +| CODECOPY | 330 | 1 | 330.0 | 1.5% | 0.1% | +| MSTORE | 12 | 1 | 12.0 | 0.1% | 0.0% | +| JUMPI | 10 | 1 | 10.0 | 0.0% | 0.0% | +| JUMP | 8 | 1 | 8.0 | 0.0% | 0.0% | +| ADD | 3 | 1 | 3.0 | 0.0% | 0.0% | +| AND | 3 | 1 | 3.0 | 0.0% | 0.0% | +| DUP1 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| DUP2 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| DUP3 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| DUP4 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| EQ | 3 | 1 | 3.0 | 0.0% | 0.0% | +| GT | 3 | 1 | 3.0 | 0.0% | 0.0% | +| ISZERO | 3 | 1 | 3.0 | 0.0% | 0.0% | +| LT | 3 | 1 | 3.0 | 0.0% | 0.0% | +| MLOAD | 3 | 1 | 3.0 | 0.0% | 0.0% | +| NOT | 3 | 1 | 3.0 | 0.0% | 0.0% | +| OR | 3 | 1 | 3.0 | 0.0% | 0.0% | +| PUSH1 | 3 | 1 | 3.0 | 0.0% | 0.0% | + +### WETH9_evm - deposit + +- **Total Gas Used:** 44,978 + +| Opcode | Total Gas | Call Count | Avg Gas/Call | % of opcodes | % of tx Gas | +| ------------ | --------- | ---------- | ------------ | ------------ | ----------- | +| SSTORE | 20,000 | 1 | 20000.0 | 84.8% | 44.5% | +| SLOAD | 2,100 | 1 | 2100.0 | 8.9% | 4.7% | +| LOG2 | 1,381 | 1 | 1381.0 | 5.9% | 3.1% | +| KECCAK256 | 42 | 1 | 42.0 | 0.2% | 0.1% | +| JUMPI | 10 | 1 | 10.0 | 0.0% | 0.0% | +| MSTORE | 9 | 1 | 9.0 | 0.0% | 0.0% | +| JUMP | 8 | 1 | 8.0 | 0.0% | 0.0% | +| ADD | 3 | 1 | 3.0 | 0.0% | 0.0% | +| CALLDATALOAD | 3 | 1 | 3.0 | 0.0% | 0.0% | +| DUP1 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| DUP2 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| EQ | 3 | 1 | 3.0 | 0.0% | 0.0% | +| ISZERO | 3 | 1 | 3.0 | 0.0% | 0.0% | +| LT | 3 | 1 | 3.0 | 0.0% | 0.0% | +| MLOAD | 3 | 1 | 3.0 | 0.0% | 0.0% | +| NOT | 3 | 1 | 3.0 | 0.0% | 0.0% | +| PUSH1 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| PUSH2 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| PUSH32 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| PUSH4 | 3 | 1 | 3.0 | 0.0% | 0.0% | + +### WETH9_evm - transfer + +- **Total Gas Used:** 26,613 + +| Opcode | Total Gas | Call Count | Avg Gas/Call | % of opcodes | % of tx Gas | +| ------------ | --------- | ---------- | ------------ | ------------ | ----------- | +| LOG3 | 1,756 | 1 | 1756.0 | 85.3% | 6.6% | +| SLOAD | 100 | 1 | 100.0 | 4.9% | 0.4% | +| SSTORE | 100 | 1 | 100.0 | 4.9% | 0.4% | +| KECCAK256 | 42 | 1 | 42.0 | 2.0% | 0.2% | +| JUMPI | 10 | 1 | 10.0 | 0.5% | 0.0% | +| JUMP | 8 | 1 | 8.0 | 0.4% | 0.0% | +| ADD | 3 | 1 | 3.0 | 0.1% | 0.0% | +| AND | 3 | 1 | 3.0 | 0.1% | 0.0% | +| CALLDATALOAD | 3 | 1 | 3.0 | 0.1% | 0.0% | +| DUP1 | 3 | 1 | 3.0 | 0.1% | 0.0% | +| DUP2 | 3 | 1 | 3.0 | 0.1% | 0.0% | +| DUP3 | 3 | 1 | 3.0 | 0.1% | 0.0% | +| DUP4 | 3 | 1 | 3.0 | 0.1% | 0.0% | +| DUP5 | 3 | 1 | 3.0 | 0.1% | 0.0% | +| DUP6 | 3 | 1 | 3.0 | 0.1% | 0.0% | +| EQ | 3 | 1 | 3.0 | 0.1% | 0.0% | +| ISZERO | 3 | 1 | 3.0 | 0.1% | 0.0% | +| LT | 3 | 1 | 3.0 | 0.1% | 0.0% | +| MLOAD | 3 | 1 | 3.0 | 0.1% | 0.0% | +| MSTORE | 3 | 1 | 3.0 | 0.1% | 0.0% | + +### WETH9_evm - withdraw + +- **Total Gas Used:** 34,953 + +| Opcode | Total Gas | Call Count | Avg Gas/Call | % of opcodes | % of tx Gas | +| ------------ | --------- | ---------- | ------------ | ------------ | ----------- | +| CALL | 9,100 | 1 | 9100.0 | 67.0% | 26.0% | +| SSTORE | 2,900 | 1 | 2900.0 | 21.4% | 8.3% | +| LOG2 | 1,381 | 1 | 1381.0 | 10.2% | 4.0% | +| SLOAD | 100 | 1 | 100.0 | 0.7% | 0.3% | +| KECCAK256 | 42 | 1 | 42.0 | 0.3% | 0.1% | +| JUMPI | 10 | 1 | 10.0 | 0.1% | 0.0% | +| MSTORE | 9 | 1 | 9.0 | 0.1% | 0.0% | +| ADD | 3 | 1 | 3.0 | 0.0% | 0.0% | +| CALLDATALOAD | 3 | 1 | 3.0 | 0.0% | 0.0% | +| DUP1 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| DUP2 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| EQ | 3 | 1 | 3.0 | 0.0% | 0.0% | +| ISZERO | 3 | 1 | 3.0 | 0.0% | 0.0% | +| LT | 3 | 1 | 3.0 | 0.0% | 0.0% | +| MLOAD | 3 | 1 | 3.0 | 0.0% | 0.0% | +| NOT | 3 | 1 | 3.0 | 0.0% | 0.0% | +| PUSH1 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| PUSH2 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| PUSH32 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| PUSH4 | 3 | 1 | 3.0 | 0.0% | 0.0% | + +### XENCrypto_evm - deploy + +- **Total Gas Used:** 2,221,205 + +| Opcode | Total Gas | Call Count | Avg Gas/Call | % of opcodes | % of tx Gas | +| -------- | --------- | ---------- | ------------ | ------------ | ----------- | +| SSTORE | 22,100 | 1 | 22100.0 | 84.1% | 1.0% | +| SLOAD | 2,100 | 1 | 2100.0 | 8.0% | 0.1% | +| CODECOPY | 2,011 | 1 | 2011.0 | 7.7% | 0.1% | +| JUMPI | 10 | 1 | 10.0 | 0.0% | 0.0% | +| JUMP | 8 | 1 | 8.0 | 0.0% | 0.0% | +| ADD | 3 | 1 | 3.0 | 0.0% | 0.0% | +| AND | 3 | 1 | 3.0 | 0.0% | 0.0% | +| DUP1 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| DUP2 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| DUP3 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| DUP4 | 3 | 1 | 3.0 | 0.0% | 0.0% | +| EQ | 3 | 1 | 3.0 | 0.0% | 0.0% | +| GT | 3 | 1 | 3.0 | 0.0% | 0.0% | +| ISZERO | 3 | 1 | 3.0 | 0.0% | 0.0% | +| LT | 3 | 1 | 3.0 | 0.0% | 0.0% | +| MLOAD | 3 | 1 | 3.0 | 0.0% | 0.0% | +| MSTORE | 3 | 1 | 3.0 | 0.0% | 0.0% | +| NOT | 3 | 1 | 3.0 | 0.0% | 0.0% | +| OR | 3 | 1 | 3.0 | 0.0% | 0.0% | +| PUSH1 | 3 | 1 | 3.0 | 0.0% | 0.0% | ## Chain: eth-rpc +### CoinTool_App_evm - deploy + +- **Total Gas Used:** 22,233,426 +- **Base Call Weight:** ref_time=1,568,628,607, proof_size=6,934 +- **Total Weight:** ref_time=1,812,498,168, proof_size=27,480 +- **Weight Consumed:** ref_time=243,869,561 (13.5% of total), proof_size=20,546 + +| Opcode | Total Gas | Call Count | Avg Gas/Call | ref time | proof size | % of ref time | % of proof size | +| -------- | --------- | ---------- | ------------ | ----------- | ---------- | ------------- | --------------- | +| SSTORE | 56,212 | 1 | 56212.0 | 170,600,294 | 10,257 | 70.0% | 49.9% | +| SLOAD | 1,384 | 1 | 1384.0 | 69,164,719 | 10,289 | 28.4% | 50.1% | +| CODECOPY | 23 | 1 | 23.0 | 1,129,668 | 0 | 0.5% | 0.0% | +| ORIGIN | 8 | 1 | 8.0 | 395,000 | 0 | 0.2% | 0.0% | +| ADDRESS | 7 | 1 | 7.0 | 349,000 | 0 | 0.1% | 0.0% | +| ADD | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| AND | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP1 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP2 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP3 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| MLOAD | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| MSTORE | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| NOT | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| OR | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| PUSH1 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| PUSH2 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| SHL | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| SUB | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| SWAP1 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| PUSH0 | 0 | 1 | 0.0 | 30,560 | 0 | 0.0% | 0.0% | + +### CoinTool_App_pvm - deploy + +- **Total Gas Used:** 85,031,858 +- **Base Call Weight:** ref_time=1,980,057,129, proof_size=6,934 +- **Total Weight:** ref_time=2,352,084,109, proof_size=27,448 +- **Weight Consumed:** ref_time=372,026,980 (15.8% of total), proof_size=20,514 + +| Opcode | Total Gas | Call Count | Avg Gas/Call | ref time | proof size | % of ref time | % of proof size | +| -------------------- | --------- | ---------- | ------------ | ----------- | ---------- | ------------- | --------------- | +| set_storage_or_clear | 56,212 | 1 | 56212.0 | 170,600,294 | 10,257 | 45.9% | 50.0% | +| set_immutable_data | 2,049 | 1 | 2049.0 | 102,480,992 | 0 | 27.5% | 0.0% | +| get_storage_or_zero | 1,383 | 1 | 1383.0 | 69,114,511 | 10,257 | 18.6% | 50.0% | +| seal_return | 10 | 1 | 10.0 | 529,465 | 0 | 0.1% | 0.0% | +| call_data_copy | 10 | 1 | 10.0 | 496,576 | 0 | 0.1% | 0.0% | +| origin | 8 | 1 | 8.0 | 395,000 | 0 | 0.1% | 0.0% | +| address | 7 | 1 | 7.0 | 349,000 | 0 | 0.1% | 0.0% | +| call_data_size | 7 | 1 | 7.0 | 330,000 | 0 | 0.1% | 0.0% | + +### CoinTool_App_evm - t + +- **Total Gas Used:** 361,202,149 +- **Base Call Weight:** ref_time=917,488,943, proof_size=9,500 +- **Total Weight:** ref_time=191,611,648,659, proof_size=9,138,203 +- **Weight Consumed:** ref_time=190,694,159,716 (99.5% of total), proof_size=9,128,703 + +| Opcode | Total Gas | Call Count | Avg Gas/Call | ref time | proof size | % of ref time | % of proof size | +| ------------ | --------- | ---------- | ------------ | --------------- | ---------- | ------------- | --------------- | +| CREATE2 | 0 | 1 | 0.0 | 186,637,653,556 | 8,926,199 | 97.9% | 97.8% | +| RETURN | 10 | 1 | 10.0 | 2,379,516,089 | 164,636 | 1.2% | 1.8% | +| DELEGATECALL | 0 | 1 | 0.0 | 247,450,874 | 30,867 | 0.1% | 0.3% | +| SSTORE | 56,212 | 1 | 56212.0 | 170,600,294 | 10,257 | 0.1% | 0.1% | +| SLOAD | 1,383 | 1 | 1383.0 | 69,164,719 | 10,289 | 0.0% | 0.1% | +| LOG2 | 443 | 1 | 443.0 | 22,179,532 | 0 | 0.0% | 0.0% | +| KECCAK256 | 302 | 1 | 302.0 | 15,109,884 | 0 | 0.0% | 0.0% | +| CALL | 0 | 1 | 0.0 | 14,914,788 | 0 | 0.0% | 0.0% | +| GAS | 39 | 1 | 39.0 | 1,929,000 | 0 | 0.0% | 0.0% | +| CODECOPY | 11 | 1 | 11.0 | 548,276 | 0 | 0.0% | 0.0% | +| CALLDATACOPY | 9 | 1 | 9.0 | 489,203 | 0 | 0.0% | 0.0% | +| CALLER | 8 | 1 | 8.0 | 403,000 | 0 | 0.0% | 0.0% | +| ORIGIN | 8 | 1 | 8.0 | 395,000 | 0 | 0.0% | 0.0% | +| ADDRESS | 7 | 1 | 7.0 | 349,000 | 0 | 0.0% | 0.0% | +| TIMESTAMP | 7 | 1 | 7.0 | 347,000 | 0 | 0.0% | 0.0% | +| CALLVALUE | 6 | 1 | 6.0 | 319,000 | 0 | 0.0% | 0.0% | +| JUMPI | 3 | 1 | 3.0 | 152,800 | 0 | 0.0% | 0.0% | +| JUMP | 2 | 1 | 2.0 | 122,240 | 0 | 0.0% | 0.0% | +| DIV | 2 | 1 | 2.0 | 76,400 | 0 | 0.0% | 0.0% | +| MUL | 1 | 1 | 1.0 | 76,400 | 0 | 0.0% | 0.0% | + +### CoinTool_App_pvm - t + +- **Total Gas Used:** 542,995,370 +- **Base Call Weight:** ref_time=917,488,943, proof_size=9,500 +- **Total Weight:** ref_time=119,832,667,856, proof_size=10,828,951 +- **Weight Consumed:** ref_time=118,915,178,913 (99.2% of total), proof_size=10,819,451 + +| Opcode | Total Gas | Call Count | Avg Gas/Call | ref time | proof size | % of ref time | % of proof size | +| -------------------- | --------- | ---------- | ------------ | --------------- | ---------- | ------------- | --------------- | +| instantiate | 0 | 1 | 0.0 | 116,289,884,100 | 10,554,518 | 97.8% | 97.6% | +| set_storage_or_clear | 56,212 | 1 | 56212.0 | 170,600,294 | 10,257 | 0.1% | 0.1% | +| call_evm | 0 | 1 | 0.0 | 113,737,019 | 3,801 | 0.1% | 0.0% | +| delegate_call_evm | 2,241 | 1 | 2241.0 | 112,040,065 | 4,899 | 0.1% | 0.0% | +| set_immutable_data | 2,050 | 1 | 2050.0 | 102,480,992 | 0 | 0.1% | 0.0% | +| get_storage_or_zero | 1,382 | 1 | 1382.0 | 69,114,511 | 10,257 | 0.1% | 0.1% | +| get_immutable_data | 635 | 1 | 635.0 | 31,769,885 | 3,801 | 0.0% | 0.0% | +| hash_keccak_256 | 303 | 1 | 303.0 | 15,109,884 | 0 | 0.0% | 0.0% | +| ref_time_left | 39 | 1 | 39.0 | 1,929,000 | 0 | 0.0% | 0.0% | +| seal_return | 11 | 1 | 11.0 | 529,465 | 0 | 0.0% | 0.0% | +| call_data_copy | 10 | 1 | 10.0 | 496,689 | 0 | 0.0% | 0.0% | +| caller | 8 | 1 | 8.0 | 403,000 | 0 | 0.0% | 0.0% | +| origin | 8 | 1 | 8.0 | 395,000 | 0 | 0.0% | 0.0% | +| address | 7 | 1 | 7.0 | 349,000 | 0 | 0.0% | 0.0% | +| now | 7 | 1 | 7.0 | 347,000 | 0 | 0.0% | 0.0% | +| call_data_load | 7 | 1 | 7.0 | 343,000 | 0 | 0.0% | 0.0% | +| call_data_size | 7 | 1 | 7.0 | 330,000 | 0 | 0.0% | 0.0% | +| value_transferred | 7 | 1 | 7.0 | 319,000 | 0 | 0.0% | 0.0% | +| return_data_size | 6 | 1 | 6.0 | 319,000 | 0 | 0.0% | 0.0% | + +### FiatTokenProxy_evm - approve + +- **Total Gas Used:** 468,043 +- **Base Call Weight:** ref_time=913,507,322, proof_size=8,531 +- **Total Weight:** ref_time=1,466,337,726, proof_size=54,554 +- **Weight Consumed:** ref_time=552,830,404 (37.7% of total), proof_size=46,023 + +| Opcode | Total Gas | Call Count | Avg Gas/Call | ref time | proof size | % of ref time | % of proof size | +| -------------- | --------- | ---------- | ------------ | ----------- | ---------- | ------------- | --------------- | +| SSTORE | 56,212 | 1 | 56212.0 | 170,600,294 | 10,257 | 30.9% | 22.3% | +| DELEGATECALL | 0 | 1 | 0.0 | 145,525,578 | 20,578 | 26.3% | 44.7% | +| SLOAD | 1,384 | 1 | 1384.0 | 69,164,719 | 10,289 | 12.5% | 22.4% | +| KECCAK256 | 304 | 1 | 304.0 | 15,219,717 | 0 | 2.8% | 0.0% | +| LOG3 | 275 | 1 | 275.0 | 13,751,564 | 0 | 2.5% | 0.0% | +| GAS | 39 | 1 | 39.0 | 1,929,000 | 0 | 0.3% | 0.0% | +| CALLDATACOPY | 10 | 1 | 10.0 | 502,804 | 0 | 0.1% | 0.0% | +| CODECOPY | 10 | 1 | 10.0 | 495,496 | 0 | 0.1% | 0.0% | +| RETURNDATACOPY | 10 | 1 | 10.0 | 495,496 | 0 | 0.1% | 0.0% | +| CALLER | 8 | 1 | 8.0 | 403,000 | 0 | 0.1% | 0.0% | +| CALLVALUE | 6 | 1 | 6.0 | 319,000 | 0 | 0.1% | 0.0% | +| JUMPI | 3 | 1 | 3.0 | 152,800 | 0 | 0.0% | 0.0% | +| JUMP | 3 | 1 | 3.0 | 122,240 | 0 | 0.0% | 0.0% | +| ADD | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| AND | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| CALLDATALOAD | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP2 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP3 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP5 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| EQ | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | + +### FiatTokenProxy_pvm - approve + +- **Total Gas Used:** 473,460 +- **Base Call Weight:** ref_time=913,507,322, proof_size=8,531 +- **Total Weight:** ref_time=1,737,192,853, proof_size=167,787 +- **Weight Consumed:** ref_time=823,685,531 (47.4% of total), proof_size=159,256 + +| Opcode | Total Gas | Call Count | Avg Gas/Call | ref time | proof size | % of ref time | % of proof size | +| -------------------- | --------- | ---------- | ------------ | ----------- | ---------- | ------------- | --------------- | +| set_storage_or_clear | 56,212 | 1 | 56212.0 | 170,600,294 | 10,257 | 20.7% | 6.4% | +| delegate_call_evm | 0 | 1 | 0.0 | 162,677,640 | 20,578 | 19.7% | 12.9% | +| get_storage_or_zero | 1,383 | 1 | 1383.0 | 69,164,719 | 10,289 | 8.4% | 6.5% | +| hash_keccak_256 | 304 | 1 | 304.0 | 15,219,717 | 0 | 1.8% | 0.0% | +| deposit_event | 275 | 1 | 275.0 | 13,751,564 | 0 | 1.7% | 0.0% | +| ref_time_left | 38 | 1 | 38.0 | 1,929,000 | 0 | 0.2% | 0.0% | +| seal_return | 11 | 1 | 11.0 | 535,865 | 0 | 0.1% | 0.0% | +| call_data_copy | 10 | 1 | 10.0 | 504,260 | 0 | 0.1% | 0.0% | +| return_data_copy | 10 | 1 | 10.0 | 495,496 | 0 | 0.1% | 0.0% | +| caller | 8 | 1 | 8.0 | 403,000 | 0 | 0.0% | 0.0% | +| call_data_load | 7 | 1 | 7.0 | 343,000 | 0 | 0.0% | 0.0% | +| call_data_size | 7 | 1 | 7.0 | 330,000 | 0 | 0.0% | 0.0% | +| return_data_size | 7 | 1 | 7.0 | 319,000 | 0 | 0.0% | 0.0% | +| value_transferred | 7 | 1 | 7.0 | 319,000 | 0 | 0.0% | 0.0% | + +### FiatTokenProxy_evm - changeAdmin + +- **Total Gas Used:** 344,997 +- **Base Call Weight:** ref_time=913,100,531, proof_size=8,432 +- **Total Weight:** ref_time=1,254,022,241, proof_size=39,299 +- **Weight Consumed:** ref_time=340,921,710 (27.2% of total), proof_size=30,867 + +| Opcode | Total Gas | Call Count | Avg Gas/Call | ref time | proof size | % of ref time | % of proof size | +| ------------ | --------- | ---------- | ------------ | ----------- | ---------- | ------------- | --------------- | +| SSTORE | 3,412 | 1 | 3412.0 | 170,624,806 | 10,289 | 50.0% | 33.3% | +| SLOAD | 1,383 | 1 | 1383.0 | 69,164,719 | 10,289 | 20.3% | 33.3% | +| LOG1 | 444 | 1 | 444.0 | 22,179,532 | 0 | 6.5% | 0.0% | +| CODECOPY | 10 | 1 | 10.0 | 495,496 | 0 | 0.1% | 0.0% | +| CALLER | 8 | 1 | 8.0 | 403,000 | 0 | 0.1% | 0.0% | +| CALLVALUE | 6 | 1 | 6.0 | 319,000 | 0 | 0.1% | 0.0% | +| JUMPI | 3 | 1 | 3.0 | 152,800 | 0 | 0.0% | 0.0% | +| JUMP | 3 | 1 | 3.0 | 122,240 | 0 | 0.0% | 0.0% | +| ADD | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| AND | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| CALLDATALOAD | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP1 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP2 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP3 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| EQ | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| ISZERO | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| LT | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| MSTORE | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| NOT | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| PUSH1 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | + +### FiatTokenProxy_pvm - changeAdmin + +- **Total Gas Used:** 346,075 +- **Base Call Weight:** ref_time=913,100,531, proof_size=8,432 +- **Total Weight:** ref_time=1,307,939,666, proof_size=48,987 +- **Weight Consumed:** ref_time=394,839,135 (30.2% of total), proof_size=40,555 + +| Opcode | Total Gas | Call Count | Avg Gas/Call | ref time | proof size | % of ref time | % of proof size | +| -------------------- | --------- | ---------- | ------------ | ----------- | ---------- | ------------- | --------------- | +| set_storage_or_clear | 3,412 | 1 | 3412.0 | 170,624,806 | 10,289 | 43.2% | 25.4% | +| get_storage_or_zero | 1,384 | 1 | 1384.0 | 69,164,719 | 10,289 | 17.5% | 25.4% | +| deposit_event | 444 | 1 | 444.0 | 22,179,532 | 0 | 5.6% | 0.0% | +| seal_return | 11 | 1 | 11.0 | 529,465 | 0 | 0.1% | 0.0% | +| caller | 8 | 1 | 8.0 | 403,000 | 0 | 0.1% | 0.0% | +| call_data_load | 7 | 1 | 7.0 | 343,000 | 0 | 0.1% | 0.0% | +| call_data_size | 7 | 1 | 7.0 | 330,000 | 0 | 0.1% | 0.0% | +| value_transferred | 7 | 1 | 7.0 | 319,000 | 0 | 0.1% | 0.0% | + +### FiatTokenProxy_evm - configureMinter + +- **Total Gas Used:** 527,114 +- **Base Call Weight:** ref_time=913,507,322, proof_size=8,531 +- **Total Weight:** ref_time=1,779,882,018, proof_size=85,389 +- **Weight Consumed:** ref_time=866,374,696 (48.7% of total), proof_size=76,858 + +| Opcode | Total Gas | Call Count | Avg Gas/Call | ref time | proof size | % of ref time | % of proof size | +| -------------- | --------- | ---------- | ------------ | ----------- | ---------- | ------------- | --------------- | +| SSTORE | 56,212 | 1 | 56212.0 | 170,600,294 | 10,257 | 19.7% | 13.3% | +| DELEGATECALL | 0 | 1 | 0.0 | 145,525,578 | 20,578 | 16.8% | 26.8% | +| SLOAD | 1,383 | 1 | 1383.0 | 69,164,719 | 10,289 | 8.0% | 13.4% | +| KECCAK256 | 305 | 1 | 305.0 | 15,219,717 | 0 | 1.8% | 0.0% | +| LOG2 | 275 | 1 | 275.0 | 13,751,564 | 0 | 1.6% | 0.0% | +| GAS | 39 | 1 | 39.0 | 1,929,000 | 0 | 0.2% | 0.0% | +| CALLDATACOPY | 10 | 1 | 10.0 | 502,804 | 0 | 0.1% | 0.0% | +| CODECOPY | 10 | 1 | 10.0 | 495,496 | 0 | 0.1% | 0.0% | +| RETURNDATACOPY | 10 | 1 | 10.0 | 495,496 | 0 | 0.1% | 0.0% | +| CALLER | 8 | 1 | 8.0 | 403,000 | 0 | 0.0% | 0.0% | +| CALLVALUE | 6 | 1 | 6.0 | 319,000 | 0 | 0.0% | 0.0% | +| JUMPI | 3 | 1 | 3.0 | 152,800 | 0 | 0.0% | 0.0% | +| JUMP | 2 | 1 | 2.0 | 122,240 | 0 | 0.0% | 0.0% | +| ADD | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| AND | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| CALLDATALOAD | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP1 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP2 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP3 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP4 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | + +### FiatTokenProxy_pvm - configureMinter + +- **Total Gas Used:** 532,541 +- **Base Call Weight:** ref_time=913,507,322, proof_size=8,531 +- **Total Weight:** ref_time=2,051,259,329, proof_size=198,590 +- **Weight Consumed:** ref_time=1,137,752,007 (55.5% of total), proof_size=190,059 + +| Opcode | Total Gas | Call Count | Avg Gas/Call | ref time | proof size | % of ref time | % of proof size | +| -------------------- | --------- | ---------- | ------------ | ----------- | ---------- | ------------- | --------------- | +| set_storage_or_clear | 56,212 | 1 | 56212.0 | 170,600,294 | 10,257 | 15.0% | 5.4% | +| delegate_call_evm | 0 | 1 | 0.0 | 162,677,640 | 20,578 | 14.3% | 10.8% | +| get_storage_or_zero | 1,382 | 1 | 1382.0 | 69,114,511 | 10,257 | 6.1% | 5.4% | +| hash_keccak_256 | 305 | 1 | 305.0 | 15,219,717 | 0 | 1.3% | 0.0% | +| deposit_event | 275 | 1 | 275.0 | 13,751,564 | 0 | 1.2% | 0.0% | +| ref_time_left | 38 | 1 | 38.0 | 1,929,000 | 0 | 0.2% | 0.0% | +| seal_return | 11 | 1 | 11.0 | 535,865 | 0 | 0.0% | 0.0% | +| call_data_copy | 10 | 1 | 10.0 | 504,260 | 0 | 0.0% | 0.0% | +| return_data_copy | 10 | 1 | 10.0 | 495,496 | 0 | 0.0% | 0.0% | +| caller | 9 | 1 | 9.0 | 403,000 | 0 | 0.0% | 0.0% | +| call_data_load | 7 | 1 | 7.0 | 343,000 | 0 | 0.0% | 0.0% | +| call_data_size | 7 | 1 | 7.0 | 330,000 | 0 | 0.0% | 0.0% | +| return_data_size | 6 | 1 | 6.0 | 319,000 | 0 | 0.0% | 0.0% | +| value_transferred | 6 | 1 | 6.0 | 319,000 | 0 | 0.0% | 0.0% | + +### FiatTokenProxy_evm - deploy + +- **Total Gas Used:** 18,701,177 +- **Base Call Weight:** ref_time=1,543,971,699, proof_size=6,934 +- **Total Weight:** ref_time=1,955,027,326, proof_size=31,458 +- **Weight Consumed:** ref_time=411,055,627 (21.0% of total), proof_size=24,524 + +| Opcode | Total Gas | Call Count | Avg Gas/Call | ref time | proof size | % of ref time | % of proof size | +| ----------- | --------- | ---------- | ------------ | ----------- | ---------- | ------------- | --------------- | +| SSTORE | 56,212 | 1 | 56212.0 | 170,600,294 | 10,257 | 41.5% | 41.8% | +| EXTCODESIZE | 1,283 | 1 | 1283.0 | 64,148,000 | 4,010 | 15.6% | 16.4% | +| CODECOPY | 15 | 1 | 15.0 | 761,223 | 0 | 0.2% | 0.0% | +| CALLER | 8 | 1 | 8.0 | 403,000 | 0 | 0.1% | 0.0% | +| CALLVALUE | 6 | 1 | 6.0 | 319,000 | 0 | 0.1% | 0.0% | +| JUMPI | 3 | 1 | 3.0 | 152,800 | 0 | 0.0% | 0.0% | +| ADD | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| AND | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP1 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP2 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP3 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP4 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP5 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| GT | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| ISZERO | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| LT | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| MSTORE | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| NOT | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| OR | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| PUSH1 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | + +### FiatTokenProxy_pvm - deploy + +- **Total Gas Used:** 36,964,533 +- **Base Call Weight:** ref_time=1,670,806,557, proof_size=6,934 +- **Total Weight:** ref_time=2,111,820,114, proof_size=31,458 +- **Weight Consumed:** ref_time=441,013,557 (20.9% of total), proof_size=24,524 + +| Opcode | Total Gas | Call Count | Avg Gas/Call | ref time | proof size | % of ref time | % of proof size | +| -------------------- | --------- | ---------- | ------------ | ----------- | ---------- | ------------- | --------------- | +| set_storage_or_clear | 56,212 | 1 | 56212.0 | 170,600,294 | 10,257 | 38.7% | 41.8% | +| code_size | 1,283 | 1 | 1283.0 | 64,148,000 | 4,010 | 14.5% | 16.4% | +| seal_return | 10 | 1 | 10.0 | 529,465 | 0 | 0.1% | 0.0% | +| call_data_copy | 10 | 1 | 10.0 | 496,576 | 0 | 0.1% | 0.0% | +| caller | 8 | 1 | 8.0 | 403,000 | 0 | 0.1% | 0.0% | +| call_data_size | 7 | 1 | 7.0 | 330,000 | 0 | 0.1% | 0.0% | +| value_transferred | 7 | 1 | 7.0 | 319,000 | 0 | 0.1% | 0.0% | + +### FiatTokenProxy_evm - initialize + +- **Total Gas Used:** 1,649,658 +- **Base Call Weight:** ref_time=918,265,544, proof_size=9,689 +- **Total Weight:** ref_time=3,467,091,226, proof_size=230,401 +- **Weight Consumed:** ref_time=2,548,825,682 (73.5% of total), proof_size=220,712 + +| Opcode | Total Gas | Call Count | Avg Gas/Call | ref time | proof size | % of ref time | % of proof size | +| ------------ | --------- | ---------- | ------------ | ------------- | ---------- | ------------- | --------------- | +| STOP | 9 | 1 | 9.0 | 2,548,351,436 | 220,712 | 100.0% | 100.0% | +| SSTORE | 3,413 | 1 | 3413.0 | 170,624,806 | 10,289 | 6.7% | 4.7% | +| DELEGATECALL | 0 | 1 | 0.0 | 145,603,530 | 20,578 | 5.7% | 9.3% | +| SLOAD | 1,383 | 1 | 1383.0 | 69,164,719 | 10,289 | 2.7% | 4.7% | +| GAS | 38 | 1 | 38.0 | 1,929,000 | 0 | 0.1% | 0.0% | +| CODECOPY | 10 | 1 | 10.0 | 495,496 | 0 | 0.0% | 0.0% | +| CALLDATACOPY | 10 | 1 | 10.0 | 489,609 | 0 | 0.0% | 0.0% | +| CALLER | 8 | 1 | 8.0 | 403,000 | 0 | 0.0% | 0.0% | +| CALLVALUE | 6 | 1 | 6.0 | 319,000 | 0 | 0.0% | 0.0% | +| JUMPI | 3 | 1 | 3.0 | 152,800 | 0 | 0.0% | 0.0% | +| JUMP | 2 | 1 | 2.0 | 122,240 | 0 | 0.0% | 0.0% | +| ADD | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| AND | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP16 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP2 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP3 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP4 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP5 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| EQ | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| GT | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | + +### FiatTokenProxy_pvm - initialize + +- **Total Gas Used:** 1,658,411 +- **Base Call Weight:** ref_time=918,265,544, proof_size=9,689 +- **Total Weight:** ref_time=3,904,751,659, proof_size=343,346 +- **Weight Consumed:** ref_time=2,986,486,115 (76.5% of total), proof_size=333,657 + +| Opcode | Total Gas | Call Count | Avg Gas/Call | ref time | proof size | % of ref time | % of proof size | +| -------------------- | --------- | ---------- | ------------ | ----------- | ---------- | ------------- | --------------- | +| set_storage_or_clear | 3,413 | 1 | 3413.0 | 170,624,806 | 10,289 | 5.7% | 3.1% | +| delegate_call_evm | 0 | 1 | 0.0 | 162,819,492 | 20,578 | 5.5% | 6.2% | +| get_storage_or_zero | 1,383 | 1 | 1383.0 | 69,164,719 | 10,289 | 2.3% | 3.1% | +| ref_time_left | 38 | 1 | 38.0 | 1,929,000 | 0 | 0.1% | 0.0% | +| seal_return | 11 | 1 | 11.0 | 529,465 | 0 | 0.0% | 0.0% | +| call_data_copy | 10 | 1 | 10.0 | 496,915 | 0 | 0.0% | 0.0% | +| return_data_copy | 10 | 1 | 10.0 | 489,000 | 0 | 0.0% | 0.0% | +| caller | 9 | 1 | 9.0 | 403,000 | 0 | 0.0% | 0.0% | +| call_data_load | 7 | 1 | 7.0 | 343,000 | 0 | 0.0% | 0.0% | +| call_data_size | 6 | 1 | 6.0 | 330,000 | 0 | 0.0% | 0.0% | +| return_data_size | 7 | 1 | 7.0 | 319,000 | 0 | 0.0% | 0.0% | +| value_transferred | 7 | 1 | 7.0 | 319,000 | 0 | 0.0% | 0.0% | + +### FiatTokenProxy_evm - mint + +- **Total Gas Used:** 487,007 +- **Base Call Weight:** ref_time=913,507,322, proof_size=8,531 +- **Total Weight:** ref_time=2,414,524,981, proof_size=147,123 +- **Weight Consumed:** ref_time=1,501,017,659 (62.2% of total), proof_size=138,592 + +| Opcode | Total Gas | Call Count | Avg Gas/Call | ref time | proof size | % of ref time | % of proof size | +| -------------- | --------- | ---------- | ------------ | ----------- | ---------- | ------------- | --------------- | +| SSTORE | 0 | 1 | 0.0 | 170,606,822 | 10,289 | 11.4% | 7.4% | +| DELEGATECALL | 0 | 1 | 0.0 | 145,525,578 | 20,578 | 9.7% | 14.8% | +| SLOAD | 1,384 | 1 | 1384.0 | 69,164,719 | 10,289 | 4.6% | 7.4% | +| KECCAK256 | 304 | 1 | 304.0 | 15,219,717 | 0 | 1.0% | 0.0% | +| LOG3 | 275 | 1 | 275.0 | 13,751,564 | 0 | 0.9% | 0.0% | +| GAS | 39 | 1 | 39.0 | 1,929,000 | 0 | 0.1% | 0.0% | +| CALLDATACOPY | 10 | 1 | 10.0 | 502,804 | 0 | 0.0% | 0.0% | +| CODECOPY | 10 | 1 | 10.0 | 495,496 | 0 | 0.0% | 0.0% | +| RETURNDATACOPY | 10 | 1 | 10.0 | 495,496 | 0 | 0.0% | 0.0% | +| CALLER | 8 | 1 | 8.0 | 403,000 | 0 | 0.0% | 0.0% | +| CALLVALUE | 6 | 1 | 6.0 | 319,000 | 0 | 0.0% | 0.0% | +| JUMPI | 3 | 1 | 3.0 | 152,800 | 0 | 0.0% | 0.0% | +| JUMP | 3 | 1 | 3.0 | 122,240 | 0 | 0.0% | 0.0% | +| ADD | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| AND | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| CALLDATALOAD | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP1 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP2 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP3 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP4 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | + +### FiatTokenProxy_pvm - mint + +- **Total Gas Used:** 495,236 +- **Base Call Weight:** ref_time=913,507,322, proof_size=8,531 +- **Total Weight:** ref_time=2,825,972,306, proof_size=260,293 +- **Weight Consumed:** ref_time=1,912,464,984 (67.7% of total), proof_size=251,762 + +| Opcode | Total Gas | Call Count | Avg Gas/Call | ref time | proof size | % of ref time | % of proof size | +| -------------------- | --------- | ---------- | ------------ | ----------- | ---------- | ------------- | --------------- | +| set_storage_or_clear | 0 | 1 | 0.0 | 172,779,550 | 10,386 | 9.0% | 4.1% | +| delegate_call_evm | 0 | 1 | 0.0 | 162,677,640 | 20,578 | 8.5% | 8.2% | +| get_storage_or_zero | 1,383 | 1 | 1383.0 | 69,114,511 | 10,257 | 3.6% | 4.1% | +| hash_keccak_256 | 305 | 1 | 305.0 | 15,219,717 | 0 | 0.8% | 0.0% | +| deposit_event | 275 | 1 | 275.0 | 13,751,564 | 0 | 0.7% | 0.0% | +| ref_time_left | 38 | 1 | 38.0 | 1,929,000 | 0 | 0.1% | 0.0% | +| seal_return | 11 | 1 | 11.0 | 535,865 | 0 | 0.0% | 0.0% | +| call_data_copy | 10 | 1 | 10.0 | 504,260 | 0 | 0.0% | 0.0% | +| return_data_copy | 10 | 1 | 10.0 | 495,496 | 0 | 0.0% | 0.0% | +| caller | 8 | 1 | 8.0 | 403,000 | 0 | 0.0% | 0.0% | +| call_data_load | 6 | 1 | 6.0 | 343,000 | 0 | 0.0% | 0.0% | +| call_data_size | 7 | 1 | 7.0 | 330,000 | 0 | 0.0% | 0.0% | +| return_data_size | 6 | 1 | 6.0 | 319,000 | 0 | 0.0% | 0.0% | +| value_transferred | 6 | 1 | 6.0 | 319,000 | 0 | 0.0% | 0.0% | + +### FiatTokenProxy_evm - transfer + +- **Total Gas Used:** 430,978 +- **Base Call Weight:** ref_time=913,507,322, proof_size=8,531 +- **Total Weight:** ref_time=2,253,109,736, proof_size=136,898 +- **Weight Consumed:** ref_time=1,339,602,414 (59.5% of total), proof_size=128,367 + +| Opcode | Total Gas | Call Count | Avg Gas/Call | ref time | proof size | % of ref time | % of proof size | +| -------------- | --------- | ---------- | ------------ | ----------- | ---------- | ------------- | --------------- | +| SSTORE | 3,412 | 1 | 3412.0 | 170,624,806 | 10,289 | 12.7% | 8.0% | +| DELEGATECALL | 0 | 1 | 0.0 | 145,525,578 | 20,578 | 10.9% | 16.0% | +| SLOAD | 1,383 | 1 | 1383.0 | 69,164,719 | 10,289 | 5.2% | 8.0% | +| KECCAK256 | 304 | 1 | 304.0 | 15,219,717 | 0 | 1.1% | 0.0% | +| LOG3 | 275 | 1 | 275.0 | 13,751,564 | 0 | 1.0% | 0.0% | +| GAS | 39 | 1 | 39.0 | 1,929,000 | 0 | 0.1% | 0.0% | +| CALLDATACOPY | 10 | 1 | 10.0 | 502,804 | 0 | 0.0% | 0.0% | +| CODECOPY | 10 | 1 | 10.0 | 495,496 | 0 | 0.0% | 0.0% | +| RETURNDATACOPY | 10 | 1 | 10.0 | 495,496 | 0 | 0.0% | 0.0% | +| CALLER | 8 | 1 | 8.0 | 403,000 | 0 | 0.0% | 0.0% | +| CALLVALUE | 7 | 1 | 7.0 | 319,000 | 0 | 0.0% | 0.0% | +| JUMPI | 3 | 1 | 3.0 | 152,800 | 0 | 0.0% | 0.0% | +| JUMP | 2 | 1 | 2.0 | 122,240 | 0 | 0.0% | 0.0% | +| ADD | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| AND | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| CALLDATALOAD | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP1 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP10 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP2 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP3 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | + +### FiatTokenProxy_pvm - transfer + +- **Total Gas Used:** 438,519 +- **Base Call Weight:** ref_time=913,507,322, proof_size=8,531 +- **Total Weight:** ref_time=2,630,114,247, proof_size=250,131 +- **Weight Consumed:** ref_time=1,716,606,925 (65.3% of total), proof_size=241,600 + +| Opcode | Total Gas | Call Count | Avg Gas/Call | ref time | proof size | % of ref time | % of proof size | +| -------------------- | --------- | ---------- | ------------ | ----------- | ---------- | ------------- | --------------- | +| set_storage_or_clear | 3,412 | 1 | 3412.0 | 170,624,806 | 10,289 | 9.9% | 4.3% | +| delegate_call_evm | 0 | 1 | 0.0 | 162,677,640 | 20,578 | 9.5% | 8.5% | +| get_storage_or_zero | 1,383 | 1 | 1383.0 | 69,164,719 | 10,289 | 4.0% | 4.3% | +| hash_keccak_256 | 304 | 1 | 304.0 | 15,219,717 | 0 | 0.9% | 0.0% | +| deposit_event | 275 | 1 | 275.0 | 13,751,564 | 0 | 0.8% | 0.0% | +| ref_time_left | 38 | 1 | 38.0 | 1,929,000 | 0 | 0.1% | 0.0% | +| seal_return | 11 | 1 | 11.0 | 535,865 | 0 | 0.0% | 0.0% | +| call_data_copy | 10 | 1 | 10.0 | 504,260 | 0 | 0.0% | 0.0% | +| return_data_copy | 10 | 1 | 10.0 | 495,496 | 0 | 0.0% | 0.0% | +| caller | 8 | 1 | 8.0 | 403,000 | 0 | 0.0% | 0.0% | +| call_data_load | 6 | 1 | 6.0 | 343,000 | 0 | 0.0% | 0.0% | +| call_data_size | 7 | 1 | 7.0 | 330,000 | 0 | 0.0% | 0.0% | +| return_data_size | 6 | 1 | 6.0 | 319,000 | 0 | 0.0% | 0.0% | +| value_transferred | 6 | 1 | 6.0 | 319,000 | 0 | 0.0% | 0.0% | + +### FiatTokenProxy_evm - transferFrom + +- **Total Gas Used:** 504,651 +- **Base Call Weight:** ref_time=913,901,786, proof_size=8,627 +- **Total Weight:** ref_time=2,736,718,638, proof_size=178,150 +- **Weight Consumed:** ref_time=1,822,816,852 (66.6% of total), proof_size=169,523 + +| Opcode | Total Gas | Call Count | Avg Gas/Call | ref time | proof size | % of ref time | % of proof size | +| -------------- | --------- | ---------- | ------------ | ----------- | ---------- | ------------- | --------------- | +| SSTORE | 3,413 | 1 | 3413.0 | 170,624,806 | 10,289 | 9.4% | 6.1% | +| DELEGATECALL | 0 | 1 | 0.0 | 145,532,074 | 20,578 | 8.0% | 12.1% | +| SLOAD | 1,384 | 1 | 1384.0 | 69,164,719 | 10,289 | 3.8% | 6.1% | +| KECCAK256 | 305 | 1 | 305.0 | 15,219,717 | 0 | 0.8% | 0.0% | +| LOG3 | 275 | 1 | 275.0 | 13,751,564 | 0 | 0.8% | 0.0% | +| GAS | 39 | 1 | 39.0 | 1,929,000 | 0 | 0.1% | 0.0% | +| CALLDATACOPY | 11 | 1 | 11.0 | 509,300 | 0 | 0.0% | 0.0% | +| CODECOPY | 10 | 1 | 10.0 | 495,496 | 0 | 0.0% | 0.0% | +| RETURNDATACOPY | 10 | 1 | 10.0 | 495,496 | 0 | 0.0% | 0.0% | +| CALLER | 8 | 1 | 8.0 | 403,000 | 0 | 0.0% | 0.0% | +| CALLVALUE | 7 | 1 | 7.0 | 319,000 | 0 | 0.0% | 0.0% | +| JUMPI | 3 | 1 | 3.0 | 152,800 | 0 | 0.0% | 0.0% | +| JUMP | 3 | 1 | 3.0 | 122,240 | 0 | 0.0% | 0.0% | +| ADD | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| AND | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP1 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP10 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP2 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP3 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP4 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | + +### FiatTokenProxy_pvm - transferFrom + +- **Total Gas Used:** 514,706 +- **Base Call Weight:** ref_time=913,901,786, proof_size=8,627 +- **Total Weight:** ref_time=3,239,499,195, proof_size=291,383 +- **Weight Consumed:** ref_time=2,325,597,409 (71.8% of total), proof_size=282,756 + +| Opcode | Total Gas | Call Count | Avg Gas/Call | ref time | proof size | % of ref time | % of proof size | +| -------------------- | --------- | ---------- | ------------ | ----------- | ---------- | ------------- | --------------- | +| set_storage_or_clear | 3,413 | 1 | 3413.0 | 170,624,806 | 10,289 | 7.3% | 3.6% | +| delegate_call_evm | 0 | 1 | 0.0 | 162,779,716 | 20,578 | 7.0% | 7.3% | +| get_storage_or_zero | 1,384 | 1 | 1384.0 | 69,164,719 | 10,289 | 3.0% | 3.6% | +| hash_keccak_256 | 304 | 1 | 304.0 | 15,219,717 | 0 | 0.7% | 0.0% | +| deposit_event | 275 | 1 | 275.0 | 13,751,564 | 0 | 0.6% | 0.0% | +| ref_time_left | 38 | 1 | 38.0 | 1,929,000 | 0 | 0.1% | 0.0% | +| seal_return | 11 | 1 | 11.0 | 535,865 | 0 | 0.0% | 0.0% | +| call_data_copy | 10 | 1 | 10.0 | 507,876 | 0 | 0.0% | 0.0% | +| return_data_copy | 10 | 1 | 10.0 | 495,496 | 0 | 0.0% | 0.0% | +| caller | 8 | 1 | 8.0 | 403,000 | 0 | 0.0% | 0.0% | +| call_data_load | 7 | 1 | 7.0 | 343,000 | 0 | 0.0% | 0.0% | +| call_data_size | 7 | 1 | 7.0 | 330,000 | 0 | 0.0% | 0.0% | +| return_data_size | 7 | 1 | 7.0 | 319,000 | 0 | 0.0% | 0.0% | +| value_transferred | 7 | 1 | 7.0 | 319,000 | 0 | 0.0% | 0.0% | + +### FiatTokenV2_2_evm - deploy + +- **Total Gas Used:** 49,899,060 +- **Base Call Weight:** ref_time=1,762,243,891, proof_size=6,934 +- **Total Weight:** ref_time=2,418,189,787, proof_size=58,283 +- **Weight Consumed:** ref_time=655,945,896 (27.1% of total), proof_size=51,349 + +| Opcode | Total Gas | Call Count | Avg Gas/Call | ref time | proof size | % of ref time | % of proof size | +| --------- | --------- | ---------- | ------------ | ----------- | ---------- | ------------- | --------------- | +| SSTORE | 3,412 | 1 | 3412.0 | 170,582,310 | 10,257 | 26.0% | 20.0% | +| SLOAD | 1,383 | 1 | 1383.0 | 69,164,719 | 10,289 | 10.5% | 20.0% | +| CODECOPY | 73 | 1 | 73.0 | 3,616,824 | 0 | 0.6% | 0.0% | +| CALLER | 8 | 1 | 8.0 | 403,000 | 0 | 0.1% | 0.0% | +| CALLVALUE | 7 | 1 | 7.0 | 319,000 | 0 | 0.0% | 0.0% | +| JUMPI | 3 | 1 | 3.0 | 152,800 | 0 | 0.0% | 0.0% | +| DUP1 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP2 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP3 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| MSTORE | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| NOT | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| OR | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| PUSH1 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| PUSH2 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| SHL | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| SUB | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| AND | 0 | 1 | 0.0 | 45,840 | 0 | 0.0% | 0.0% | +| SWAP1 | 0 | 1 | 0.0 | 45,840 | 0 | 0.0% | 0.0% | +| PUSH0 | 0 | 1 | 0.0 | 30,560 | 0 | 0.0% | 0.0% | +| RETURN | 0 | 1 | 0.0 | 0 | 0 | 0.0% | 0.0% | + +### FiatTokenV2_2_pvm - deploy + +- **Total Gas Used:** 249,011,072 +- **Base Call Weight:** ref_time=3,156,166,319, proof_size=6,934 +- **Total Weight:** ref_time=3,837,783,006, proof_size=58,477 +- **Weight Consumed:** ref_time=681,616,687 (17.8% of total), proof_size=51,543 + +| Opcode | Total Gas | Call Count | Avg Gas/Call | ref time | proof size | % of ref time | % of proof size | +| -------------------- | --------- | ---------- | ------------ | ----------- | ---------- | ------------- | --------------- | +| set_storage_or_clear | 3,456 | 1 | 3456.0 | 172,779,550 | 10,386 | 25.3% | 20.2% | +| get_storage_or_zero | 1,382 | 1 | 1382.0 | 69,114,511 | 10,257 | 10.1% | 19.9% | +| seal_return | 10 | 1 | 10.0 | 529,465 | 0 | 0.1% | 0.0% | +| call_data_copy | 10 | 1 | 10.0 | 496,576 | 0 | 0.1% | 0.0% | +| caller | 8 | 1 | 8.0 | 403,000 | 0 | 0.1% | 0.0% | +| call_data_size | 6 | 1 | 6.0 | 330,000 | 0 | 0.0% | 0.0% | +| value_transferred | 6 | 1 | 6.0 | 319,000 | 0 | 0.0% | 0.0% | + ### Fibonacci_evm - deploy -- **Total Gas Used:** 7,677,100 -- **Base Call Weight:** ref_time=1,523,651,115, proof_size=6,934 -- **Total Weight:** ref_time=1,525,205,124, proof_size=6,934 -- **Weight Consumed:** ref_time=1,554,009 (0.1% of total), proof_size=0 +- **Total Gas Used:** 15,557,530 +- **Base Call Weight:** ref_time=1,522,210,949, proof_size=6,934 +- **Total Weight:** ref_time=1,523,685,771, proof_size=6,934 +- **Weight Consumed:** ref_time=1,474,822 (0.1% of total), proof_size=0 | Opcode | Total Gas | Call Count | Avg Gas/Call | ref time | proof size | % of ref time | % of proof size | | --------- | --------- | ---------- | ------------ | -------- | ---------- | ------------- | --------------- | -| CODECOPY | 11 | 1 | 11.0 | 562,689 | 0 | 36.2% | 0.0% | -| CALLVALUE | 7 | 1 | 7.0 | 319,000 | 0 | 20.5% | 0.0% | -| JUMPI | 3 | 1 | 3.0 | 152,800 | 0 | 9.8% | 0.0% | -| ISZERO | 1 | 1 | 1.0 | 45,840 | 0 | 2.9% | 0.0% | -| POP | 1 | 1 | 1.0 | 30,560 | 0 | 2.0% | 0.0% | -| PUSH0 | 1 | 1 | 1.0 | 30,560 | 0 | 2.0% | 0.0% | -| PUSH1 | 1 | 1 | 1.0 | 45,840 | 0 | 2.9% | 0.0% | -| PUSH2 | 1 | 1 | 1.0 | 45,840 | 0 | 2.9% | 0.0% | -| DUP1 | 0 | 1 | 0.0 | 45,840 | 0 | 2.9% | 0.0% | -| JUMPDEST | 0 | 1 | 0.0 | 15,280 | 0 | 1.0% | 0.0% | -| MSTORE | 0 | 1 | 0.0 | 45,840 | 0 | 2.9% | 0.0% | +| CODECOPY | 10 | 1 | 10.0 | 544,622 | 0 | 36.9% | 0.0% | +| CALLVALUE | 6 | 1 | 6.0 | 319,000 | 0 | 21.6% | 0.0% | +| JUMPI | 3 | 1 | 3.0 | 152,800 | 0 | 10.4% | 0.0% | +| DUP1 | 1 | 1 | 1.0 | 45,840 | 0 | 3.1% | 0.0% | +| DUP2 | 1 | 1 | 1.0 | 45,840 | 0 | 3.1% | 0.0% | +| DUP3 | 1 | 1 | 1.0 | 45,840 | 0 | 3.1% | 0.0% | +| MSTORE | 1 | 1 | 1.0 | 45,840 | 0 | 3.1% | 0.0% | +| PUSH1 | 1 | 1 | 1.0 | 45,840 | 0 | 3.1% | 0.0% | +| PUSH2 | 1 | 1 | 1.0 | 45,840 | 0 | 3.1% | 0.0% | +| SWAP1 | 1 | 1 | 1.0 | 45,840 | 0 | 3.1% | 0.0% | | RETURN | 0 | 1 | 0.0 | 0 | 0 | 0.0% | 0.0% | - ### Fibonacci_pvm - deploy -- **Total Gas Used:** 10,767,375 -- **Base Call Weight:** ref_time=1,547,374,289, proof_size=6,934 -- **Total Weight:** ref_time=1,557,918,056, proof_size=6,934 -- **Weight Consumed:** ref_time=10,543,767 (0.7% of total), proof_size=0 +- **Total Gas Used:** 18,382,827 +- **Base Call Weight:** ref_time=1,541,930,145, proof_size=6,934 +- **Total Weight:** ref_time=1,552,523,142, proof_size=6,934 +- **Weight Consumed:** ref_time=10,592,997 (0.7% of total), proof_size=0 | Opcode | Total Gas | Call Count | Avg Gas/Call | ref time | proof size | % of ref time | % of proof size | | ----------------- | --------- | ---------- | ------------ | -------- | ---------- | ------------- | --------------- | -| seal_return | 11 | 1 | 11.0 | 529,465 | 0 | 5.0% | 0.0% | +| seal_return | 10 | 1 | 10.0 | 529,465 | 0 | 5.0% | 0.0% | | call_data_copy | 10 | 1 | 10.0 | 496,576 | 0 | 4.7% | 0.0% | -| value_transferred | 7 | 1 | 7.0 | 319,000 | 0 | 3.0% | 0.0% | | call_data_size | 6 | 1 | 6.0 | 330,000 | 0 | 3.1% | 0.0% | - +| value_transferred | 7 | 1 | 7.0 | 319,000 | 0 | 3.0% | 0.0% | ### fibonacci_ink - deploy -- **Total Gas Used:** 17,092,219 -- **Base Call Weight:** ref_time=1,532,561,153, proof_size=6,934 -- **Total Weight:** ref_time=1,718,162,422, proof_size=17,191 +- **Total Gas Used:** 17,426,746 +- **Base Call Weight:** ref_time=1,534,903,401, proof_size=6,934 +- **Total Weight:** ref_time=1,720,504,670, proof_size=17,191 - **Weight Consumed:** ref_time=185,601,269 (10.8% of total), proof_size=10,257 | Opcode | Total Gas | Call Count | Avg Gas/Call | ref time | proof size | % of ref time | % of proof size | | ----------------- | --------- | ---------- | ------------ | ----------- | ---------- | ------------- | --------------- | -| set_storage | 47,412 | 1 | 47412.0 | 170,582,310 | 10,257 | 91.9% | 100.0% | +| set_storage | 47,411 | 1 | 47411.0 | 170,582,310 | 10,257 | 91.9% | 100.0% | | call_data_copy | 47 | 1 | 47.0 | 2,347,968 | 0 | 1.3% | 0.0% | | seal_return | 11 | 1 | 11.0 | 529,465 | 0 | 0.3% | 0.0% | | value_transferred | 7 | 1 | 7.0 | 319,000 | 0 | 0.2% | 0.0% | - ### Fibonacci_evm - fib_10 -- **Total Gas Used:** 350,299 +- **Total Gas Used:** 346,268 - **Base Call Weight:** ref_time=913,100,531, proof_size=8,432 -- **Total Weight:** ref_time=1,519,150,709, proof_size=8,432 -- **Weight Consumed:** ref_time=606,050,178 (39.9% of total), proof_size=0 +- **Total Weight:** ref_time=1,317,572,855, proof_size=8,432 +- **Weight Consumed:** ref_time=404,472,324 (30.7% of total), proof_size=0 | Opcode | Total Gas | Call Count | Avg Gas/Call | ref time | proof size | % of ref time | % of proof size | | ------------ | --------- | ---------- | ------------ | -------- | ---------- | ------------- | --------------- | @@ -199,150 +1301,140 @@ Generated on: 2026-01-13 | DUP1 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | | DUP2 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | | DUP3 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | -| DUP4 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | -| DUP5 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | -| DUP6 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | | EQ | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | | GT | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | | ISZERO | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | -| JUMPDEST | 1 | 1 | 1.0 | 15,280 | 0 | 0.0% | 0.0% | | LT | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | | MSTORE | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | -| POP | 1 | 1 | 1.0 | 30,560 | 0 | 0.0% | 0.0% | -| PUSH0 | 1 | 1 | 1.0 | 30,560 | 0 | 0.0% | 0.0% | - +| NOT | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| PUSH1 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| PUSH4 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| SHR | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| SLT | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| SUB | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | ### Fibonacci_pvm - fib_10 -- **Total Gas Used:** 351,658 +- **Total Gas Used:** 347,222 - **Base Call Weight:** ref_time=913,100,531, proof_size=8,432 -- **Total Weight:** ref_time=1,587,091,712, proof_size=10,322 -- **Weight Consumed:** ref_time=673,991,181 (42.5% of total), proof_size=1,890 +- **Total Weight:** ref_time=1,365,307,158, proof_size=9,978 +- **Weight Consumed:** ref_time=452,206,627 (33.1% of total), proof_size=1,546 | Opcode | Total Gas | Call Count | Avg Gas/Call | ref time | proof size | % of ref time | % of proof size | | ----------------- | --------- | ---------- | ------------ | -------- | ---------- | ------------- | --------------- | | seal_return | 11 | 1 | 11.0 | 529,465 | 0 | 0.1% | 0.0% | | call_data_load | 7 | 1 | 7.0 | 343,000 | 0 | 0.1% | 0.0% | -| value_transferred | 7 | 1 | 7.0 | 319,000 | 0 | 0.0% | 0.0% | -| call_data_size | 6 | 1 | 6.0 | 330,000 | 0 | 0.0% | 0.0% | - +| call_data_size | 6 | 1 | 6.0 | 330,000 | 0 | 0.1% | 0.0% | +| value_transferred | 6 | 1 | 6.0 | 319,000 | 0 | 0.1% | 0.0% | ### fibonacci_ink - fib_10 -- **Total Gas Used:** 342,915 +- **Total Gas Used:** 342,941 - **Base Call Weight:** ref_time=913,100,531, proof_size=8,432 -- **Total Weight:** ref_time=1,149,918,571, proof_size=19,643 -- **Weight Consumed:** ref_time=236,818,040 (20.6% of total), proof_size=11,211 +- **Total Weight:** ref_time=1,151,214,899, proof_size=19,791 +- **Weight Consumed:** ref_time=238,114,368 (20.7% of total), proof_size=11,359 | Opcode | Total Gas | Call Count | Avg Gas/Call | ref time | proof size | % of ref time | % of proof size | | ----------------- | --------- | ---------- | ------------ | ---------- | ---------- | ------------- | --------------- | -| get_storage | 1,382 | 1 | 1382.0 | 69,114,511 | 10,257 | 29.2% | 91.5% | +| get_storage | 1,382 | 1 | 1382.0 | 69,114,511 | 10,257 | 29.0% | 90.3% | | call_data_copy | 47 | 1 | 47.0 | 2,347,968 | 0 | 1.0% | 0.0% | -| seal_return | 10 | 1 | 10.0 | 529,465 | 0 | 0.2% | 0.0% | +| seal_return | 11 | 1 | 11.0 | 529,465 | 0 | 0.2% | 0.0% | | value_transferred | 6 | 1 | 6.0 | 319,000 | 0 | 0.1% | 0.0% | - ### fibonacci_rust - fib_10 -- **Total Gas Used:** 339,996 +- **Total Gas Used:** 339,995 - **Base Call Weight:** ref_time=913,100,531, proof_size=8,432 -- **Total Weight:** ref_time=1,003,971,839, proof_size=8,652 -- **Weight Consumed:** ref_time=90,871,308 (9.1% of total), proof_size=220 +- **Total Weight:** ref_time=1,003,955,988, proof_size=8,641 +- **Weight Consumed:** ref_time=90,855,457 (9.0% of total), proof_size=209 | Opcode | Total Gas | Call Count | Avg Gas/Call | ref time | proof size | % of ref time | % of proof size | | -------------- | --------- | ---------- | ------------ | -------- | ---------- | ------------- | --------------- | | call_data_copy | 10 | 1 | 10.0 | 497,028 | 0 | 0.5% | 0.0% | - ### fibonacci_u128_rust - fib_10 - **Total Gas Used:** 341,847 - **Base Call Weight:** ref_time=913,100,531, proof_size=8,432 -- **Total Weight:** ref_time=1,096,536,651, proof_size=8,728 -- **Weight Consumed:** ref_time=183,436,120 (16.7% of total), proof_size=296 +- **Total Weight:** ref_time=1,096,520,800, proof_size=8,717 +- **Weight Consumed:** ref_time=183,420,269 (16.7% of total), proof_size=285 | Opcode | Total Gas | Call Count | Avg Gas/Call | ref time | proof size | % of ref time | % of proof size | | -------------- | --------- | ---------- | ------------ | -------- | ---------- | ------------- | --------------- | | call_data_copy | 10 | 1 | 10.0 | 498,384 | 0 | 0.3% | 0.0% | - ### fibonacci_u256_rust - fib_10 -- **Total Gas Used:** 370,173 +- **Total Gas Used:** 370,172 - **Base Call Weight:** ref_time=913,100,531, proof_size=8,432 -- **Total Weight:** ref_time=2,512,824,851, proof_size=9,130 -- **Weight Consumed:** ref_time=1,599,724,320 (63.7% of total), proof_size=698 +- **Total Weight:** ref_time=2,512,807,559, proof_size=9,118 +- **Weight Consumed:** ref_time=1,599,707,028 (63.7% of total), proof_size=686 | Opcode | Total Gas | Call Count | Avg Gas/Call | ref time | proof size | % of ref time | % of proof size | | -------------- | --------- | ---------- | ------------ | -------- | ---------- | ------------- | --------------- | | call_data_copy | 10 | 1 | 10.0 | 500,192 | 0 | 0.0% | 0.0% | - ### SimpleToken_evm - deploy -- **Total Gas Used:** 8,082,983 -- **Base Call Weight:** ref_time=1,526,768,837, proof_size=6,934 -- **Total Weight:** ref_time=1,528,362,837, proof_size=6,934 -- **Weight Consumed:** ref_time=1,594,000 (0.1% of total), proof_size=0 +- **Total Gas Used:** 15,980,210 +- **Base Call Weight:** ref_time=1,525,170,411, proof_size=6,934 +- **Total Weight:** ref_time=1,526,683,194, proof_size=6,934 +- **Weight Consumed:** ref_time=1,512,783 (0.1% of total), proof_size=0 | Opcode | Total Gas | Call Count | Avg Gas/Call | ref time | proof size | % of ref time | % of proof size | | --------- | --------- | ---------- | ------------ | -------- | ---------- | ------------- | --------------- | -| CODECOPY | 12 | 1 | 12.0 | 602,680 | 0 | 37.8% | 0.0% | -| CALLVALUE | 6 | 1 | 6.0 | 319,000 | 0 | 20.0% | 0.0% | -| JUMPI | 3 | 1 | 3.0 | 152,800 | 0 | 9.6% | 0.0% | -| DUP1 | 1 | 1 | 1.0 | 45,840 | 0 | 2.9% | 0.0% | -| ISZERO | 1 | 1 | 1.0 | 45,840 | 0 | 2.9% | 0.0% | -| MSTORE | 1 | 1 | 1.0 | 45,840 | 0 | 2.9% | 0.0% | -| POP | 1 | 1 | 1.0 | 30,560 | 0 | 1.9% | 0.0% | -| PUSH0 | 1 | 1 | 1.0 | 30,560 | 0 | 1.9% | 0.0% | -| PUSH1 | 1 | 1 | 1.0 | 45,840 | 0 | 2.9% | 0.0% | -| PUSH2 | 1 | 1 | 1.0 | 45,840 | 0 | 2.9% | 0.0% | -| JUMPDEST | 0 | 1 | 0.0 | 15,280 | 0 | 1.0% | 0.0% | +| CODECOPY | 11 | 1 | 11.0 | 582,583 | 0 | 38.5% | 0.0% | +| CALLVALUE | 6 | 1 | 6.0 | 319,000 | 0 | 21.1% | 0.0% | +| JUMPI | 3 | 1 | 3.0 | 152,800 | 0 | 10.1% | 0.0% | +| DUP1 | 1 | 1 | 1.0 | 45,840 | 0 | 3.0% | 0.0% | +| DUP2 | 1 | 1 | 1.0 | 45,840 | 0 | 3.0% | 0.0% | +| DUP3 | 1 | 1 | 1.0 | 45,840 | 0 | 3.0% | 0.0% | +| MSTORE | 1 | 1 | 1.0 | 45,840 | 0 | 3.0% | 0.0% | +| PUSH1 | 1 | 1 | 1.0 | 45,840 | 0 | 3.0% | 0.0% | +| PUSH2 | 1 | 1 | 1.0 | 45,840 | 0 | 3.0% | 0.0% | +| SWAP1 | 1 | 1 | 1.0 | 45,840 | 0 | 3.0% | 0.0% | | RETURN | 0 | 1 | 0.0 | 0 | 0 | 0.0% | 0.0% | - ### SimpleToken_pvm - deploy -- **Total Gas Used:** 17,673,493 -- **Base Call Weight:** ref_time=1,600,407,215, proof_size=6,934 -- **Total Weight:** ref_time=1,610,852,522, proof_size=6,934 -- **Weight Consumed:** ref_time=10,445,307 (0.6% of total), proof_size=0 +- **Total Gas Used:** 26,354,961 +- **Base Call Weight:** ref_time=1,597,748,447, proof_size=6,934 +- **Total Weight:** ref_time=1,608,242,984, proof_size=6,934 +- **Weight Consumed:** ref_time=10,494,537 (0.7% of total), proof_size=0 | Opcode | Total Gas | Call Count | Avg Gas/Call | ref time | proof size | % of ref time | % of proof size | | ----------------- | --------- | ---------- | ------------ | -------- | ---------- | ------------- | --------------- | -| seal_return | 11 | 1 | 11.0 | 529,465 | 0 | 5.1% | 0.0% | -| call_data_copy | 10 | 1 | 10.0 | 496,576 | 0 | 4.8% | 0.0% | -| call_data_size | 7 | 1 | 7.0 | 330,000 | 0 | 3.2% | 0.0% | -| value_transferred | 6 | 1 | 6.0 | 319,000 | 0 | 3.1% | 0.0% | - +| seal_return | 11 | 1 | 11.0 | 529,465 | 0 | 5.0% | 0.0% | +| call_data_copy | 10 | 1 | 10.0 | 496,576 | 0 | 4.7% | 0.0% | +| call_data_size | 7 | 1 | 7.0 | 330,000 | 0 | 3.1% | 0.0% | +| value_transferred | 6 | 1 | 6.0 | 319,000 | 0 | 3.0% | 0.0% | ### simple_token_ink - deploy -- **Total Gas Used:** 21,368,560 -- **Base Call Weight:** ref_time=1,628,371,757, proof_size=6,934 -- **Total Weight:** ref_time=1,843,184,400, proof_size=17,191 -- **Weight Consumed:** ref_time=214,812,643 (11.7% of total), proof_size=10,257 +- **Total Gas Used:** 31,332,424 +- **Base Call Weight:** ref_time=1,632,217,475, proof_size=6,934 +- **Total Weight:** ref_time=1,847,374,728, proof_size=17,191 +- **Weight Consumed:** ref_time=215,157,253 (11.6% of total), proof_size=10,257 | Opcode | Total Gas | Call Count | Avg Gas/Call | ref time | proof size | % of ref time | % of proof size | | ----------------- | --------- | ---------- | ------------ | ----------- | ---------- | ------------- | --------------- | -| set_storage | 53,812 | 1 | 53812.0 | 170,600,294 | 10,257 | 79.4% | 100.0% | +| set_storage | 53,812 | 1 | 53812.0 | 170,600,294 | 10,257 | 79.3% | 100.0% | | call_data_copy | 47 | 1 | 47.0 | 2,347,968 | 0 | 1.1% | 0.0% | | seal_return | 10 | 1 | 10.0 | 529,465 | 0 | 0.2% | 0.0% | | value_transferred | 6 | 1 | 6.0 | 319,000 | 0 | 0.1% | 0.0% | - ### SimpleToken_evm - mint -- **Total Gas Used:** 520,147 +- **Total Gas Used:** 520,096 - **Base Call Weight:** ref_time=913,507,322, proof_size=8,531 -- **Total Weight:** ref_time=1,431,551,949, proof_size=49,623 -- **Weight Consumed:** ref_time=518,044,627 (36.2% of total), proof_size=41,092 +- **Total Weight:** ref_time=1,428,980,355, proof_size=49,623 +- **Weight Consumed:** ref_time=515,473,033 (36.1% of total), proof_size=41,092 | Opcode | Total Gas | Call Count | Avg Gas/Call | ref time | proof size | % of ref time | % of proof size | | ------------ | --------- | ---------- | ------------ | ----------- | ---------- | ------------- | --------------- | -| SSTORE | 56,212 | 1 | 56212.0 | 170,600,294 | 10,257 | 32.9% | 25.0% | +| SSTORE | 56,212 | 1 | 56212.0 | 170,600,294 | 10,257 | 33.1% | 25.0% | | SLOAD | 1,383 | 1 | 1383.0 | 69,164,719 | 10,289 | 13.4% | 25.0% | -| KECCAK256 | 305 | 1 | 305.0 | 15,219,717 | 0 | 2.9% | 0.0% | +| KECCAK256 | 304 | 1 | 304.0 | 15,219,717 | 0 | 3.0% | 0.0% | | LOG3 | 275 | 1 | 275.0 | 13,751,564 | 0 | 2.7% | 0.0% | | CALLVALUE | 7 | 1 | 7.0 | 319,000 | 0 | 0.1% | 0.0% | | JUMPI | 3 | 1 | 3.0 | 152,800 | 0 | 0.0% | 0.0% | @@ -353,57 +1445,54 @@ Generated on: 2026-01-13 | DUP1 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | | DUP2 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | | DUP3 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | -| DUP4 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | | DUP5 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | | DUP6 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | | EQ | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | | GT | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | | ISZERO | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | -| JUMPDEST | 1 | 1 | 1.0 | 15,280 | 0 | 0.0% | 0.0% | - +| LT | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| MLOAD | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | ### SimpleToken_pvm - mint -- **Total Gas Used:** 521,464 +- **Total Gas Used:** 521,421 - **Base Call Weight:** ref_time=913,507,322, proof_size=8,531 -- **Total Weight:** ref_time=1,497,388,823, proof_size=54,800 -- **Weight Consumed:** ref_time=583,881,501 (39.0% of total), proof_size=46,269 +- **Total Weight:** ref_time=1,495,226,765, proof_size=54,632 +- **Weight Consumed:** ref_time=581,719,443 (38.9% of total), proof_size=46,101 | Opcode | Total Gas | Call Count | Avg Gas/Call | ref time | proof size | % of ref time | % of proof size | | -------------------- | --------- | ---------- | ------------ | ----------- | ---------- | ------------- | --------------- | -| set_storage_or_clear | 56,212 | 1 | 56212.0 | 170,600,294 | 10,257 | 29.2% | 22.2% | -| get_storage_or_zero | 1,382 | 1 | 1382.0 | 69,114,511 | 10,257 | 11.8% | 22.2% | -| hash_keccak_256 | 304 | 1 | 304.0 | 15,219,717 | 0 | 2.6% | 0.0% | +| set_storage_or_clear | 56,212 | 1 | 56212.0 | 170,600,294 | 10,257 | 29.3% | 22.2% | +| get_storage_or_zero | 1,382 | 1 | 1382.0 | 69,114,511 | 10,257 | 11.9% | 22.2% | +| hash_keccak_256 | 305 | 1 | 305.0 | 15,219,717 | 0 | 2.6% | 0.0% | | deposit_event | 275 | 1 | 275.0 | 13,751,564 | 0 | 2.4% | 0.0% | | seal_return | 10 | 1 | 10.0 | 529,465 | 0 | 0.1% | 0.0% | -| call_data_load | 7 | 1 | 7.0 | 343,000 | 0 | 0.1% | 0.0% | -| call_data_size | 7 | 1 | 7.0 | 330,000 | 0 | 0.1% | 0.0% | -| value_transferred | 6 | 1 | 6.0 | 319,000 | 0 | 0.1% | 0.0% | - +| call_data_load | 6 | 1 | 6.0 | 343,000 | 0 | 0.1% | 0.0% | +| call_data_size | 6 | 1 | 6.0 | 330,000 | 0 | 0.1% | 0.0% | +| value_transferred | 7 | 1 | 7.0 | 319,000 | 0 | 0.1% | 0.0% | ### simple_token_ink - mint -- **Total Gas Used:** 474,068 +- **Total Gas Used:** 474,081 - **Base Call Weight:** ref_time=913,507,322, proof_size=8,531 -- **Total Weight:** ref_time=1,687,565,485, proof_size=56,631 -- **Weight Consumed:** ref_time=774,058,163 (45.9% of total), proof_size=48,100 +- **Total Weight:** ref_time=1,688,260,258, proof_size=56,874 +- **Weight Consumed:** ref_time=774,752,936 (45.9% of total), proof_size=48,343 | Opcode | Total Gas | Call Count | Avg Gas/Call | ref time | proof size | % of ref time | % of proof size | | ----------------- | --------- | ---------- | ------------ | ----------- | ---------- | ------------- | --------------- | -| set_storage | 3,413 | 1 | 3413.0 | 170,624,806 | 10,289 | 22.0% | 21.4% | -| get_storage | 1,382 | 1 | 1382.0 | 69,114,511 | 10,257 | 8.9% | 21.3% | +| set_storage | 3,413 | 1 | 3413.0 | 170,624,806 | 10,289 | 22.0% | 21.3% | +| get_storage | 1,382 | 1 | 1382.0 | 69,114,511 | 10,257 | 8.9% | 21.2% | | deposit_event | 275 | 1 | 275.0 | 13,751,564 | 0 | 1.8% | 0.0% | | call_data_copy | 47 | 1 | 47.0 | 2,347,968 | 0 | 0.3% | 0.0% | | seal_return | 11 | 1 | 11.0 | 529,465 | 0 | 0.1% | 0.0% | | value_transferred | 7 | 1 | 7.0 | 319,000 | 0 | 0.0% | 0.0% | - ### simple_token_no_alloc_rust - mint - **Total Gas Used:** 521,627 - **Base Call Weight:** ref_time=913,507,322, proof_size=8,531 -- **Total Weight:** ref_time=1,505,513,738, proof_size=53,929 -- **Weight Consumed:** ref_time=592,006,416 (39.3% of total), proof_size=45,398 +- **Total Weight:** ref_time=1,505,548,558, proof_size=53,919 +- **Weight Consumed:** ref_time=592,041,236 (39.3% of total), proof_size=45,388 | Opcode | Total Gas | Call Count | Avg Gas/Call | ref time | proof size | % of ref time | % of proof size | | --------------- | --------- | ---------- | ------------ | ----------- | ---------- | ------------- | --------------- | @@ -411,85 +1500,81 @@ Generated on: 2026-01-13 | get_storage | 1,382 | 1 | 1382.0 | 69,114,511 | 10,257 | 11.7% | 22.6% | | hash_keccak_256 | 305 | 1 | 305.0 | 15,219,717 | 0 | 2.6% | 0.0% | | deposit_event | 275 | 1 | 275.0 | 13,751,564 | 0 | 2.3% | 0.0% | -| call_data_copy | 10 | 1 | 10.0 | 504,260 | 0 | 0.1% | 0.0% | +| call_data_copy | 11 | 1 | 11.0 | 504,260 | 0 | 0.1% | 0.0% | | call_data_size | 7 | 1 | 7.0 | 330,000 | 0 | 0.1% | 0.0% | - ### SimpleToken_evm - transfer -- **Total Gas Used:** 415,141 +- **Total Gas Used:** 415,117 - **Base Call Weight:** ref_time=913,507,322, proof_size=8,531 -- **Total Weight:** ref_time=1,461,245,351, proof_size=49,655 -- **Weight Consumed:** ref_time=547,738,029 (37.5% of total), proof_size=41,124 +- **Total Weight:** ref_time=1,460,054,677, proof_size=49,655 +- **Weight Consumed:** ref_time=546,547,355 (37.4% of total), proof_size=41,124 | Opcode | Total Gas | Call Count | Avg Gas/Call | ref time | proof size | % of ref time | % of proof size | | ------------ | --------- | ---------- | ------------ | ----------- | ---------- | ------------- | --------------- | -| SSTORE | 3,412 | 1 | 3412.0 | 170,600,294 | 10,257 | 31.1% | 24.9% | -| SLOAD | 1,384 | 1 | 1384.0 | 69,164,719 | 10,289 | 12.6% | 25.0% | +| SSTORE | 3,412 | 1 | 3412.0 | 170,600,294 | 10,257 | 31.2% | 24.9% | +| SLOAD | 1,384 | 1 | 1384.0 | 69,164,719 | 10,289 | 12.7% | 25.0% | | KECCAK256 | 304 | 1 | 304.0 | 15,219,717 | 0 | 2.8% | 0.0% | | LOG3 | 275 | 1 | 275.0 | 13,751,564 | 0 | 2.5% | 0.0% | | CALLER | 8 | 1 | 8.0 | 403,000 | 0 | 0.1% | 0.0% | -| CALLVALUE | 7 | 1 | 7.0 | 319,000 | 0 | 0.1% | 0.0% | +| CALLVALUE | 6 | 1 | 6.0 | 319,000 | 0 | 0.1% | 0.0% | | JUMPI | 3 | 1 | 3.0 | 152,800 | 0 | 0.0% | 0.0% | | JUMP | 2 | 1 | 2.0 | 122,240 | 0 | 0.0% | 0.0% | -| ADD | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| AND | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | | CALLDATALOAD | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | -| CALLDATASIZE | 1 | 1 | 1.0 | 30,560 | 0 | 0.0% | 0.0% | | DUP1 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | | DUP2 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | -| DUP4 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | -| DUP5 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | -| DUP6 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | -| DUP7 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | -| DUP8 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP3 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | | EQ | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | | ISZERO | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | - +| LT | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| MLOAD | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| MSTORE | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| NOT | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| PUSH1 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | ### SimpleToken_pvm - transfer -- **Total Gas Used:** 417,001 +- **Total Gas Used:** 416,988 - **Base Call Weight:** ref_time=913,507,322, proof_size=8,531 -- **Total Weight:** ref_time=1,554,253,981, proof_size=54,961 -- **Weight Consumed:** ref_time=640,746,659 (41.2% of total), proof_size=46,430 +- **Total Weight:** ref_time=1,553,568,823, proof_size=54,793 +- **Weight Consumed:** ref_time=640,061,501 (41.2% of total), proof_size=46,262 | Opcode | Total Gas | Call Count | Avg Gas/Call | ref time | proof size | % of ref time | % of proof size | | -------------------- | --------- | ---------- | ------------ | ----------- | ---------- | ------------- | --------------- | -| set_storage_or_clear | 3,412 | 1 | 3412.0 | 170,600,294 | 10,257 | 26.6% | 22.1% | -| get_storage_or_zero | 1,382 | 1 | 1382.0 | 69,114,511 | 10,257 | 10.8% | 22.1% | +| set_storage_or_clear | 3,412 | 1 | 3412.0 | 170,600,294 | 10,257 | 26.7% | 22.2% | +| get_storage_or_zero | 1,383 | 1 | 1383.0 | 69,114,511 | 10,257 | 10.8% | 22.2% | | hash_keccak_256 | 304 | 1 | 304.0 | 15,219,717 | 0 | 2.4% | 0.0% | | deposit_event | 275 | 1 | 275.0 | 13,751,564 | 0 | 2.1% | 0.0% | -| seal_return | 11 | 1 | 11.0 | 529,465 | 0 | 0.1% | 0.0% | +| seal_return | 10 | 1 | 10.0 | 529,465 | 0 | 0.1% | 0.0% | | caller | 8 | 1 | 8.0 | 403,000 | 0 | 0.1% | 0.0% | -| call_data_load | 7 | 1 | 7.0 | 343,000 | 0 | 0.1% | 0.0% | -| call_data_size | 7 | 1 | 7.0 | 330,000 | 0 | 0.1% | 0.0% | -| value_transferred | 6 | 1 | 6.0 | 319,000 | 0 | 0.0% | 0.0% | - +| call_data_load | 6 | 1 | 6.0 | 343,000 | 0 | 0.1% | 0.0% | +| call_data_size | 6 | 1 | 6.0 | 330,000 | 0 | 0.1% | 0.0% | +| value_transferred | 7 | 1 | 7.0 | 319,000 | 0 | 0.0% | 0.0% | ### simple_token_ink - transfer -- **Total Gas Used:** 480,566 +- **Total Gas Used:** 480,597 - **Base Call Weight:** ref_time=913,507,322, proof_size=8,531 -- **Total Weight:** ref_time=2,012,500,642, proof_size=77,209 -- **Weight Consumed:** ref_time=1,098,993,320 (54.6% of total), proof_size=68,678 +- **Total Weight:** ref_time=2,014,032,325, proof_size=77,452 +- **Weight Consumed:** ref_time=1,100,525,003 (54.6% of total), proof_size=68,921 | Opcode | Total Gas | Call Count | Avg Gas/Call | ref time | proof size | % of ref time | % of proof size | | ----------------- | --------- | ---------- | ------------ | ----------- | ---------- | ------------- | --------------- | -| set_storage | 3,412 | 1 | 3412.0 | 170,624,806 | 10,289 | 15.5% | 15.0% | +| set_storage | 3,413 | 1 | 3413.0 | 170,624,806 | 10,289 | 15.5% | 14.9% | | get_storage | 1,382 | 1 | 1382.0 | 69,114,511 | 10,257 | 6.3% | 14.9% | -| deposit_event | 275 | 1 | 275.0 | 13,751,564 | 0 | 1.3% | 0.0% | +| deposit_event | 275 | 1 | 275.0 | 13,751,564 | 0 | 1.2% | 0.0% | | call_data_copy | 47 | 1 | 47.0 | 2,347,968 | 0 | 0.2% | 0.0% | -| seal_return | 11 | 1 | 11.0 | 529,465 | 0 | 0.0% | 0.0% | +| seal_return | 10 | 1 | 10.0 | 529,465 | 0 | 0.0% | 0.0% | | caller | 8 | 1 | 8.0 | 403,000 | 0 | 0.0% | 0.0% | | value_transferred | 7 | 1 | 7.0 | 319,000 | 0 | 0.0% | 0.0% | - ### simple_token_no_alloc_rust - transfer -- **Total Gas Used:** 469,957 +- **Total Gas Used:** 469,958 - **Base Call Weight:** ref_time=913,507,322, proof_size=8,531 -- **Total Weight:** ref_time=1,562,060,164, proof_size=53,961 -- **Weight Consumed:** ref_time=648,552,842 (41.5% of total), proof_size=45,430 +- **Total Weight:** ref_time=1,562,094,984, proof_size=53,951 +- **Weight Consumed:** ref_time=648,587,662 (41.5% of total), proof_size=45,420 | Opcode | Total Gas | Call Count | Avg Gas/Call | ref time | proof size | % of ref time | % of proof size | | --------------- | --------- | ---------- | ------------ | ----------- | ---------- | ------------- | --------------- | @@ -497,8 +1582,443 @@ Generated on: 2026-01-13 | get_storage | 1,382 | 1 | 1382.0 | 69,114,511 | 10,257 | 10.7% | 22.6% | | hash_keccak_256 | 304 | 1 | 304.0 | 15,219,717 | 0 | 2.3% | 0.0% | | deposit_event | 275 | 1 | 275.0 | 13,751,564 | 0 | 2.1% | 0.0% | -| call_data_copy | 10 | 1 | 10.0 | 504,260 | 0 | 0.1% | 0.0% | +| call_data_copy | 11 | 1 | 11.0 | 504,260 | 0 | 0.1% | 0.0% | | caller | 8 | 1 | 8.0 | 403,000 | 0 | 0.1% | 0.0% | | call_data_size | 7 | 1 | 7.0 | 330,000 | 0 | 0.1% | 0.0% | +### TetherToken_evm - approve +- **Total Gas Used:** 464,863 +- **Base Call Weight:** ref_time=913,507,322, proof_size=8,531 +- **Total Weight:** ref_time=1,307,318,598, proof_size=39,366 +- **Weight Consumed:** ref_time=393,811,276 (30.1% of total), proof_size=30,835 + +| Opcode | Total Gas | Call Count | Avg Gas/Call | ref time | proof size | % of ref time | % of proof size | +| ------------ | --------- | ---------- | ------------ | ----------- | ---------- | ------------- | --------------- | +| SSTORE | 56,212 | 1 | 56212.0 | 170,600,294 | 10,257 | 43.3% | 33.3% | +| SLOAD | 1,383 | 1 | 1383.0 | 69,164,719 | 10,289 | 17.6% | 33.4% | +| KECCAK256 | 304 | 1 | 304.0 | 15,219,717 | 0 | 3.9% | 0.0% | +| LOG3 | 275 | 1 | 275.0 | 13,751,564 | 0 | 3.5% | 0.0% | +| CALLER | 8 | 1 | 8.0 | 403,000 | 0 | 0.1% | 0.0% | +| CALLVALUE | 7 | 1 | 7.0 | 319,000 | 0 | 0.1% | 0.0% | +| JUMPI | 3 | 1 | 3.0 | 152,800 | 0 | 0.0% | 0.0% | +| JUMP | 3 | 1 | 3.0 | 122,240 | 0 | 0.0% | 0.0% | +| ADD | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| AND | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| CALLDATALOAD | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP1 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP2 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP3 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| EQ | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| ISZERO | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| LT | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| MLOAD | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| MSTORE | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| NOT | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | + +### TetherToken_pvm - approve + +- **Total Gas Used:** 468,259 +- **Base Call Weight:** ref_time=913,507,322, proof_size=8,531 +- **Total Weight:** ref_time=1,477,151,338, proof_size=93,921 +- **Weight Consumed:** ref_time=563,644,016 (38.2% of total), proof_size=85,390 + +| Opcode | Total Gas | Call Count | Avg Gas/Call | ref time | proof size | % of ref time | % of proof size | +| -------------------- | --------- | ---------- | ------------ | ----------- | ---------- | ------------- | --------------- | +| set_storage_or_clear | 56,212 | 1 | 56212.0 | 170,600,294 | 10,257 | 30.3% | 12.0% | +| get_storage_or_zero | 1,382 | 1 | 1382.0 | 69,114,511 | 10,257 | 12.3% | 12.0% | +| hash_keccak_256 | 304 | 1 | 304.0 | 15,219,717 | 0 | 2.7% | 0.0% | +| deposit_event | 275 | 1 | 275.0 | 13,751,564 | 0 | 2.4% | 0.0% | +| seal_return | 11 | 1 | 11.0 | 529,465 | 0 | 0.1% | 0.0% | +| caller | 8 | 1 | 8.0 | 403,000 | 0 | 0.1% | 0.0% | +| call_data_load | 7 | 1 | 7.0 | 343,000 | 0 | 0.1% | 0.0% | +| call_data_size | 6 | 1 | 6.0 | 330,000 | 0 | 0.1% | 0.0% | +| value_transferred | 6 | 1 | 6.0 | 319,000 | 0 | 0.1% | 0.0% | + +### TetherToken_evm - deploy + +- **Total Gas Used:** 28,192,460 +- **Base Call Weight:** ref_time=1,610,425,073, proof_size=6,934 +- **Total Weight:** ref_time=3,536,168,883, proof_size=150,692 +- **Weight Consumed:** ref_time=1,925,743,810 (54.5% of total), proof_size=143,758 + +| Opcode | Total Gas | Call Count | Avg Gas/Call | ref time | proof size | % of ref time | % of proof size | +| --------- | --------- | ---------- | ------------ | ----------- | ---------- | ------------- | --------------- | +| SSTORE | 3,412 | 1 | 3412.0 | 170,582,310 | 10,257 | 8.9% | 7.1% | +| SLOAD | 1,383 | 1 | 1383.0 | 69,164,719 | 10,289 | 3.6% | 7.2% | +| KECCAK256 | 304 | 1 | 304.0 | 15,219,717 | 0 | 0.8% | 0.0% | +| CODECOPY | 28 | 1 | 28.0 | 1,425,236 | 0 | 0.1% | 0.0% | +| CALLER | 8 | 1 | 8.0 | 403,000 | 0 | 0.0% | 0.0% | +| CALLVALUE | 6 | 1 | 6.0 | 319,000 | 0 | 0.0% | 0.0% | +| JUMPI | 3 | 1 | 3.0 | 152,800 | 0 | 0.0% | 0.0% | +| JUMP | 3 | 1 | 3.0 | 122,240 | 0 | 0.0% | 0.0% | +| MCOPY | 1 | 1 | 1.0 | 91,680 | 0 | 0.0% | 0.0% | +| AND | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP1 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP2 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP3 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP4 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP5 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP6 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP7 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| EQ | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| GT | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| ISZERO | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | + +### TetherToken_pvm - deploy + +- **Total Gas Used:** 139,224,508 +- **Base Call Weight:** ref_time=2,382,032,403, proof_size=6,934 +- **Total Weight:** ref_time=4,547,576,316, proof_size=150,951 +- **Weight Consumed:** ref_time=2,165,543,913 (47.6% of total), proof_size=144,017 + +| Opcode | Total Gas | Call Count | Avg Gas/Call | ref time | proof size | % of ref time | % of proof size | +| -------------------- | --------- | ---------- | ------------ | ----------- | ---------- | ------------- | --------------- | +| set_storage_or_clear | 3,456 | 1 | 3456.0 | 172,779,550 | 10,386 | 8.0% | 7.2% | +| get_storage_or_zero | 1,382 | 1 | 1382.0 | 69,114,511 | 10,257 | 3.2% | 7.1% | +| hash_keccak_256 | 304 | 1 | 304.0 | 15,219,717 | 0 | 0.7% | 0.0% | +| seal_return | 10 | 1 | 10.0 | 529,465 | 0 | 0.0% | 0.0% | +| call_data_copy | 10 | 1 | 10.0 | 496,576 | 0 | 0.0% | 0.0% | +| caller | 8 | 1 | 8.0 | 403,000 | 0 | 0.0% | 0.0% | +| call_data_size | 7 | 1 | 7.0 | 330,000 | 0 | 0.0% | 0.0% | +| value_transferred | 7 | 1 | 7.0 | 319,000 | 0 | 0.0% | 0.0% | + +### TetherToken_evm - transfer + +- **Total Gas Used:** 422,929 +- **Base Call Weight:** ref_time=913,507,322, proof_size=8,531 +- **Total Weight:** ref_time=1,850,629,748, proof_size=101,132 +- **Weight Consumed:** ref_time=937,122,426 (50.6% of total), proof_size=92,601 + +| Opcode | Total Gas | Call Count | Avg Gas/Call | ref time | proof size | % of ref time | % of proof size | +| ------------ | --------- | ---------- | ------------ | ----------- | ---------- | ------------- | --------------- | +| SSTORE | 3,412 | 1 | 3412.0 | 170,624,806 | 10,289 | 18.2% | 11.1% | +| SLOAD | 1,383 | 1 | 1383.0 | 69,164,719 | 10,289 | 7.4% | 11.1% | +| KECCAK256 | 305 | 1 | 305.0 | 15,219,717 | 0 | 1.6% | 0.0% | +| LOG3 | 275 | 1 | 275.0 | 13,751,564 | 0 | 1.5% | 0.0% | +| CALLER | 8 | 1 | 8.0 | 403,000 | 0 | 0.0% | 0.0% | +| CALLVALUE | 6 | 1 | 6.0 | 319,000 | 0 | 0.0% | 0.0% | +| JUMPI | 3 | 1 | 3.0 | 152,800 | 0 | 0.0% | 0.0% | +| JUMP | 2 | 1 | 2.0 | 122,240 | 0 | 0.0% | 0.0% | +| DIV | 2 | 1 | 2.0 | 76,400 | 0 | 0.0% | 0.0% | +| MUL | 2 | 1 | 2.0 | 76,400 | 0 | 0.0% | 0.0% | +| ADD | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| AND | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| CALLDATALOAD | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP1 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP2 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP3 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP4 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP5 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| EQ | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| GT | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | + +### TetherToken_pvm - transfer + +- **Total Gas Used:** 427,024 +- **Base Call Weight:** ref_time=913,507,322, proof_size=8,531 +- **Total Weight:** ref_time=2,055,388,174, proof_size=155,623 +- **Weight Consumed:** ref_time=1,141,880,852 (55.6% of total), proof_size=147,092 + +| Opcode | Total Gas | Call Count | Avg Gas/Call | ref time | proof size | % of ref time | % of proof size | +| -------------------- | --------- | ---------- | ------------ | ----------- | ---------- | ------------- | --------------- | +| set_storage_or_clear | 3,413 | 1 | 3413.0 | 170,624,806 | 10,289 | 14.9% | 7.0% | +| get_storage_or_zero | 1,384 | 1 | 1384.0 | 69,164,719 | 10,289 | 6.1% | 7.0% | +| hash_keccak_256 | 304 | 1 | 304.0 | 15,219,717 | 0 | 1.3% | 0.0% | +| deposit_event | 275 | 1 | 275.0 | 13,751,564 | 0 | 1.2% | 0.0% | +| seal_return | 10 | 1 | 10.0 | 529,465 | 0 | 0.0% | 0.0% | +| caller | 8 | 1 | 8.0 | 403,000 | 0 | 0.0% | 0.0% | +| call_data_load | 6 | 1 | 6.0 | 343,000 | 0 | 0.0% | 0.0% | +| call_data_size | 6 | 1 | 6.0 | 330,000 | 0 | 0.0% | 0.0% | +| value_transferred | 6 | 1 | 6.0 | 319,000 | 0 | 0.0% | 0.0% | + +### TetherToken_evm - transferFrom + +- **Total Gas Used:** 492,935 +- **Base Call Weight:** ref_time=913,901,786, proof_size=8,627 +- **Total Weight:** ref_time=2,150,947,725, proof_size=121,806 +- **Weight Consumed:** ref_time=1,237,045,939 (57.5% of total), proof_size=113,179 + +| Opcode | Total Gas | Call Count | Avg Gas/Call | ref time | proof size | % of ref time | % of proof size | +| ------------ | --------- | ---------- | ------------ | ----------- | ---------- | ------------- | --------------- | +| SSTORE | 3,413 | 1 | 3413.0 | 170,624,806 | 10,289 | 13.8% | 9.1% | +| SLOAD | 1,383 | 1 | 1383.0 | 69,164,719 | 10,289 | 5.6% | 9.1% | +| KECCAK256 | 304 | 1 | 304.0 | 15,219,717 | 0 | 1.2% | 0.0% | +| LOG3 | 275 | 1 | 275.0 | 13,751,564 | 0 | 1.1% | 0.0% | +| CALLER | 8 | 1 | 8.0 | 403,000 | 0 | 0.0% | 0.0% | +| CALLVALUE | 6 | 1 | 6.0 | 319,000 | 0 | 0.0% | 0.0% | +| JUMPI | 3 | 1 | 3.0 | 152,800 | 0 | 0.0% | 0.0% | +| JUMP | 2 | 1 | 2.0 | 122,240 | 0 | 0.0% | 0.0% | +| DIV | 2 | 1 | 2.0 | 76,400 | 0 | 0.0% | 0.0% | +| MUL | 2 | 1 | 2.0 | 76,400 | 0 | 0.0% | 0.0% | +| ADD | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| AND | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| CALLDATALOAD | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP2 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP3 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP4 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP5 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP6 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP7 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP8 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | + +### TetherToken_pvm - transferFrom + +- **Total Gas Used:** 498,231 +- **Base Call Weight:** ref_time=913,901,786, proof_size=8,627 +- **Total Weight:** ref_time=2,415,750,101, proof_size=176,297 +- **Weight Consumed:** ref_time=1,501,848,315 (62.2% of total), proof_size=167,670 + +| Opcode | Total Gas | Call Count | Avg Gas/Call | ref time | proof size | % of ref time | % of proof size | +| -------------------- | --------- | ---------- | ------------ | ----------- | ---------- | ------------- | --------------- | +| set_storage_or_clear | 3,413 | 1 | 3413.0 | 170,624,806 | 10,289 | 11.4% | 6.1% | +| get_storage_or_zero | 1,384 | 1 | 1384.0 | 69,164,719 | 10,289 | 4.6% | 6.1% | +| hash_keccak_256 | 304 | 1 | 304.0 | 15,219,717 | 0 | 1.0% | 0.0% | +| deposit_event | 275 | 1 | 275.0 | 13,751,564 | 0 | 0.9% | 0.0% | +| seal_return | 11 | 1 | 11.0 | 529,465 | 0 | 0.0% | 0.0% | +| caller | 8 | 1 | 8.0 | 403,000 | 0 | 0.0% | 0.0% | +| call_data_load | 7 | 1 | 7.0 | 343,000 | 0 | 0.0% | 0.0% | +| call_data_size | 7 | 1 | 7.0 | 330,000 | 0 | 0.0% | 0.0% | +| value_transferred | 6 | 1 | 6.0 | 319,000 | 0 | 0.0% | 0.0% | + +### WETH9_evm - deploy + +- **Total Gas Used:** 19,522,786 +- **Base Call Weight:** ref_time=1,549,305,061, proof_size=6,934 +- **Total Weight:** ref_time=2,275,464,063, proof_size=68,572 +- **Weight Consumed:** ref_time=726,159,002 (31.9% of total), proof_size=61,638 + +| Opcode | Total Gas | Call Count | Avg Gas/Call | ref time | proof size | % of ref time | % of proof size | +| --------- | --------- | ---------- | ------------ | ----------- | ---------- | ------------- | --------------- | +| SSTORE | 56,212 | 1 | 56212.0 | 170,600,294 | 10,257 | 23.5% | 16.6% | +| SLOAD | 1,383 | 1 | 1383.0 | 69,164,719 | 10,289 | 9.5% | 16.7% | +| CODECOPY | 17 | 1 | 17.0 | 830,243 | 0 | 0.1% | 0.0% | +| CALLVALUE | 6 | 1 | 6.0 | 319,000 | 0 | 0.0% | 0.0% | +| JUMPI | 4 | 1 | 4.0 | 152,800 | 0 | 0.0% | 0.0% | +| JUMP | 3 | 1 | 3.0 | 122,240 | 0 | 0.0% | 0.0% | +| AND | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP1 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP2 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP3 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP4 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| EQ | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| GT | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| ISZERO | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| LT | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| MLOAD | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| MSTORE | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| NOT | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| OR | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| PUSH1 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | + +### WETH9_pvm - deploy + +- **Total Gas Used:** 53,938,166 +- **Base Call Weight:** ref_time=1,789,607,045, proof_size=6,934 +- **Total Weight:** ref_time=2,549,508,821, proof_size=68,476 +- **Weight Consumed:** ref_time=759,901,776 (29.8% of total), proof_size=61,542 + +| Opcode | Total Gas | Call Count | Avg Gas/Call | ref time | proof size | % of ref time | % of proof size | +| -------------------- | --------- | ---------- | ------------ | ----------- | ---------- | ------------- | --------------- | +| set_storage_or_clear | 56,212 | 1 | 56212.0 | 170,600,294 | 10,257 | 22.5% | 16.7% | +| get_storage_or_zero | 1,383 | 1 | 1383.0 | 69,114,511 | 10,257 | 9.1% | 16.7% | +| seal_return | 11 | 1 | 11.0 | 529,465 | 0 | 0.1% | 0.0% | +| call_data_copy | 10 | 1 | 10.0 | 496,576 | 0 | 0.1% | 0.0% | +| call_data_size | 7 | 1 | 7.0 | 330,000 | 0 | 0.0% | 0.0% | +| value_transferred | 6 | 1 | 6.0 | 319,000 | 0 | 0.0% | 0.0% | + +### WETH9_evm - deposit + +- **Total Gas Used:** 348,490 +- **Base Call Weight:** ref_time=912,804,683, proof_size=8,360 +- **Total Weight:** ref_time=1,188,685,343, proof_size=28,906 +- **Weight Consumed:** ref_time=275,880,660 (23.2% of total), proof_size=20,546 + +| Opcode | Total Gas | Call Count | Avg Gas/Call | ref time | proof size | % of ref time | % of proof size | +| ------------ | --------- | ---------- | ------------ | ----------- | ---------- | ------------- | --------------- | +| SSTORE | 56,212 | 1 | 56212.0 | 170,600,294 | 10,257 | 61.8% | 49.9% | +| SLOAD | 1,383 | 1 | 1383.0 | 69,164,719 | 10,289 | 25.1% | 50.1% | +| KECCAK256 | 304 | 1 | 304.0 | 15,219,717 | 0 | 5.5% | 0.0% | +| LOG2 | 275 | 1 | 275.0 | 13,751,564 | 0 | 5.0% | 0.0% | +| CALLER | 8 | 1 | 8.0 | 403,000 | 0 | 0.1% | 0.0% | +| CALLVALUE | 6 | 1 | 6.0 | 319,000 | 0 | 0.1% | 0.0% | +| JUMPI | 4 | 1 | 4.0 | 152,800 | 0 | 0.1% | 0.0% | +| JUMP | 2 | 1 | 2.0 | 122,240 | 0 | 0.0% | 0.0% | +| ADD | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| CALLDATALOAD | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP1 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP2 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| EQ | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| LT | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| MLOAD | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| MSTORE | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| NOT | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| PUSH1 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| PUSH2 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| PUSH32 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | + +### WETH9_pvm - deposit + +- **Total Gas Used:** 349,907 +- **Base Call Weight:** ref_time=912,804,683, proof_size=8,360 +- **Total Weight:** ref_time=1,259,534,400, proof_size=46,070 +- **Weight Consumed:** ref_time=346,729,717 (27.5% of total), proof_size=37,710 + +| Opcode | Total Gas | Call Count | Avg Gas/Call | ref time | proof size | % of ref time | % of proof size | +| -------------------- | --------- | ---------- | ------------ | ----------- | ---------- | ------------- | --------------- | +| set_storage_or_clear | 56,212 | 1 | 56212.0 | 170,600,294 | 10,257 | 49.2% | 27.2% | +| get_storage_or_zero | 1,382 | 1 | 1382.0 | 69,114,511 | 10,257 | 19.9% | 27.2% | +| hash_keccak_256 | 304 | 1 | 304.0 | 15,219,717 | 0 | 4.4% | 0.0% | +| deposit_event | 275 | 1 | 275.0 | 13,751,564 | 0 | 4.0% | 0.0% | +| seal_return | 10 | 1 | 10.0 | 529,465 | 0 | 0.2% | 0.0% | +| caller | 8 | 1 | 8.0 | 403,000 | 0 | 0.1% | 0.0% | +| call_data_load | 7 | 1 | 7.0 | 343,000 | 0 | 0.1% | 0.0% | +| call_data_size | 7 | 1 | 7.0 | 330,000 | 0 | 0.1% | 0.0% | +| value_transferred | 6 | 1 | 6.0 | 319,000 | 0 | 0.1% | 0.0% | + +### WETH9_evm - transfer + +- **Total Gas Used:** 416,568 +- **Base Call Weight:** ref_time=913,507,322, proof_size=8,531 +- **Total Weight:** ref_time=1,532,612,772, proof_size=59,976 +- **Weight Consumed:** ref_time=619,105,450 (40.4% of total), proof_size=51,445 + +| Opcode | Total Gas | Call Count | Avg Gas/Call | ref time | proof size | % of ref time | % of proof size | +| --------- | --------- | ---------- | ------------ | ----------- | ---------- | ------------- | --------------- | +| SSTORE | 3,412 | 1 | 3412.0 | 170,624,806 | 10,289 | 27.6% | 20.0% | +| SLOAD | 1,383 | 1 | 1383.0 | 69,164,719 | 10,289 | 11.2% | 20.0% | +| KECCAK256 | 304 | 1 | 304.0 | 15,219,717 | 0 | 2.5% | 0.0% | +| LOG3 | 275 | 1 | 275.0 | 13,751,564 | 0 | 2.2% | 0.0% | +| CALLER | 8 | 1 | 8.0 | 403,000 | 0 | 0.1% | 0.0% | +| CALLVALUE | 6 | 1 | 6.0 | 319,000 | 0 | 0.1% | 0.0% | +| JUMPI | 3 | 1 | 3.0 | 152,800 | 0 | 0.0% | 0.0% | +| JUMP | 2 | 1 | 2.0 | 122,240 | 0 | 0.0% | 0.0% | +| ADD | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| AND | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP1 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP2 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP3 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP4 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP5 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP6 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| EQ | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| ISZERO | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| LT | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| MLOAD | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | + +### WETH9_pvm - transfer + +- **Total Gas Used:** 419,024 +- **Base Call Weight:** ref_time=913,507,322, proof_size=8,531 +- **Total Weight:** ref_time=1,655,399,731, proof_size=77,172 +- **Weight Consumed:** ref_time=741,892,409 (44.8% of total), proof_size=68,641 + +| Opcode | Total Gas | Call Count | Avg Gas/Call | ref time | proof size | % of ref time | % of proof size | +| -------------------- | --------- | ---------- | ------------ | ----------- | ---------- | ------------- | --------------- | +| set_storage_or_clear | 3,413 | 1 | 3413.0 | 170,624,806 | 10,289 | 23.0% | 15.0% | +| get_storage_or_zero | 1,384 | 1 | 1384.0 | 69,164,719 | 10,289 | 9.3% | 15.0% | +| hash_keccak_256 | 304 | 1 | 304.0 | 15,219,717 | 0 | 2.1% | 0.0% | +| deposit_event | 275 | 1 | 275.0 | 13,751,564 | 0 | 1.9% | 0.0% | +| seal_return | 11 | 1 | 11.0 | 535,865 | 0 | 0.1% | 0.0% | +| caller | 8 | 1 | 8.0 | 403,000 | 0 | 0.1% | 0.0% | +| call_data_load | 7 | 1 | 7.0 | 343,000 | 0 | 0.0% | 0.0% | +| call_data_size | 7 | 1 | 7.0 | 330,000 | 0 | 0.0% | 0.0% | +| value_transferred | 6 | 1 | 6.0 | 319,000 | 0 | 0.0% | 0.0% | + +### WETH9_evm - withdraw + +- **Total Gas Used:** 354,379 +- **Base Call Weight:** ref_time=913,100,531, proof_size=8,432 +- **Total Weight:** ref_time=1,723,148,219, proof_size=44,893 +- **Weight Consumed:** ref_time=810,047,688 (47.0% of total), proof_size=36,461 + +| Opcode | Total Gas | Call Count | Avg Gas/Call | ref time | proof size | % of ref time | % of proof size | +| ------------ | --------- | ---------- | ------------ | ----------- | ---------- | ------------- | --------------- | +| CALL | 8,988 | 1 | 8988.0 | 449,408,600 | 5,594 | 55.5% | 15.3% | +| SSTORE | 3,412 | 1 | 3412.0 | 170,624,806 | 10,289 | 21.1% | 28.2% | +| SLOAD | 1,383 | 1 | 1383.0 | 69,164,719 | 10,289 | 8.5% | 28.2% | +| KECCAK256 | 304 | 1 | 304.0 | 15,219,717 | 0 | 1.9% | 0.0% | +| LOG2 | 275 | 1 | 275.0 | 13,751,564 | 0 | 1.7% | 0.0% | +| CALLER | 8 | 1 | 8.0 | 403,000 | 0 | 0.0% | 0.0% | +| CALLVALUE | 6 | 1 | 6.0 | 319,000 | 0 | 0.0% | 0.0% | +| JUMPI | 3 | 1 | 3.0 | 152,800 | 0 | 0.0% | 0.0% | +| ADD | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| CALLDATALOAD | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP2 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| EQ | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| ISZERO | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| LT | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| MLOAD | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| MSTORE | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| NOT | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| PUSH1 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| PUSH2 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| PUSH32 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | + +### WETH9_pvm - withdraw + +- **Total Gas Used:** 356,206 +- **Base Call Weight:** ref_time=913,100,531, proof_size=8,432 +- **Total Weight:** ref_time=1,814,506,569, proof_size=62,089 +- **Weight Consumed:** ref_time=901,406,038 (49.7% of total), proof_size=53,657 + +| Opcode | Total Gas | Call Count | Avg Gas/Call | ref time | proof size | % of ref time | % of proof size | +| -------------------- | --------- | ---------- | ------------ | ----------- | ---------- | ------------- | --------------- | +| call_evm | 8,999 | 1 | 8999.0 | 449,944,465 | 5,594 | 49.9% | 10.4% | +| set_storage_or_clear | 3,412 | 1 | 3412.0 | 170,624,806 | 10,289 | 18.9% | 19.2% | +| get_storage_or_zero | 1,383 | 1 | 1383.0 | 69,164,719 | 10,289 | 7.7% | 19.2% | +| hash_keccak_256 | 305 | 1 | 305.0 | 15,219,717 | 0 | 1.7% | 0.0% | +| deposit_event | 275 | 1 | 275.0 | 13,751,564 | 0 | 1.5% | 0.0% | +| seal_return | 11 | 1 | 11.0 | 529,465 | 0 | 0.1% | 0.0% | +| caller | 8 | 1 | 8.0 | 403,000 | 0 | 0.0% | 0.0% | +| call_data_load | 7 | 1 | 7.0 | 343,000 | 0 | 0.0% | 0.0% | +| call_data_size | 6 | 1 | 6.0 | 330,000 | 0 | 0.0% | 0.0% | +| value_transferred | 7 | 1 | 7.0 | 319,000 | 0 | 0.0% | 0.0% | + +### XENCrypto_evm - deploy + +- **Total Gas Used:** 38,554,123 +- **Base Call Weight:** ref_time=1,683,462,063, proof_size=6,934 +- **Total Weight:** ref_time=2,350,323,858, proof_size=58,283 +- **Weight Consumed:** ref_time=666,861,795 (28.4% of total), proof_size=51,349 + +| Opcode | Total Gas | Call Count | Avg Gas/Call | ref time | proof size | % of ref time | % of proof size | +| --------- | --------- | ---------- | ------------ | ----------- | ---------- | ------------- | --------------- | +| SSTORE | 56,212 | 1 | 56212.0 | 170,600,294 | 10,257 | 25.6% | 20.0% | +| SLOAD | 1,384 | 1 | 1384.0 | 69,164,719 | 10,289 | 10.4% | 20.0% | +| CODECOPY | 49 | 1 | 49.0 | 2,450,995 | 0 | 0.4% | 0.0% | +| TIMESTAMP | 7 | 1 | 7.0 | 347,000 | 0 | 0.1% | 0.0% | +| CALLVALUE | 6 | 1 | 6.0 | 319,000 | 0 | 0.0% | 0.0% | +| JUMPI | 4 | 1 | 4.0 | 152,800 | 0 | 0.0% | 0.0% | +| JUMP | 2 | 1 | 2.0 | 122,240 | 0 | 0.0% | 0.0% | +| ADD | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| AND | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP1 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP2 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP3 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| DUP4 | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| EQ | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| ISZERO | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| LT | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| MLOAD | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| MSTORE | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| NOT | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | +| OR | 1 | 1 | 1.0 | 45,840 | 0 | 0.0% | 0.0% | + +### XENCrypto_pvm - deploy + +- **Total Gas Used:** 273,702,905 +- **Base Call Weight:** ref_time=3,300,230,397, proof_size=6,934 +- **Total Weight:** ref_time=4,190,424,438, proof_size=58,219 +- **Weight Consumed:** ref_time=890,194,041 (21.2% of total), proof_size=51,285 + +| Opcode | Total Gas | Call Count | Avg Gas/Call | ref time | proof size | % of ref time | % of proof size | +| -------------------- | --------- | ---------- | ------------ | ----------- | ---------- | ------------- | --------------- | +| set_storage_or_clear | 56,212 | 1 | 56212.0 | 170,600,294 | 10,257 | 19.2% | 20.0% | +| set_immutable_data | 2,049 | 1 | 2049.0 | 102,480,992 | 0 | 11.5% | 0.0% | +| get_storage_or_zero | 1,382 | 1 | 1382.0 | 69,114,511 | 10,257 | 7.8% | 20.0% | +| seal_return | 10 | 1 | 10.0 | 529,465 | 0 | 0.1% | 0.0% | +| call_data_copy | 10 | 1 | 10.0 | 496,576 | 0 | 0.1% | 0.0% | +| now | 7 | 1 | 7.0 | 347,000 | 0 | 0.0% | 0.0% | +| call_data_size | 7 | 1 | 7.0 | 330,000 | 0 | 0.0% | 0.0% | +| value_transferred | 7 | 1 | 7.0 | 319,000 | 0 | 0.0% | 0.0% | diff --git a/codegen/libs/.gitkeep b/codegen/libs/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/contracts/original/tether.sol b/contracts/original/tether.sol new file mode 100644 index 0000000..16a4876 --- /dev/null +++ b/contracts/original/tether.sol @@ -0,0 +1,451 @@ +/** + *Submitted for verification at Etherscan.io on 2017-11-28 +*/ + +pragma solidity ^0.4.17; + +/** + * @title SafeMath + * @dev Math operations with safety checks that throw on error + */ +library SafeMath { + function mul(uint256 a, uint256 b) internal pure returns (uint256) { + if (a == 0) { + return 0; + } + uint256 c = a * b; + assert(c / a == b); + return c; + } + + function div(uint256 a, uint256 b) internal pure returns (uint256) { + // assert(b > 0); // Solidity automatically throws when dividing by 0 + uint256 c = a / b; + // assert(a == b * c + a % b); // There is no case in which this doesn't hold + return c; + } + + function sub(uint256 a, uint256 b) internal pure returns (uint256) { + assert(b <= a); + return a - b; + } + + function add(uint256 a, uint256 b) internal pure returns (uint256) { + uint256 c = a + b; + assert(c >= a); + return c; + } +} + +/** + * @title Ownable + * @dev The Ownable contract has an owner address, and provides basic authorization control + * functions, this simplifies the implementation of "user permissions". + */ +contract Ownable { + address public owner; + + /** + * @dev The Ownable constructor sets the original `owner` of the contract to the sender + * account. + */ + function Ownable() public { + owner = msg.sender; + } + + /** + * @dev Throws if called by any account other than the owner. + */ + modifier onlyOwner() { + require(msg.sender == owner); + _; + } + + /** + * @dev Allows the current owner to transfer control of the contract to a newOwner. + * @param newOwner The address to transfer ownership to. + */ + function transferOwnership(address newOwner) public onlyOwner { + if (newOwner != address(0)) { + owner = newOwner; + } + } + +} + +/** + * @title ERC20Basic + * @dev Simpler version of ERC20 interface + * @dev see https://github.com/ethereum/EIPs/issues/20 + */ +contract ERC20Basic { + uint public _totalSupply; + function totalSupply() public constant returns (uint); + function balanceOf(address who) public constant returns (uint); + function transfer(address to, uint value) public; + event Transfer(address indexed from, address indexed to, uint value); +} + +/** + * @title ERC20 interface + * @dev see https://github.com/ethereum/EIPs/issues/20 + */ +contract ERC20 is ERC20Basic { + function allowance(address owner, address spender) public constant returns (uint); + function transferFrom(address from, address to, uint value) public; + function approve(address spender, uint value) public; + event Approval(address indexed owner, address indexed spender, uint value); +} + +/** + * @title Basic token + * @dev Basic version of StandardToken, with no allowances. + */ +contract BasicToken is Ownable, ERC20Basic { + using SafeMath for uint; + + mapping(address => uint) public balances; + + // additional variables for use if transaction fees ever became necessary + uint public basisPointsRate = 0; + uint public maximumFee = 0; + + /** + * @dev Fix for the ERC20 short address attack. + */ + modifier onlyPayloadSize(uint size) { + require(!(msg.data.length < size + 4)); + _; + } + + /** + * @dev transfer token for a specified address + * @param _to The address to transfer to. + * @param _value The amount to be transferred. + */ + function transfer(address _to, uint _value) public onlyPayloadSize(2 * 32) { + uint fee = (_value.mul(basisPointsRate)).div(10000); + if (fee > maximumFee) { + fee = maximumFee; + } + uint sendAmount = _value.sub(fee); + balances[msg.sender] = balances[msg.sender].sub(_value); + balances[_to] = balances[_to].add(sendAmount); + if (fee > 0) { + balances[owner] = balances[owner].add(fee); + Transfer(msg.sender, owner, fee); + } + Transfer(msg.sender, _to, sendAmount); + } + + /** + * @dev Gets the balance of the specified address. + * @param _owner The address to query the the balance of. + * @return An uint representing the amount owned by the passed address. + */ + function balanceOf(address _owner) public constant returns (uint balance) { + return balances[_owner]; + } + +} + +/** + * @title Standard ERC20 token + * + * @dev Implementation of the basic standard token. + * @dev https://github.com/ethereum/EIPs/issues/20 + * @dev Based oncode by FirstBlood: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol + */ +contract StandardToken is BasicToken, ERC20 { + + mapping (address => mapping (address => uint)) public allowed; + + uint public constant MAX_UINT = 2**256 - 1; + + /** + * @dev Transfer tokens from one address to another + * @param _from address The address which you want to send tokens from + * @param _to address The address which you want to transfer to + * @param _value uint the amount of tokens to be transferred + */ + function transferFrom(address _from, address _to, uint _value) public onlyPayloadSize(3 * 32) { + var _allowance = allowed[_from][msg.sender]; + + // Check is not needed because sub(_allowance, _value) will already throw if this condition is not met + // if (_value > _allowance) throw; + + uint fee = (_value.mul(basisPointsRate)).div(10000); + if (fee > maximumFee) { + fee = maximumFee; + } + if (_allowance < MAX_UINT) { + allowed[_from][msg.sender] = _allowance.sub(_value); + } + uint sendAmount = _value.sub(fee); + balances[_from] = balances[_from].sub(_value); + balances[_to] = balances[_to].add(sendAmount); + if (fee > 0) { + balances[owner] = balances[owner].add(fee); + Transfer(_from, owner, fee); + } + Transfer(_from, _to, sendAmount); + } + + /** + * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender. + * @param _spender The address which will spend the funds. + * @param _value The amount of tokens to be spent. + */ + function approve(address _spender, uint _value) public onlyPayloadSize(2 * 32) { + + // To change the approve amount you first have to reduce the addresses` + // allowance to zero by calling `approve(_spender, 0)` if it is not + // already 0 to mitigate the race condition described here: + // https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 + require(!((_value != 0) && (allowed[msg.sender][_spender] != 0))); + + allowed[msg.sender][_spender] = _value; + Approval(msg.sender, _spender, _value); + } + + /** + * @dev Function to check the amount of tokens than an owner allowed to a spender. + * @param _owner address The address which owns the funds. + * @param _spender address The address which will spend the funds. + * @return A uint specifying the amount of tokens still available for the spender. + */ + function allowance(address _owner, address _spender) public constant returns (uint remaining) { + return allowed[_owner][_spender]; + } + +} + + +/** + * @title Pausable + * @dev Base contract which allows children to implement an emergency stop mechanism. + */ +contract Pausable is Ownable { + event Pause(); + event Unpause(); + + bool public paused = false; + + + /** + * @dev Modifier to make a function callable only when the contract is not paused. + */ + modifier whenNotPaused() { + require(!paused); + _; + } + + /** + * @dev Modifier to make a function callable only when the contract is paused. + */ + modifier whenPaused() { + require(paused); + _; + } + + /** + * @dev called by the owner to pause, triggers stopped state + */ + function pause() onlyOwner whenNotPaused public { + paused = true; + Pause(); + } + + /** + * @dev called by the owner to unpause, returns to normal state + */ + function unpause() onlyOwner whenPaused public { + paused = false; + Unpause(); + } +} + +contract BlackList is Ownable, BasicToken { + + /////// Getters to allow the same blacklist to be used also by other contracts (including upgraded Tether) /////// + function getBlackListStatus(address _maker) external constant returns (bool) { + return isBlackListed[_maker]; + } + + function getOwner() external constant returns (address) { + return owner; + } + + mapping (address => bool) public isBlackListed; + + function addBlackList (address _evilUser) public onlyOwner { + isBlackListed[_evilUser] = true; + AddedBlackList(_evilUser); + } + + function removeBlackList (address _clearedUser) public onlyOwner { + isBlackListed[_clearedUser] = false; + RemovedBlackList(_clearedUser); + } + + function destroyBlackFunds (address _blackListedUser) public onlyOwner { + require(isBlackListed[_blackListedUser]); + uint dirtyFunds = balanceOf(_blackListedUser); + balances[_blackListedUser] = 0; + _totalSupply -= dirtyFunds; + DestroyedBlackFunds(_blackListedUser, dirtyFunds); + } + + event DestroyedBlackFunds(address _blackListedUser, uint _balance); + + event AddedBlackList(address _user); + + event RemovedBlackList(address _user); + +} + +contract UpgradedStandardToken is StandardToken{ + // those methods are called by the legacy contract + // and they must ensure msg.sender to be the contract address + function transferByLegacy(address from, address to, uint value) public; + function transferFromByLegacy(address sender, address from, address spender, uint value) public; + function approveByLegacy(address from, address spender, uint value) public; +} + +contract TetherToken is Pausable, StandardToken, BlackList { + + string public name; + string public symbol; + uint public decimals; + address public upgradedAddress; + bool public deprecated; + + // The contract can be initialized with a number of tokens + // All the tokens are deposited to the owner address + // + // @param _balance Initial supply of the contract + // @param _name Token Name + // @param _symbol Token symbol + // @param _decimals Token decimals + function TetherToken(uint _initialSupply, string _name, string _symbol, uint _decimals) public { + _totalSupply = _initialSupply; + name = _name; + symbol = _symbol; + decimals = _decimals; + balances[owner] = _initialSupply; + deprecated = false; + } + + // Forward ERC20 methods to upgraded contract if this one is deprecated + function transfer(address _to, uint _value) public whenNotPaused { + require(!isBlackListed[msg.sender]); + if (deprecated) { + return UpgradedStandardToken(upgradedAddress).transferByLegacy(msg.sender, _to, _value); + } else { + return super.transfer(_to, _value); + } + } + + // Forward ERC20 methods to upgraded contract if this one is deprecated + function transferFrom(address _from, address _to, uint _value) public whenNotPaused { + require(!isBlackListed[_from]); + if (deprecated) { + return UpgradedStandardToken(upgradedAddress).transferFromByLegacy(msg.sender, _from, _to, _value); + } else { + return super.transferFrom(_from, _to, _value); + } + } + + // Forward ERC20 methods to upgraded contract if this one is deprecated + function balanceOf(address who) public constant returns (uint) { + if (deprecated) { + return UpgradedStandardToken(upgradedAddress).balanceOf(who); + } else { + return super.balanceOf(who); + } + } + + // Forward ERC20 methods to upgraded contract if this one is deprecated + function approve(address _spender, uint _value) public onlyPayloadSize(2 * 32) { + if (deprecated) { + return UpgradedStandardToken(upgradedAddress).approveByLegacy(msg.sender, _spender, _value); + } else { + return super.approve(_spender, _value); + } + } + + // Forward ERC20 methods to upgraded contract if this one is deprecated + function allowance(address _owner, address _spender) public constant returns (uint remaining) { + if (deprecated) { + return StandardToken(upgradedAddress).allowance(_owner, _spender); + } else { + return super.allowance(_owner, _spender); + } + } + + // deprecate current contract in favour of a new one + function deprecate(address _upgradedAddress) public onlyOwner { + deprecated = true; + upgradedAddress = _upgradedAddress; + Deprecate(_upgradedAddress); + } + + // deprecate current contract if favour of a new one + function totalSupply() public constant returns (uint) { + if (deprecated) { + return StandardToken(upgradedAddress).totalSupply(); + } else { + return _totalSupply; + } + } + + // Issue a new amount of tokens + // these tokens are deposited into the owner address + // + // @param _amount Number of tokens to be issued + function issue(uint amount) public onlyOwner { + require(_totalSupply + amount > _totalSupply); + require(balances[owner] + amount > balances[owner]); + + balances[owner] += amount; + _totalSupply += amount; + Issue(amount); + } + + // Redeem tokens. + // These tokens are withdrawn from the owner address + // if the balance must be enough to cover the redeem + // or the call will fail. + // @param _amount Number of tokens to be issued + function redeem(uint amount) public onlyOwner { + require(_totalSupply >= amount); + require(balances[owner] >= amount); + + _totalSupply -= amount; + balances[owner] -= amount; + Redeem(amount); + } + + function setParams(uint newBasisPoints, uint newMaxFee) public onlyOwner { + // Ensure transparency by hardcoding limit beyond which fees can never be added + require(newBasisPoints < 20); + require(newMaxFee < 50); + + basisPointsRate = newBasisPoints; + maximumFee = newMaxFee.mul(10**decimals); + + Params(basisPointsRate, maximumFee); + } + + // Called when new token are issued + event Issue(uint amount); + + // Called when tokens are redeemed + event Redeem(uint amount); + + // Called when contract is deprecated + event Deprecate(address newAddress); + + // Called if contract ever adds fees + event Params(uint feeBasisPoints, uint maxFee); +} \ No newline at end of file diff --git a/contracts/original/usdc_implementation.sol b/contracts/original/usdc_implementation.sol new file mode 100644 index 0000000..50008b1 --- /dev/null +++ b/contracts/original/usdc_implementation.sol @@ -0,0 +1,2876 @@ +// * SPDX-License-Identifier: Apache-2.0 +pragma solidity <0.8.0 =0.6.12 >=0.6.2; + +// node_modules/@openzeppelin/contracts/utils/Address.sol + +/** + * @dev Collection of functions related to the address type + */ +library Address { + /** + * @dev Returns true if `account` is a contract. + * + * [IMPORTANT] + * ==== + * It is unsafe to assume that an address for which this function returns + * false is an externally-owned account (EOA) and not a contract. + * + * Among others, `isContract` will return false for the following + * types of addresses: + * + * - an externally-owned account + * - a contract in construction + * - an address where a contract will be created + * - an address where a contract lived, but was destroyed + * ==== + */ + function isContract(address account) internal view returns (bool) { + // This method relies on extcodesize, which returns 0 for contracts in + // construction, since the code is only stored at the end of the + // constructor execution. + + uint256 size; + // solhint-disable-next-line no-inline-assembly + assembly { size := extcodesize(account) } + return size > 0; + } + + /** + * @dev Replacement for Solidity's `transfer`: sends `amount` wei to + * `recipient`, forwarding all available gas and reverting on errors. + * + * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost + * of certain opcodes, possibly making contracts go over the 2300 gas limit + * imposed by `transfer`, making them unable to receive funds via + * `transfer`. {sendValue} removes this limitation. + * + * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. + * + * IMPORTANT: because control is transferred to `recipient`, care must be + * taken to not create reentrancy vulnerabilities. Consider using + * {ReentrancyGuard} or the + * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. + */ + function sendValue(address payable recipient, uint256 amount) internal { + require(address(this).balance >= amount, "Address: insufficient balance"); + + // solhint-disable-next-line avoid-low-level-calls, avoid-call-value + (bool success, ) = recipient.call{ value: amount }(""); + require(success, "Address: unable to send value, recipient may have reverted"); + } + + /** + * @dev Performs a Solidity function call using a low level `call`. A + * plain`call` is an unsafe replacement for a function call: use this + * function instead. + * + * If `target` reverts with a revert reason, it is bubbled up by this + * function (like regular Solidity function calls). + * + * Returns the raw returned data. To convert to the expected return value, + * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. + * + * Requirements: + * + * - `target` must be a contract. + * - calling `target` with `data` must not revert. + * + * _Available since v3.1._ + */ + function functionCall(address target, bytes memory data) internal returns (bytes memory) { + return functionCall(target, data, "Address: low-level call failed"); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with + * `errorMessage` as a fallback revert reason when `target` reverts. + * + * _Available since v3.1._ + */ + function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { + return functionCallWithValue(target, data, 0, errorMessage); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], + * but also transferring `value` wei to `target`. + * + * Requirements: + * + * - the calling contract must have an ETH balance of at least `value`. + * - the called Solidity function must be `payable`. + * + * _Available since v3.1._ + */ + function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { + return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); + } + + /** + * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but + * with `errorMessage` as a fallback revert reason when `target` reverts. + * + * _Available since v3.1._ + */ + function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) { + require(address(this).balance >= value, "Address: insufficient balance for call"); + require(isContract(target), "Address: call to non-contract"); + + // solhint-disable-next-line avoid-low-level-calls + (bool success, bytes memory returndata) = target.call{ value: value }(data); + return _verifyCallResult(success, returndata, errorMessage); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], + * but performing a static call. + * + * _Available since v3.3._ + */ + function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { + return functionStaticCall(target, data, "Address: low-level static call failed"); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], + * but performing a static call. + * + * _Available since v3.3._ + */ + function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) { + require(isContract(target), "Address: static call to non-contract"); + + // solhint-disable-next-line avoid-low-level-calls + (bool success, bytes memory returndata) = target.staticcall(data); + return _verifyCallResult(success, returndata, errorMessage); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], + * but performing a delegate call. + * + * _Available since v3.4._ + */ + function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { + return functionDelegateCall(target, data, "Address: low-level delegate call failed"); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], + * but performing a delegate call. + * + * _Available since v3.4._ + */ + function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { + require(isContract(target), "Address: delegate call to non-contract"); + + // solhint-disable-next-line avoid-low-level-calls + (bool success, bytes memory returndata) = target.delegatecall(data); + return _verifyCallResult(success, returndata, errorMessage); + } + + function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) { + if (success) { + return returndata; + } else { + // Look for revert reason and bubble it up if present + if (returndata.length > 0) { + // The easiest way to bubble the revert reason is using memory via assembly + + // solhint-disable-next-line no-inline-assembly + assembly { + let returndata_size := mload(returndata) + revert(add(32, returndata), returndata_size) + } + } else { + revert(errorMessage); + } + } + } +} + +// contracts/interface/IERC1271.sol +/** + * Copyright 2023 Circle Internet Group, Inc. All rights reserved. + * + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @dev Interface of the ERC1271 standard signature validation method for + * contracts as defined in https://eips.ethereum.org/EIPS/eip-1271[ERC-1271]. + */ +interface IERC1271 { + /** + * @dev Should return whether the signature provided is valid for the provided data + * @param hash Hash of the data to be signed + * @param signature Signature byte array associated with the provided data hash + * @return magicValue bytes4 magic value 0x1626ba7e when function passes + */ + function isValidSignature(bytes32 hash, bytes memory signature) + external + view + returns (bytes4 magicValue); +} + +// contracts/util/ECRecover.sol +/** + * Copyright 2023 Circle Internet Group, Inc. All rights reserved. + * + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @title ECRecover + * @notice A library that provides a safe ECDSA recovery function + */ +library ECRecover { + /** + * @notice Recover signer's address from a signed message + * @dev Adapted from: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/65e4ffde586ec89af3b7e9140bdc9235d1254853/contracts/cryptography/ECDSA.sol + * Modifications: Accept v, r, and s as separate arguments + * @param digest Keccak-256 hash digest of the signed message + * @param v v of the signature + * @param r r of the signature + * @param s s of the signature + * @return Signer address + */ + function recover( + bytes32 digest, + uint8 v, + bytes32 r, + bytes32 s + ) internal pure returns (address) { + // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature + // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines + // the valid range for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most + // signatures from current libraries generate a unique signature with an s-value in the lower half order. + // + // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value + // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or + // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept + // these malleable signatures as well. + if ( + uint256(s) > + 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0 + ) { + revert("ECRecover: invalid signature 's' value"); + } + + if (v != 27 && v != 28) { + revert("ECRecover: invalid signature 'v' value"); + } + + // If the signature is valid (and not malleable), return the signer address + address signer = ecrecover(digest, v, r, s); + require(signer != address(0), "ECRecover: invalid signature"); + + return signer; + } + + /** + * @notice Recover signer's address from a signed message + * @dev Adapted from: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/0053ee040a7ff1dbc39691c9e67a69f564930a88/contracts/utils/cryptography/ECDSA.sol + * @param digest Keccak-256 hash digest of the signed message + * @param signature Signature byte array associated with hash + * @return Signer address + */ + function recover(bytes32 digest, bytes memory signature) + internal + pure + returns (address) + { + require(signature.length == 65, "ECRecover: invalid signature length"); + + bytes32 r; + bytes32 s; + uint8 v; + + // ecrecover takes the signature parameters, and the only way to get them + // currently is to use assembly. + /// @solidity memory-safe-assembly + assembly { + r := mload(add(signature, 0x20)) + s := mload(add(signature, 0x40)) + v := byte(0, mload(add(signature, 0x60))) + } + return recover(digest, v, r, s); + } +} + +// contracts/util/EIP712.sol +/** + * Copyright 2023 Circle Internet Group, Inc. All rights reserved. + * + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @title EIP712 + * @notice A library that provides EIP712 helper functions + */ +library EIP712 { + /** + * @notice Make EIP712 domain separator + * @param name Contract name + * @param version Contract version + * @param chainId Blockchain ID + * @return Domain separator + */ + function makeDomainSeparator( + string memory name, + string memory version, + uint256 chainId + ) internal view returns (bytes32) { + return + keccak256( + abi.encode( + // keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)") + 0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f, + keccak256(bytes(name)), + keccak256(bytes(version)), + chainId, + address(this) + ) + ); + } + + /** + * @notice Make EIP712 domain separator + * @param name Contract name + * @param version Contract version + * @return Domain separator + */ + function makeDomainSeparator(string memory name, string memory version) + internal + view + returns (bytes32) + { + uint256 chainId; + assembly { + chainId := chainid() + } + return makeDomainSeparator(name, version, chainId); + } +} + +// contracts/util/MessageHashUtils.sol +/** + * Copyright 2023 Circle Internet Group, Inc. All rights reserved. + * + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @dev Signature message hash utilities for producing digests to be consumed by {ECDSA} recovery or signing. + * + * The library provides methods for generating a hash of a message that conforms to the + * https://eips.ethereum.org/EIPS/eip-191[EIP 191] and https://eips.ethereum.org/EIPS/eip-712[EIP 712] + * specifications. + */ +library MessageHashUtils { + /** + * @dev Returns the keccak256 digest of an EIP-712 typed data (EIP-191 version `0x01`). + * Adapted from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/21bb89ef5bfc789b9333eb05e3ba2b7b284ac77c/contracts/utils/cryptography/MessageHashUtils.sol + * + * The digest is calculated from a `domainSeparator` and a `structHash`, by prefixing them with + * `\x19\x01` and hashing the result. It corresponds to the hash signed by the + * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] JSON-RPC method as part of EIP-712. + * + * @param domainSeparator Domain separator + * @param structHash Hashed EIP-712 data struct + * @return digest The keccak256 digest of an EIP-712 typed data + */ + function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) + internal + pure + returns (bytes32 digest) + { + assembly { + let ptr := mload(0x40) + mstore(ptr, "\x19\x01") + mstore(add(ptr, 0x02), domainSeparator) + mstore(add(ptr, 0x22), structHash) + digest := keccak256(ptr, 0x42) + } + } +} + +// contracts/v1/Ownable.sol +/** + + * + * Copyright (c) 2018 zOS Global Limited. + * Copyright (c) 2018-2020 CENTRE SECZ + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/** + * @notice The Ownable contract has an owner address, and provides basic + * authorization control functions + * @dev Forked from https://github.com/OpenZeppelin/openzeppelin-labs/blob/3887ab77b8adafba4a26ace002f3a684c1a3388b/upgradeability_ownership/contracts/ownership/Ownable.sol + * Modifications: + * 1. Consolidate OwnableStorage into this contract (7/13/18) + * 2. Reformat, conform to Solidity 0.6 syntax, and add error messages (5/13/20) + * 3. Make public functions external (5/27/20) + */ +contract Ownable { + // Owner of the contract + address private _owner; + + /** + * @dev Event to show ownership has been transferred + * @param previousOwner representing the address of the previous owner + * @param newOwner representing the address of the new owner + */ + event OwnershipTransferred(address previousOwner, address newOwner); + + /** + * @dev The constructor sets the original owner of the contract to the sender account. + */ + constructor() public { + setOwner(msg.sender); + } + + /** + * @dev Tells the address of the owner + * @return the address of the owner + */ + function owner() external view returns (address) { + return _owner; + } + + /** + * @dev Sets a new owner address + */ + function setOwner(address newOwner) internal { + _owner = newOwner; + } + + /** + * @dev Throws if called by any account other than the owner. + */ + modifier onlyOwner() { + require(msg.sender == _owner, "Ownable: caller is not the owner"); + _; + } + + /** + * @dev Allows the current owner to transfer control of the contract to a newOwner. + * @param newOwner The address to transfer ownership to. + */ + function transferOwnership(address newOwner) external onlyOwner { + require( + newOwner != address(0), + "Ownable: new owner is the zero address" + ); + emit OwnershipTransferred(_owner, newOwner); + setOwner(newOwner); + } +} + +// contracts/v2/EIP712Domain.sol +/** + * Copyright 2023 Circle Internet Group, Inc. All rights reserved. + * + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// solhint-disable func-name-mixedcase + +/** + * @title EIP712 Domain + */ +contract EIP712Domain { + // was originally DOMAIN_SEPARATOR + // but that has been moved to a method so we can override it in V2_2+ + bytes32 internal _DEPRECATED_CACHED_DOMAIN_SEPARATOR; + + /** + * @notice Get the EIP712 Domain Separator. + * @return The bytes32 EIP712 domain separator. + */ + function DOMAIN_SEPARATOR() external view returns (bytes32) { + return _domainSeparator(); + } + + /** + * @dev Internal method to get the EIP712 Domain Separator. + * @return The bytes32 EIP712 domain separator. + */ + function _domainSeparator() internal virtual view returns (bytes32) { + return _DEPRECATED_CACHED_DOMAIN_SEPARATOR; + } +} + +// node_modules/@openzeppelin/contracts/math/SafeMath.sol + +/** + * @dev Wrappers over Solidity's arithmetic operations with added overflow + * checks. + * + * Arithmetic operations in Solidity wrap on overflow. This can easily result + * in bugs, because programmers usually assume that an overflow raises an + * error, which is the standard behavior in high level programming languages. + * `SafeMath` restores this intuition by reverting the transaction when an + * operation overflows. + * + * Using this library instead of the unchecked operations eliminates an entire + * class of bugs, so it's recommended to use it always. + */ +library SafeMath { + /** + * @dev Returns the addition of two unsigned integers, with an overflow flag. + * + * _Available since v3.4._ + */ + function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) { + uint256 c = a + b; + if (c < a) return (false, 0); + return (true, c); + } + + /** + * @dev Returns the substraction of two unsigned integers, with an overflow flag. + * + * _Available since v3.4._ + */ + function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) { + if (b > a) return (false, 0); + return (true, a - b); + } + + /** + * @dev Returns the multiplication of two unsigned integers, with an overflow flag. + * + * _Available since v3.4._ + */ + function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) { + // Gas optimization: this is cheaper than requiring 'a' not being zero, but the + // benefit is lost if 'b' is also tested. + // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 + if (a == 0) return (true, 0); + uint256 c = a * b; + if (c / a != b) return (false, 0); + return (true, c); + } + + /** + * @dev Returns the division of two unsigned integers, with a division by zero flag. + * + * _Available since v3.4._ + */ + function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) { + if (b == 0) return (false, 0); + return (true, a / b); + } + + /** + * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag. + * + * _Available since v3.4._ + */ + function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) { + if (b == 0) return (false, 0); + return (true, a % b); + } + + /** + * @dev Returns the addition of two unsigned integers, reverting on + * overflow. + * + * Counterpart to Solidity's `+` operator. + * + * Requirements: + * + * - Addition cannot overflow. + */ + function add(uint256 a, uint256 b) internal pure returns (uint256) { + uint256 c = a + b; + require(c >= a, "SafeMath: addition overflow"); + return c; + } + + /** + * @dev Returns the subtraction of two unsigned integers, reverting on + * overflow (when the result is negative). + * + * Counterpart to Solidity's `-` operator. + * + * Requirements: + * + * - Subtraction cannot overflow. + */ + function sub(uint256 a, uint256 b) internal pure returns (uint256) { + require(b <= a, "SafeMath: subtraction overflow"); + return a - b; + } + + /** + * @dev Returns the multiplication of two unsigned integers, reverting on + * overflow. + * + * Counterpart to Solidity's `*` operator. + * + * Requirements: + * + * - Multiplication cannot overflow. + */ + function mul(uint256 a, uint256 b) internal pure returns (uint256) { + if (a == 0) return 0; + uint256 c = a * b; + require(c / a == b, "SafeMath: multiplication overflow"); + return c; + } + + /** + * @dev Returns the integer division of two unsigned integers, reverting on + * division by zero. The result is rounded towards zero. + * + * Counterpart to Solidity's `/` operator. Note: this function uses a + * `revert` opcode (which leaves remaining gas untouched) while Solidity + * uses an invalid opcode to revert (consuming all remaining gas). + * + * Requirements: + * + * - The divisor cannot be zero. + */ + function div(uint256 a, uint256 b) internal pure returns (uint256) { + require(b > 0, "SafeMath: division by zero"); + return a / b; + } + + /** + * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), + * reverting when dividing by zero. + * + * Counterpart to Solidity's `%` operator. This function uses a `revert` + * opcode (which leaves remaining gas untouched) while Solidity uses an + * invalid opcode to revert (consuming all remaining gas). + * + * Requirements: + * + * - The divisor cannot be zero. + */ + function mod(uint256 a, uint256 b) internal pure returns (uint256) { + require(b > 0, "SafeMath: modulo by zero"); + return a % b; + } + + /** + * @dev Returns the subtraction of two unsigned integers, reverting with custom message on + * overflow (when the result is negative). + * + * CAUTION: This function is deprecated because it requires allocating memory for the error + * message unnecessarily. For custom revert reasons use {trySub}. + * + * Counterpart to Solidity's `-` operator. + * + * Requirements: + * + * - Subtraction cannot overflow. + */ + function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { + require(b <= a, errorMessage); + return a - b; + } + + /** + * @dev Returns the integer division of two unsigned integers, reverting with custom message on + * division by zero. The result is rounded towards zero. + * + * CAUTION: This function is deprecated because it requires allocating memory for the error + * message unnecessarily. For custom revert reasons use {tryDiv}. + * + * Counterpart to Solidity's `/` operator. Note: this function uses a + * `revert` opcode (which leaves remaining gas untouched) while Solidity + * uses an invalid opcode to revert (consuming all remaining gas). + * + * Requirements: + * + * - The divisor cannot be zero. + */ + function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { + require(b > 0, errorMessage); + return a / b; + } + + /** + * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), + * reverting with custom message when dividing by zero. + * + * CAUTION: This function is deprecated because it requires allocating memory for the error + * message unnecessarily. For custom revert reasons use {tryMod}. + * + * Counterpart to Solidity's `%` operator. This function uses a `revert` + * opcode (which leaves remaining gas untouched) while Solidity uses an + * invalid opcode to revert (consuming all remaining gas). + * + * Requirements: + * + * - The divisor cannot be zero. + */ + function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { + require(b > 0, errorMessage); + return a % b; + } +} + +// node_modules/@openzeppelin/contracts/token/ERC20/IERC20.sol + +/** + * @dev Interface of the ERC20 standard as defined in the EIP. + */ +interface IERC20 { + /** + * @dev Returns the amount of tokens in existence. + */ + function totalSupply() external view returns (uint256); + + /** + * @dev Returns the amount of tokens owned by `account`. + */ + function balanceOf(address account) external view returns (uint256); + + /** + * @dev Moves `amount` tokens from the caller's account to `recipient`. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * Emits a {Transfer} event. + */ + function transfer(address recipient, uint256 amount) external returns (bool); + + /** + * @dev Returns the remaining number of tokens that `spender` will be + * allowed to spend on behalf of `owner` through {transferFrom}. This is + * zero by default. + * + * This value changes when {approve} or {transferFrom} are called. + */ + function allowance(address owner, address spender) external view returns (uint256); + + /** + * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * IMPORTANT: Beware that changing an allowance with this method brings the risk + * that someone may use both the old and the new allowance by unfortunate + * transaction ordering. One possible solution to mitigate this race + * condition is to first reduce the spender's allowance to 0 and set the + * desired value afterwards: + * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 + * + * Emits an {Approval} event. + */ + function approve(address spender, uint256 amount) external returns (bool); + + /** + * @dev Moves `amount` tokens from `sender` to `recipient` using the + * allowance mechanism. `amount` is then deducted from the caller's + * allowance. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * Emits a {Transfer} event. + */ + function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); + + /** + * @dev Emitted when `value` tokens are moved from one account (`from`) to + * another (`to`). + * + * Note that `value` may be zero. + */ + event Transfer(address indexed from, address indexed to, uint256 value); + + /** + * @dev Emitted when the allowance of a `spender` for an `owner` is set by + * a call to {approve}. `value` is the new allowance. + */ + event Approval(address indexed owner, address indexed spender, uint256 value); +} + +// contracts/v1/AbstractFiatTokenV1.sol +/** + * Copyright 2023 Circle Internet Group, Inc. All rights reserved. + * + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +abstract contract AbstractFiatTokenV1 is IERC20 { + function _approve( + address owner, + address spender, + uint256 value + ) internal virtual; + + function _transfer( + address from, + address to, + uint256 value + ) internal virtual; +} + +// contracts/v1/Blacklistable.sol +/** + * Copyright 2023 Circle Internet Group, Inc. All rights reserved. + * + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @title Blacklistable Token + * @dev Allows accounts to be blacklisted by a "blacklister" role + */ +abstract contract Blacklistable is Ownable { + address public blacklister; + mapping(address => bool) internal _deprecatedBlacklisted; + + event Blacklisted(address indexed _account); + event UnBlacklisted(address indexed _account); + event BlacklisterChanged(address indexed newBlacklister); + + /** + * @dev Throws if called by any account other than the blacklister. + */ + modifier onlyBlacklister() { + require( + msg.sender == blacklister, + "Blacklistable: caller is not the blacklister" + ); + _; + } + + /** + * @dev Throws if argument account is blacklisted. + * @param _account The address to check. + */ + modifier notBlacklisted(address _account) { + require( + !_isBlacklisted(_account), + "Blacklistable: account is blacklisted" + ); + _; + } + + /** + * @notice Checks if account is blacklisted. + * @param _account The address to check. + * @return True if the account is blacklisted, false if the account is not blacklisted. + */ + function isBlacklisted(address _account) external view returns (bool) { + return _isBlacklisted(_account); + } + + /** + * @notice Adds account to blacklist. + * @param _account The address to blacklist. + */ + function blacklist(address _account) external onlyBlacklister { + _blacklist(_account); + emit Blacklisted(_account); + } + + /** + * @notice Removes account from blacklist. + * @param _account The address to remove from the blacklist. + */ + function unBlacklist(address _account) external onlyBlacklister { + _unBlacklist(_account); + emit UnBlacklisted(_account); + } + + /** + * @notice Updates the blacklister address. + * @param _newBlacklister The address of the new blacklister. + */ + function updateBlacklister(address _newBlacklister) external onlyOwner { + require( + _newBlacklister != address(0), + "Blacklistable: new blacklister is the zero address" + ); + blacklister = _newBlacklister; + emit BlacklisterChanged(blacklister); + } + + /** + * @dev Checks if account is blacklisted. + * @param _account The address to check. + * @return true if the account is blacklisted, false otherwise. + */ + function _isBlacklisted(address _account) + internal + virtual + view + returns (bool); + + /** + * @dev Helper method that blacklists an account. + * @param _account The address to blacklist. + */ + function _blacklist(address _account) internal virtual; + + /** + * @dev Helper method that unblacklists an account. + * @param _account The address to unblacklist. + */ + function _unBlacklist(address _account) internal virtual; +} + +// contracts/v1/Pausable.sol +/** + + * + * Copyright (c) 2016 Smart Contract Solutions, Inc. + * Copyright (c) 2018-2020 CENTRE SECZ + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/** + * @notice Base contract which allows children to implement an emergency stop + * mechanism + * @dev Forked from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/feb665136c0dae9912e08397c1a21c4af3651ef3/contracts/lifecycle/Pausable.sol + * Modifications: + * 1. Added pauser role, switched pause/unpause to be onlyPauser (6/14/2018) + * 2. Removed whenNotPause/whenPaused from pause/unpause (6/14/2018) + * 3. Removed whenPaused (6/14/2018) + * 4. Switches ownable library to use ZeppelinOS (7/12/18) + * 5. Remove constructor (7/13/18) + * 6. Reformat, conform to Solidity 0.6 syntax and add error messages (5/13/20) + * 7. Make public functions external (5/27/20) + */ +contract Pausable is Ownable { + event Pause(); + event Unpause(); + event PauserChanged(address indexed newAddress); + + address public pauser; + bool public paused = false; + + /** + * @dev Modifier to make a function callable only when the contract is not paused. + */ + modifier whenNotPaused() { + require(!paused, "Pausable: paused"); + _; + } + + /** + * @dev throws if called by any account other than the pauser + */ + modifier onlyPauser() { + require(msg.sender == pauser, "Pausable: caller is not the pauser"); + _; + } + + /** + * @dev called by the owner to pause, triggers stopped state + */ + function pause() external onlyPauser { + paused = true; + emit Pause(); + } + + /** + * @dev called by the owner to unpause, returns to normal state + */ + function unpause() external onlyPauser { + paused = false; + emit Unpause(); + } + + /** + * @notice Updates the pauser address. + * @param _newPauser The address of the new pauser. + */ + function updatePauser(address _newPauser) external onlyOwner { + require( + _newPauser != address(0), + "Pausable: new pauser is the zero address" + ); + pauser = _newPauser; + emit PauserChanged(pauser); + } +} + +// contracts/util/SignatureChecker.sol +/** + * Copyright 2023 Circle Internet Group, Inc. All rights reserved. + * + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @dev Signature verification helper that can be used instead of `ECRecover.recover` to seamlessly support both ECDSA + * signatures from externally owned accounts (EOAs) as well as ERC1271 signatures from smart contract wallets. + * + * Adapted from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/21bb89ef5bfc789b9333eb05e3ba2b7b284ac77c/contracts/utils/cryptography/SignatureChecker.sol + */ +library SignatureChecker { + /** + * @dev Checks if a signature is valid for a given signer and data hash. If the signer is a smart contract, the + * signature is validated against that smart contract using ERC1271, otherwise it's validated using `ECRecover.recover`. + * @param signer Address of the claimed signer + * @param digest Keccak-256 hash digest of the signed message + * @param signature Signature byte array associated with hash + */ + function isValidSignatureNow( + address signer, + bytes32 digest, + bytes memory signature + ) external view returns (bool) { + if (!isContract(signer)) { + return ECRecover.recover(digest, signature) == signer; + } + return isValidERC1271SignatureNow(signer, digest, signature); + } + + /** + * @dev Checks if a signature is valid for a given signer and data hash. The signature is validated + * against the signer smart contract using ERC1271. + * @param signer Address of the claimed signer + * @param digest Keccak-256 hash digest of the signed message + * @param signature Signature byte array associated with hash + * + * NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus + * change through time. It could return true at block N and false at block N+1 (or the opposite). + */ + function isValidERC1271SignatureNow( + address signer, + bytes32 digest, + bytes memory signature + ) internal view returns (bool) { + (bool success, bytes memory result) = signer.staticcall( + abi.encodeWithSelector( + IERC1271.isValidSignature.selector, + digest, + signature + ) + ); + return (success && + result.length >= 32 && + abi.decode(result, (bytes32)) == + bytes32(IERC1271.isValidSignature.selector)); + } + + /** + * @dev Checks if the input address is a smart contract. + */ + function isContract(address addr) internal view returns (bool) { + uint256 size; + assembly { + size := extcodesize(addr) + } + return size > 0; + } +} + +// contracts/v2/AbstractFiatTokenV2.sol +/** + * Copyright 2023 Circle Internet Group, Inc. All rights reserved. + * + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +abstract contract AbstractFiatTokenV2 is AbstractFiatTokenV1 { + function _increaseAllowance( + address owner, + address spender, + uint256 increment + ) internal virtual; + + function _decreaseAllowance( + address owner, + address spender, + uint256 decrement + ) internal virtual; +} + +// node_modules/@openzeppelin/contracts/token/ERC20/SafeERC20.sol + +/** + * @title SafeERC20 + * @dev Wrappers around ERC20 operations that throw on failure (when the token + * contract returns false). Tokens that return no value (and instead revert or + * throw on failure) are also supported, non-reverting calls are assumed to be + * successful. + * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, + * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. + */ +library SafeERC20 { + using SafeMath for uint256; + using Address for address; + + function safeTransfer(IERC20 token, address to, uint256 value) internal { + _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); + } + + function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { + _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); + } + + /** + * @dev Deprecated. This function has issues similar to the ones found in + * {IERC20-approve}, and its usage is discouraged. + * + * Whenever possible, use {safeIncreaseAllowance} and + * {safeDecreaseAllowance} instead. + */ + function safeApprove(IERC20 token, address spender, uint256 value) internal { + // safeApprove should only be called when setting an initial allowance, + // or when resetting it to zero. To increase and decrease it, use + // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' + // solhint-disable-next-line max-line-length + require((value == 0) || (token.allowance(address(this), spender) == 0), + "SafeERC20: approve from non-zero to non-zero allowance" + ); + _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); + } + + function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { + uint256 newAllowance = token.allowance(address(this), spender).add(value); + _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); + } + + function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal { + uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero"); + _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); + } + + /** + * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement + * on the return value: the return value is optional (but if data is returned, it must not be false). + * @param token The token targeted by the call. + * @param data The call data (encoded using abi.encode or one of its variants). + */ + function _callOptionalReturn(IERC20 token, bytes memory data) private { + // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since + // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that + // the target address contains contract code and also asserts for success in the low-level call. + + bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); + if (returndata.length > 0) { // Return data is optional + // solhint-disable-next-line max-line-length + require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); + } + } +} + +// contracts/v1.1/Rescuable.sol +/** + * Copyright 2023 Circle Internet Group, Inc. All rights reserved. + * + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +contract Rescuable is Ownable { + using SafeERC20 for IERC20; + + address private _rescuer; + + event RescuerChanged(address indexed newRescuer); + + /** + * @notice Returns current rescuer + * @return Rescuer's address + */ + function rescuer() external view returns (address) { + return _rescuer; + } + + /** + * @notice Revert if called by any account other than the rescuer. + */ + modifier onlyRescuer() { + require(msg.sender == _rescuer, "Rescuable: caller is not the rescuer"); + _; + } + + /** + * @notice Rescue ERC20 tokens locked up in this contract. + * @param tokenContract ERC20 token contract address + * @param to Recipient address + * @param amount Amount to withdraw + */ + function rescueERC20( + IERC20 tokenContract, + address to, + uint256 amount + ) external onlyRescuer { + tokenContract.safeTransfer(to, amount); + } + + /** + * @notice Updates the rescuer address. + * @param newRescuer The address of the new rescuer. + */ + function updateRescuer(address newRescuer) external onlyOwner { + require( + newRescuer != address(0), + "Rescuable: new rescuer is the zero address" + ); + _rescuer = newRescuer; + emit RescuerChanged(newRescuer); + } +} + +// contracts/v1/FiatTokenV1.sol +/** + * Copyright 2023 Circle Internet Group, Inc. All rights reserved. + * + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @title FiatToken + * @dev ERC20 Token backed by fiat reserves + */ +contract FiatTokenV1 is AbstractFiatTokenV1, Ownable, Pausable, Blacklistable { + using SafeMath for uint256; + + string public name; + string public symbol; + uint8 public decimals; + string public currency; + address public masterMinter; + bool internal initialized; + + /// @dev A mapping that stores the balance and blacklist states for a given address. + /// The first bit defines whether the address is blacklisted (1 if blacklisted, 0 otherwise). + /// The last 255 bits define the balance for the address. + mapping(address => uint256) internal balanceAndBlacklistStates; + mapping(address => mapping(address => uint256)) internal allowed; + uint256 internal totalSupply_ = 0; + mapping(address => bool) internal minters; + mapping(address => uint256) internal minterAllowed; + + event Mint(address indexed minter, address indexed to, uint256 amount); + event Burn(address indexed burner, uint256 amount); + event MinterConfigured(address indexed minter, uint256 minterAllowedAmount); + event MinterRemoved(address indexed oldMinter); + event MasterMinterChanged(address indexed newMasterMinter); + + /** + * @notice Initializes the fiat token contract. + * @param tokenName The name of the fiat token. + * @param tokenSymbol The symbol of the fiat token. + * @param tokenCurrency The fiat currency that the token represents. + * @param tokenDecimals The number of decimals that the token uses. + * @param newMasterMinter The masterMinter address for the fiat token. + * @param newPauser The pauser address for the fiat token. + * @param newBlacklister The blacklister address for the fiat token. + * @param newOwner The owner of the fiat token. + */ + function initialize( + string memory tokenName, + string memory tokenSymbol, + string memory tokenCurrency, + uint8 tokenDecimals, + address newMasterMinter, + address newPauser, + address newBlacklister, + address newOwner + ) public { + require(!initialized, "FiatToken: contract is already initialized"); + require( + newMasterMinter != address(0), + "FiatToken: new masterMinter is the zero address" + ); + require( + newPauser != address(0), + "FiatToken: new pauser is the zero address" + ); + require( + newBlacklister != address(0), + "FiatToken: new blacklister is the zero address" + ); + require( + newOwner != address(0), + "FiatToken: new owner is the zero address" + ); + + name = tokenName; + symbol = tokenSymbol; + currency = tokenCurrency; + decimals = tokenDecimals; + masterMinter = newMasterMinter; + pauser = newPauser; + blacklister = newBlacklister; + setOwner(newOwner); + initialized = true; + } + + /** + * @dev Throws if called by any account other than a minter. + */ + modifier onlyMinters() { + require(minters[msg.sender], "FiatToken: caller is not a minter"); + _; + } + + /** + * @notice Mints fiat tokens to an address. + * @param _to The address that will receive the minted tokens. + * @param _amount The amount of tokens to mint. Must be less than or equal + * to the minterAllowance of the caller. + * @return True if the operation was successful. + */ + function mint(address _to, uint256 _amount) + external + whenNotPaused + onlyMinters + notBlacklisted(msg.sender) + notBlacklisted(_to) + returns (bool) + { + require(_to != address(0), "FiatToken: mint to the zero address"); + require(_amount > 0, "FiatToken: mint amount not greater than 0"); + + uint256 mintingAllowedAmount = minterAllowed[msg.sender]; + require( + _amount <= mintingAllowedAmount, + "FiatToken: mint amount exceeds minterAllowance" + ); + + totalSupply_ = totalSupply_.add(_amount); + _setBalance(_to, _balanceOf(_to).add(_amount)); + minterAllowed[msg.sender] = mintingAllowedAmount.sub(_amount); + emit Mint(msg.sender, _to, _amount); + emit Transfer(address(0), _to, _amount); + return true; + } + + /** + * @dev Throws if called by any account other than the masterMinter + */ + modifier onlyMasterMinter() { + require( + msg.sender == masterMinter, + "FiatToken: caller is not the masterMinter" + ); + _; + } + + /** + * @notice Gets the minter allowance for an account. + * @param minter The address to check. + * @return The remaining minter allowance for the account. + */ + function minterAllowance(address minter) external view returns (uint256) { + return minterAllowed[minter]; + } + + /** + * @notice Checks if an account is a minter. + * @param account The address to check. + * @return True if the account is a minter, false if the account is not a minter. + */ + function isMinter(address account) external view returns (bool) { + return minters[account]; + } + + /** + * @notice Gets the remaining amount of fiat tokens a spender is allowed to transfer on + * behalf of the token owner. + * @param owner The token owner's address. + * @param spender The spender's address. + * @return The remaining allowance. + */ + function allowance(address owner, address spender) + external + override + view + returns (uint256) + { + return allowed[owner][spender]; + } + + /** + * @notice Gets the totalSupply of the fiat token. + * @return The totalSupply of the fiat token. + */ + function totalSupply() external override view returns (uint256) { + return totalSupply_; + } + + /** + * @notice Gets the fiat token balance of an account. + * @param account The address to check. + * @return balance The fiat token balance of the account. + */ + function balanceOf(address account) + external + override + view + returns (uint256) + { + return _balanceOf(account); + } + + /** + * @notice Sets a fiat token allowance for a spender to spend on behalf of the caller. + * @param spender The spender's address. + * @param value The allowance amount. + * @return True if the operation was successful. + */ + function approve(address spender, uint256 value) + external + virtual + override + whenNotPaused + notBlacklisted(msg.sender) + notBlacklisted(spender) + returns (bool) + { + _approve(msg.sender, spender, value); + return true; + } + + /** + * @dev Internal function to set allowance. + * @param owner Token owner's address. + * @param spender Spender's address. + * @param value Allowance amount. + */ + function _approve( + address owner, + address spender, + uint256 value + ) internal override { + require(owner != address(0), "ERC20: approve from the zero address"); + require(spender != address(0), "ERC20: approve to the zero address"); + allowed[owner][spender] = value; + emit Approval(owner, spender, value); + } + + /** + * @notice Transfers tokens from an address to another by spending the caller's allowance. + * @dev The caller must have some fiat token allowance on the payer's tokens. + * @param from Payer's address. + * @param to Payee's address. + * @param value Transfer amount. + * @return True if the operation was successful. + */ + function transferFrom( + address from, + address to, + uint256 value + ) + external + override + whenNotPaused + notBlacklisted(msg.sender) + notBlacklisted(from) + notBlacklisted(to) + returns (bool) + { + require( + value <= allowed[from][msg.sender], + "ERC20: transfer amount exceeds allowance" + ); + _transfer(from, to, value); + allowed[from][msg.sender] = allowed[from][msg.sender].sub(value); + return true; + } + + /** + * @notice Transfers tokens from the caller. + * @param to Payee's address. + * @param value Transfer amount. + * @return True if the operation was successful. + */ + function transfer(address to, uint256 value) + external + override + whenNotPaused + notBlacklisted(msg.sender) + notBlacklisted(to) + returns (bool) + { + _transfer(msg.sender, to, value); + return true; + } + + /** + * @dev Internal function to process transfers. + * @param from Payer's address. + * @param to Payee's address. + * @param value Transfer amount. + */ + function _transfer( + address from, + address to, + uint256 value + ) internal override { + require(from != address(0), "ERC20: transfer from the zero address"); + require(to != address(0), "ERC20: transfer to the zero address"); + require( + value <= _balanceOf(from), + "ERC20: transfer amount exceeds balance" + ); + + _setBalance(from, _balanceOf(from).sub(value)); + _setBalance(to, _balanceOf(to).add(value)); + emit Transfer(from, to, value); + } + + /** + * @notice Adds or updates a new minter with a mint allowance. + * @param minter The address of the minter. + * @param minterAllowedAmount The minting amount allowed for the minter. + * @return True if the operation was successful. + */ + function configureMinter(address minter, uint256 minterAllowedAmount) + external + whenNotPaused + onlyMasterMinter + returns (bool) + { + minters[minter] = true; + minterAllowed[minter] = minterAllowedAmount; + emit MinterConfigured(minter, minterAllowedAmount); + return true; + } + + /** + * @notice Removes a minter. + * @param minter The address of the minter to remove. + * @return True if the operation was successful. + */ + function removeMinter(address minter) + external + onlyMasterMinter + returns (bool) + { + minters[minter] = false; + minterAllowed[minter] = 0; + emit MinterRemoved(minter); + return true; + } + + /** + * @notice Allows a minter to burn some of its own tokens. + * @dev The caller must be a minter, must not be blacklisted, and the amount to burn + * should be less than or equal to the account's balance. + * @param _amount the amount of tokens to be burned. + */ + function burn(uint256 _amount) + external + whenNotPaused + onlyMinters + notBlacklisted(msg.sender) + { + uint256 balance = _balanceOf(msg.sender); + require(_amount > 0, "FiatToken: burn amount not greater than 0"); + require(balance >= _amount, "FiatToken: burn amount exceeds balance"); + + totalSupply_ = totalSupply_.sub(_amount); + _setBalance(msg.sender, balance.sub(_amount)); + emit Burn(msg.sender, _amount); + emit Transfer(msg.sender, address(0), _amount); + } + + /** + * @notice Updates the master minter address. + * @param _newMasterMinter The address of the new master minter. + */ + function updateMasterMinter(address _newMasterMinter) external onlyOwner { + require( + _newMasterMinter != address(0), + "FiatToken: new masterMinter is the zero address" + ); + masterMinter = _newMasterMinter; + emit MasterMinterChanged(masterMinter); + } + + /** + * + */ + function _blacklist(address _account) internal override { + _setBlacklistState(_account, true); + } + + /** + * + */ + function _unBlacklist(address _account) internal override { + _setBlacklistState(_account, false); + } + + /** + * @dev Helper method that sets the blacklist state of an account. + * @param _account The address of the account. + * @param _shouldBlacklist True if the account should be blacklisted, false if the account should be unblacklisted. + */ + function _setBlacklistState(address _account, bool _shouldBlacklist) + internal + virtual + { + _deprecatedBlacklisted[_account] = _shouldBlacklist; + } + + /** + * @dev Helper method that sets the balance of an account. + * @param _account The address of the account. + * @param _balance The new fiat token balance of the account. + */ + function _setBalance(address _account, uint256 _balance) internal virtual { + balanceAndBlacklistStates[_account] = _balance; + } + + /** + * + */ + function _isBlacklisted(address _account) + internal + virtual + override + view + returns (bool) + { + return _deprecatedBlacklisted[_account]; + } + + /** + * @dev Helper method to obtain the balance of an account. + * @param _account The address of the account. + * @return The fiat token balance of the account. + */ + function _balanceOf(address _account) + internal + virtual + view + returns (uint256) + { + return balanceAndBlacklistStates[_account]; + } +} + +// contracts/v2/EIP2612.sol +/** + * Copyright 2023 Circle Internet Group, Inc. All rights reserved. + * + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @title EIP-2612 + * @notice Provide internal implementation for gas-abstracted approvals + */ +abstract contract EIP2612 is AbstractFiatTokenV2, EIP712Domain { + // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)") + bytes32 + public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9; + + mapping(address => uint256) private _permitNonces; + + /** + * @notice Nonces for permit + * @param owner Token owner's address (Authorizer) + * @return Next nonce + */ + function nonces(address owner) external view returns (uint256) { + return _permitNonces[owner]; + } + + /** + * @notice Verify a signed approval permit and execute if valid + * @param owner Token owner's address (Authorizer) + * @param spender Spender's address + * @param value Amount of allowance + * @param deadline The time at which the signature expires (unix time), or max uint256 value to signal no expiration + * @param v v of the signature + * @param r r of the signature + * @param s s of the signature + */ + function _permit( + address owner, + address spender, + uint256 value, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) internal { + _permit(owner, spender, value, deadline, abi.encodePacked(r, s, v)); + } + + /** + * @notice Verify a signed approval permit and execute if valid + * @dev EOA wallet signatures should be packed in the order of r, s, v. + * @param owner Token owner's address (Authorizer) + * @param spender Spender's address + * @param value Amount of allowance + * @param deadline The time at which the signature expires (unix time), or max uint256 value to signal no expiration + * @param signature Signature byte array signed by an EOA wallet or a contract wallet + */ + function _permit( + address owner, + address spender, + uint256 value, + uint256 deadline, + bytes memory signature + ) internal { + require( + deadline == type(uint256).max || deadline >= now, + "FiatTokenV2: permit is expired" + ); + + bytes32 typedDataHash = MessageHashUtils.toTypedDataHash( + _domainSeparator(), + keccak256( + abi.encode( + PERMIT_TYPEHASH, + owner, + spender, + value, + _permitNonces[owner]++, + deadline + ) + ) + ); + require( + SignatureChecker.isValidSignatureNow( + owner, + typedDataHash, + signature + ), + "EIP2612: invalid signature" + ); + + _approve(owner, spender, value); + } +} + +// contracts/v2/EIP3009.sol +/** + * Copyright 2023 Circle Internet Group, Inc. All rights reserved. + * + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @title EIP-3009 + * @notice Provide internal implementation for gas-abstracted transfers + * @dev Contracts that inherit from this must wrap these with publicly + * accessible functions, optionally adding modifiers where necessary + */ +abstract contract EIP3009 is AbstractFiatTokenV2, EIP712Domain { + // keccak256("TransferWithAuthorization(address from,address to,uint256 value,uint256 validAfter,uint256 validBefore,bytes32 nonce)") + bytes32 + public constant TRANSFER_WITH_AUTHORIZATION_TYPEHASH = 0x7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a2267; + + // keccak256("ReceiveWithAuthorization(address from,address to,uint256 value,uint256 validAfter,uint256 validBefore,bytes32 nonce)") + bytes32 + public constant RECEIVE_WITH_AUTHORIZATION_TYPEHASH = 0xd099cc98ef71107a616c4f0f941f04c322d8e254fe26b3c6668db87aae413de8; + + // keccak256("CancelAuthorization(address authorizer,bytes32 nonce)") + bytes32 + public constant CANCEL_AUTHORIZATION_TYPEHASH = 0x158b0a9edf7a828aad02f63cd515c68ef2f50ba807396f6d12842833a1597429; + + /** + * @dev authorizer address => nonce => bool (true if nonce is used) + */ + mapping(address => mapping(bytes32 => bool)) private _authorizationStates; + + event AuthorizationUsed(address indexed authorizer, bytes32 indexed nonce); + event AuthorizationCanceled( + address indexed authorizer, + bytes32 indexed nonce + ); + + /** + * @notice Returns the state of an authorization + * @dev Nonces are randomly generated 32-byte data unique to the + * authorizer's address + * @param authorizer Authorizer's address + * @param nonce Nonce of the authorization + * @return True if the nonce is used + */ + function authorizationState(address authorizer, bytes32 nonce) + external + view + returns (bool) + { + return _authorizationStates[authorizer][nonce]; + } + + /** + * @notice Execute a transfer with a signed authorization + * @param from Payer's address (Authorizer) + * @param to Payee's address + * @param value Amount to be transferred + * @param validAfter The time after which this is valid (unix time) + * @param validBefore The time before which this is valid (unix time) + * @param nonce Unique nonce + * @param v v of the signature + * @param r r of the signature + * @param s s of the signature + */ + function _transferWithAuthorization( + address from, + address to, + uint256 value, + uint256 validAfter, + uint256 validBefore, + bytes32 nonce, + uint8 v, + bytes32 r, + bytes32 s + ) internal { + _transferWithAuthorization( + from, + to, + value, + validAfter, + validBefore, + nonce, + abi.encodePacked(r, s, v) + ); + } + + /** + * @notice Execute a transfer with a signed authorization + * @dev EOA wallet signatures should be packed in the order of r, s, v. + * @param from Payer's address (Authorizer) + * @param to Payee's address + * @param value Amount to be transferred + * @param validAfter The time after which this is valid (unix time) + * @param validBefore The time before which this is valid (unix time) + * @param nonce Unique nonce + * @param signature Signature byte array produced by an EOA wallet or a contract wallet + */ + function _transferWithAuthorization( + address from, + address to, + uint256 value, + uint256 validAfter, + uint256 validBefore, + bytes32 nonce, + bytes memory signature + ) internal { + _requireValidAuthorization(from, nonce, validAfter, validBefore); + _requireValidSignature( + from, + keccak256( + abi.encode( + TRANSFER_WITH_AUTHORIZATION_TYPEHASH, + from, + to, + value, + validAfter, + validBefore, + nonce + ) + ), + signature + ); + + _markAuthorizationAsUsed(from, nonce); + _transfer(from, to, value); + } + + /** + * @notice Receive a transfer with a signed authorization from the payer + * @dev This has an additional check to ensure that the payee's address + * matches the caller of this function to prevent front-running attacks. + * @param from Payer's address (Authorizer) + * @param to Payee's address + * @param value Amount to be transferred + * @param validAfter The time after which this is valid (unix time) + * @param validBefore The time before which this is valid (unix time) + * @param nonce Unique nonce + * @param v v of the signature + * @param r r of the signature + * @param s s of the signature + */ + function _receiveWithAuthorization( + address from, + address to, + uint256 value, + uint256 validAfter, + uint256 validBefore, + bytes32 nonce, + uint8 v, + bytes32 r, + bytes32 s + ) internal { + _receiveWithAuthorization( + from, + to, + value, + validAfter, + validBefore, + nonce, + abi.encodePacked(r, s, v) + ); + } + + /** + * @notice Receive a transfer with a signed authorization from the payer + * @dev This has an additional check to ensure that the payee's address + * matches the caller of this function to prevent front-running attacks. + * EOA wallet signatures should be packed in the order of r, s, v. + * @param from Payer's address (Authorizer) + * @param to Payee's address + * @param value Amount to be transferred + * @param validAfter The time after which this is valid (unix time) + * @param validBefore The time before which this is valid (unix time) + * @param nonce Unique nonce + * @param signature Signature byte array produced by an EOA wallet or a contract wallet + */ + function _receiveWithAuthorization( + address from, + address to, + uint256 value, + uint256 validAfter, + uint256 validBefore, + bytes32 nonce, + bytes memory signature + ) internal { + require(to == msg.sender, "FiatTokenV2: caller must be the payee"); + _requireValidAuthorization(from, nonce, validAfter, validBefore); + _requireValidSignature( + from, + keccak256( + abi.encode( + RECEIVE_WITH_AUTHORIZATION_TYPEHASH, + from, + to, + value, + validAfter, + validBefore, + nonce + ) + ), + signature + ); + + _markAuthorizationAsUsed(from, nonce); + _transfer(from, to, value); + } + + /** + * @notice Attempt to cancel an authorization + * @param authorizer Authorizer's address + * @param nonce Nonce of the authorization + * @param v v of the signature + * @param r r of the signature + * @param s s of the signature + */ + function _cancelAuthorization( + address authorizer, + bytes32 nonce, + uint8 v, + bytes32 r, + bytes32 s + ) internal { + _cancelAuthorization(authorizer, nonce, abi.encodePacked(r, s, v)); + } + + /** + * @notice Attempt to cancel an authorization + * @dev EOA wallet signatures should be packed in the order of r, s, v. + * @param authorizer Authorizer's address + * @param nonce Nonce of the authorization + * @param signature Signature byte array produced by an EOA wallet or a contract wallet + */ + function _cancelAuthorization( + address authorizer, + bytes32 nonce, + bytes memory signature + ) internal { + _requireUnusedAuthorization(authorizer, nonce); + _requireValidSignature( + authorizer, + keccak256( + abi.encode(CANCEL_AUTHORIZATION_TYPEHASH, authorizer, nonce) + ), + signature + ); + + _authorizationStates[authorizer][nonce] = true; + emit AuthorizationCanceled(authorizer, nonce); + } + + /** + * @notice Validates that signature against input data struct + * @param signer Signer's address + * @param dataHash Hash of encoded data struct + * @param signature Signature byte array produced by an EOA wallet or a contract wallet + */ + function _requireValidSignature( + address signer, + bytes32 dataHash, + bytes memory signature + ) private view { + require( + SignatureChecker.isValidSignatureNow( + signer, + MessageHashUtils.toTypedDataHash(_domainSeparator(), dataHash), + signature + ), + "FiatTokenV2: invalid signature" + ); + } + + /** + * @notice Check that an authorization is unused + * @param authorizer Authorizer's address + * @param nonce Nonce of the authorization + */ + function _requireUnusedAuthorization(address authorizer, bytes32 nonce) + private + view + { + require( + !_authorizationStates[authorizer][nonce], + "FiatTokenV2: authorization is used or canceled" + ); + } + + /** + * @notice Check that authorization is valid + * @param authorizer Authorizer's address + * @param nonce Nonce of the authorization + * @param validAfter The time after which this is valid (unix time) + * @param validBefore The time before which this is valid (unix time) + */ + function _requireValidAuthorization( + address authorizer, + bytes32 nonce, + uint256 validAfter, + uint256 validBefore + ) private view { + require( + now > validAfter, + "FiatTokenV2: authorization is not yet valid" + ); + require(now < validBefore, "FiatTokenV2: authorization is expired"); + _requireUnusedAuthorization(authorizer, nonce); + } + + /** + * @notice Mark an authorization as used + * @param authorizer Authorizer's address + * @param nonce Nonce of the authorization + */ + function _markAuthorizationAsUsed(address authorizer, bytes32 nonce) + private + { + _authorizationStates[authorizer][nonce] = true; + emit AuthorizationUsed(authorizer, nonce); + } +} + +// contracts/v1.1/FiatTokenV1_1.sol +/** + * Copyright 2023 Circle Internet Group, Inc. All rights reserved. + * + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @title FiatTokenV1_1 + * @dev ERC20 Token backed by fiat reserves + */ +contract FiatTokenV1_1 is FiatTokenV1, Rescuable { + +} + +// contracts/v2/FiatTokenV2.sol +/** + * Copyright 2023 Circle Internet Group, Inc. All rights reserved. + * + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @title FiatToken V2 + * @notice ERC20 Token backed by fiat reserves, version 2 + */ +contract FiatTokenV2 is FiatTokenV1_1, EIP3009, EIP2612 { + uint8 internal _initializedVersion; + + /** + * @notice Initialize v2 + * @param newName New token name + */ + function initializeV2(string calldata newName) external { + // solhint-disable-next-line reason-string + require(initialized && _initializedVersion == 0); + name = newName; + _DEPRECATED_CACHED_DOMAIN_SEPARATOR = EIP712.makeDomainSeparator( + newName, + "2" + ); + _initializedVersion = 1; + } + + /** + * @notice Increase the allowance by a given increment + * @param spender Spender's address + * @param increment Amount of increase in allowance + * @return True if successful + */ + function increaseAllowance(address spender, uint256 increment) + external + virtual + whenNotPaused + notBlacklisted(msg.sender) + notBlacklisted(spender) + returns (bool) + { + _increaseAllowance(msg.sender, spender, increment); + return true; + } + + /** + * @notice Decrease the allowance by a given decrement + * @param spender Spender's address + * @param decrement Amount of decrease in allowance + * @return True if successful + */ + function decreaseAllowance(address spender, uint256 decrement) + external + virtual + whenNotPaused + notBlacklisted(msg.sender) + notBlacklisted(spender) + returns (bool) + { + _decreaseAllowance(msg.sender, spender, decrement); + return true; + } + + /** + * @notice Execute a transfer with a signed authorization + * @param from Payer's address (Authorizer) + * @param to Payee's address + * @param value Amount to be transferred + * @param validAfter The time after which this is valid (unix time) + * @param validBefore The time before which this is valid (unix time) + * @param nonce Unique nonce + * @param v v of the signature + * @param r r of the signature + * @param s s of the signature + */ + function transferWithAuthorization( + address from, + address to, + uint256 value, + uint256 validAfter, + uint256 validBefore, + bytes32 nonce, + uint8 v, + bytes32 r, + bytes32 s + ) external whenNotPaused notBlacklisted(from) notBlacklisted(to) { + _transferWithAuthorization( + from, + to, + value, + validAfter, + validBefore, + nonce, + v, + r, + s + ); + } + + /** + * @notice Receive a transfer with a signed authorization from the payer + * @dev This has an additional check to ensure that the payee's address + * matches the caller of this function to prevent front-running attacks. + * @param from Payer's address (Authorizer) + * @param to Payee's address + * @param value Amount to be transferred + * @param validAfter The time after which this is valid (unix time) + * @param validBefore The time before which this is valid (unix time) + * @param nonce Unique nonce + * @param v v of the signature + * @param r r of the signature + * @param s s of the signature + */ + function receiveWithAuthorization( + address from, + address to, + uint256 value, + uint256 validAfter, + uint256 validBefore, + bytes32 nonce, + uint8 v, + bytes32 r, + bytes32 s + ) external whenNotPaused notBlacklisted(from) notBlacklisted(to) { + _receiveWithAuthorization( + from, + to, + value, + validAfter, + validBefore, + nonce, + v, + r, + s + ); + } + + /** + * @notice Attempt to cancel an authorization + * @dev Works only if the authorization is not yet used. + * @param authorizer Authorizer's address + * @param nonce Nonce of the authorization + * @param v v of the signature + * @param r r of the signature + * @param s s of the signature + */ + function cancelAuthorization( + address authorizer, + bytes32 nonce, + uint8 v, + bytes32 r, + bytes32 s + ) external whenNotPaused { + _cancelAuthorization(authorizer, nonce, v, r, s); + } + + /** + * @notice Update allowance with a signed permit + * @param owner Token owner's address (Authorizer) + * @param spender Spender's address + * @param value Amount of allowance + * @param deadline The time at which the signature expires (unix time), or max uint256 value to signal no expiration + * @param v v of the signature + * @param r r of the signature + * @param s s of the signature + */ + function permit( + address owner, + address spender, + uint256 value, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) + external + virtual + whenNotPaused + notBlacklisted(owner) + notBlacklisted(spender) + { + _permit(owner, spender, value, deadline, v, r, s); + } + + /** + * @dev Internal function to increase the allowance by a given increment + * @param owner Token owner's address + * @param spender Spender's address + * @param increment Amount of increase + */ + function _increaseAllowance( + address owner, + address spender, + uint256 increment + ) internal override { + _approve(owner, spender, allowed[owner][spender].add(increment)); + } + + /** + * @dev Internal function to decrease the allowance by a given decrement + * @param owner Token owner's address + * @param spender Spender's address + * @param decrement Amount of decrease + */ + function _decreaseAllowance( + address owner, + address spender, + uint256 decrement + ) internal override { + _approve( + owner, + spender, + allowed[owner][spender].sub( + decrement, + "ERC20: decreased allowance below zero" + ) + ); + } +} + +// contracts/v2/FiatTokenV2_1.sol +/** + * Copyright 2023 Circle Internet Group, Inc. All rights reserved. + * + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// solhint-disable func-name-mixedcase + +/** + * @title FiatToken V2.1 + * @notice ERC20 Token backed by fiat reserves, version 2.1 + */ +contract FiatTokenV2_1 is FiatTokenV2 { + /** + * @notice Initialize v2.1 + * @param lostAndFound The address to which the locked funds are sent + */ + function initializeV2_1(address lostAndFound) external { + // solhint-disable-next-line reason-string + require(_initializedVersion == 1); + + uint256 lockedAmount = _balanceOf(address(this)); + if (lockedAmount > 0) { + _transfer(address(this), lostAndFound, lockedAmount); + } + _blacklist(address(this)); + + _initializedVersion = 2; + } + + /** + * @notice Version string for the EIP712 domain separator + * @return Version string + */ + function version() external pure returns (string memory) { + return "2"; + } +} + +// contracts/v2/FiatTokenV2_2.sol +/** + * Copyright 2023 Circle Internet Group, Inc. All rights reserved. + * + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + // solhint-disable-line no-unused-import + // solhint-disable-line no-unused-import + // solhint-disable-line no-unused-import + // solhint-disable-line no-unused-import + +// solhint-disable func-name-mixedcase + +/** + * @title FiatToken V2.2 + * @notice ERC20 Token backed by fiat reserves, version 2.2 + */ +contract FiatTokenV2_2 is FiatTokenV2_1 { + /** + * @notice Initialize v2.2 + * @param accountsToBlacklist A list of accounts to migrate from the old blacklist + * @param newSymbol New token symbol + * data structure to the new blacklist data structure. + */ + function initializeV2_2( + address[] calldata accountsToBlacklist, + string calldata newSymbol + ) external { + // solhint-disable-next-line reason-string + require(_initializedVersion == 2); + + // Update fiat token symbol + symbol = newSymbol; + + // Add previously blacklisted accounts to the new blacklist data structure + // and remove them from the old blacklist data structure. + for (uint256 i = 0; i < accountsToBlacklist.length; i++) { + require( + _deprecatedBlacklisted[accountsToBlacklist[i]], + "FiatTokenV2_2: Blacklisting previously unblacklisted account!" + ); + _blacklist(accountsToBlacklist[i]); + delete _deprecatedBlacklisted[accountsToBlacklist[i]]; + } + _blacklist(address(this)); + delete _deprecatedBlacklisted[address(this)]; + + _initializedVersion = 3; + } + + /** + * @dev Internal function to get the current chain id. + * @return The current chain id. + */ + function _chainId() internal virtual view returns (uint256) { + uint256 chainId; + assembly { + chainId := chainid() + } + return chainId; + } + + /** + * + */ + function _domainSeparator() internal override view returns (bytes32) { + return EIP712.makeDomainSeparator(name, "2", _chainId()); + } + + /** + * @notice Update allowance with a signed permit + * @dev EOA wallet signatures should be packed in the order of r, s, v. + * @param owner Token owner's address (Authorizer) + * @param spender Spender's address + * @param value Amount of allowance + * @param deadline The time at which the signature expires (unix time), or max uint256 value to signal no expiration + * @param signature Signature bytes signed by an EOA wallet or a contract wallet + */ + function permit( + address owner, + address spender, + uint256 value, + uint256 deadline, + bytes memory signature + ) external whenNotPaused { + _permit(owner, spender, value, deadline, signature); + } + + /** + * @notice Execute a transfer with a signed authorization + * @dev EOA wallet signatures should be packed in the order of r, s, v. + * @param from Payer's address (Authorizer) + * @param to Payee's address + * @param value Amount to be transferred + * @param validAfter The time after which this is valid (unix time) + * @param validBefore The time before which this is valid (unix time) + * @param nonce Unique nonce + * @param signature Signature bytes signed by an EOA wallet or a contract wallet + */ + function transferWithAuthorization( + address from, + address to, + uint256 value, + uint256 validAfter, + uint256 validBefore, + bytes32 nonce, + bytes memory signature + ) external whenNotPaused notBlacklisted(from) notBlacklisted(to) { + _transferWithAuthorization( + from, + to, + value, + validAfter, + validBefore, + nonce, + signature + ); + } + + /** + * @notice Receive a transfer with a signed authorization from the payer + * @dev This has an additional check to ensure that the payee's address + * matches the caller of this function to prevent front-running attacks. + * EOA wallet signatures should be packed in the order of r, s, v. + * @param from Payer's address (Authorizer) + * @param to Payee's address + * @param value Amount to be transferred + * @param validAfter The time after which this is valid (unix time) + * @param validBefore The time before which this is valid (unix time) + * @param nonce Unique nonce + * @param signature Signature bytes signed by an EOA wallet or a contract wallet + */ + function receiveWithAuthorization( + address from, + address to, + uint256 value, + uint256 validAfter, + uint256 validBefore, + bytes32 nonce, + bytes memory signature + ) external whenNotPaused notBlacklisted(from) notBlacklisted(to) { + _receiveWithAuthorization( + from, + to, + value, + validAfter, + validBefore, + nonce, + signature + ); + } + + /** + * @notice Attempt to cancel an authorization + * @dev Works only if the authorization is not yet used. + * EOA wallet signatures should be packed in the order of r, s, v. + * @param authorizer Authorizer's address + * @param nonce Nonce of the authorization + * @param signature Signature bytes signed by an EOA wallet or a contract wallet + */ + function cancelAuthorization( + address authorizer, + bytes32 nonce, + bytes memory signature + ) external whenNotPaused { + _cancelAuthorization(authorizer, nonce, signature); + } + + /** + * @dev Helper method that sets the blacklist state of an account on balanceAndBlacklistStates. + * If _shouldBlacklist is true, we apply a (1 << 255) bitmask with an OR operation on the + * account's balanceAndBlacklistState. This flips the high bit for the account to 1, + * indicating that the account is blacklisted. + * + * If _shouldBlacklist if false, we reset the account's balanceAndBlacklistStates to their + * balances. This clears the high bit for the account, indicating that the account is unblacklisted. + * @param _account The address of the account. + * @param _shouldBlacklist True if the account should be blacklisted, false if the account should be unblacklisted. + */ + function _setBlacklistState(address _account, bool _shouldBlacklist) + internal + override + { + balanceAndBlacklistStates[_account] = _shouldBlacklist + ? balanceAndBlacklistStates[_account] | (1 << 255) + : _balanceOf(_account); + } + + /** + * @dev Helper method that sets the balance of an account on balanceAndBlacklistStates. + * Since balances are stored in the last 255 bits of the balanceAndBlacklistStates value, + * we need to ensure that the updated balance does not exceed (2^255 - 1). + * Since blacklisted accounts' balances cannot be updated, the method will also + * revert if the account is blacklisted + * @param _account The address of the account. + * @param _balance The new fiat token balance of the account (max: (2^255 - 1)). + */ + function _setBalance(address _account, uint256 _balance) internal override { + require( + _balance <= ((1 << 255) - 1), + "FiatTokenV2_2: Balance exceeds (2^255 - 1)" + ); + require( + !_isBlacklisted(_account), + "FiatTokenV2_2: Account is blacklisted" + ); + + balanceAndBlacklistStates[_account] = _balance; + } + + /** + * + */ + function _isBlacklisted(address _account) + internal + override + view + returns (bool) + { + return balanceAndBlacklistStates[_account] >> 255 == 1; + } + + /** + * @dev Helper method to obtain the balance of an account. Since balances + * are stored in the last 255 bits of the balanceAndBlacklistStates value, + * we apply a ((1 << 255) - 1) bit bitmask with an AND operation on the + * balanceAndBlacklistState to obtain the balance. + * @param _account The address of the account. + * @return The fiat token balance of the account. + */ + function _balanceOf(address _account) + internal + override + view + returns (uint256) + { + return balanceAndBlacklistStates[_account] & ((1 << 255) - 1); + } + + /** + * + */ + function approve(address spender, uint256 value) + external + override + whenNotPaused + returns (bool) + { + _approve(msg.sender, spender, value); + return true; + } + + /** + * + */ + function permit( + address owner, + address spender, + uint256 value, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) external override whenNotPaused { + _permit(owner, spender, value, deadline, v, r, s); + } + + /** + * + */ + function increaseAllowance(address spender, uint256 increment) + external + override + whenNotPaused + returns (bool) + { + _increaseAllowance(msg.sender, spender, increment); + return true; + } + + /** + * + */ + function decreaseAllowance(address spender, uint256 decrement) + external + override + whenNotPaused + returns (bool) + { + _decreaseAllowance(msg.sender, spender, decrement); + return true; + } +} \ No newline at end of file diff --git a/contracts/original/usdc_proxy.sol b/contracts/original/usdc_proxy.sol new file mode 100644 index 0000000..50272d5 --- /dev/null +++ b/contracts/original/usdc_proxy.sol @@ -0,0 +1,576 @@ +// * SPDX-License-Identifier: Apache-2.0 +pragma solidity <0.8.0 =0.6.12 >=0.6.2; + +// contracts/upgradeability/Proxy.sol +/** + * Copyright 2023 Circle Internet Group, Inc. All rights reserved. + * + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @notice Implements delegation of calls to other contracts, with proper + * forwarding of return values and bubbling of failures. + * It defines a fallback function that delegates all calls to the address + * returned by the abstract _implementation() internal function. + * @dev Forked from https://github.com/zeppelinos/zos-lib/blob/8a16ef3ad17ec7430e3a9d2b5e3f39b8204f8c8d/contracts/upgradeability/Proxy.sol + * Modifications: + * 1. Reformat and conform to Solidity 0.6 syntax (5/13/20) + */ +abstract contract Proxy { + /** + * @dev Fallback function. + * Implemented entirely in `_fallback`. + */ + fallback() external payable { + _fallback(); + } + + /** + * @return The Address of the implementation. + */ + function _implementation() internal virtual view returns (address); + + /** + * @dev Delegates execution to an implementation contract. + * This is a low level function that doesn't return to its internal call site. + * It will return to the external caller whatever the implementation returns. + * @param implementation Address to delegate. + */ + function _delegate(address implementation) internal { + assembly { + // Copy msg.data. We take full control of memory in this inline assembly + // block because it will not return to Solidity code. We overwrite the + // Solidity scratch pad at memory position 0. + calldatacopy(0, 0, calldatasize()) + + // Call the implementation. + // out and outsize are 0 because we don't know the size yet. + let result := delegatecall( + gas(), + implementation, + 0, + calldatasize(), + 0, + 0 + ) + + // Copy the returned data. + returndatacopy(0, 0, returndatasize()) + + switch result + // delegatecall returns 0 on error. + case 0 { + revert(0, returndatasize()) + } + default { + return(0, returndatasize()) + } + } + } + + /** + * @dev Function that is run as the first thing in the fallback function. + * Can be redefined in derived contracts to add functionality. + * Redefinitions must call super._willFallback(). + */ + function _willFallback() internal virtual {} + + /** + * @dev fallback implementation. + * Extracted to enable manual triggering. + */ + function _fallback() internal { + _willFallback(); + _delegate(_implementation()); + } +} + +// node_modules/@openzeppelin/contracts/utils/Address.sol + +/** + * @dev Collection of functions related to the address type + */ +library Address { + /** + * @dev Returns true if `account` is a contract. + * + * [IMPORTANT] + * ==== + * It is unsafe to assume that an address for which this function returns + * false is an externally-owned account (EOA) and not a contract. + * + * Among others, `isContract` will return false for the following + * types of addresses: + * + * - an externally-owned account + * - a contract in construction + * - an address where a contract will be created + * - an address where a contract lived, but was destroyed + * ==== + */ + function isContract(address account) internal view returns (bool) { + // This method relies on extcodesize, which returns 0 for contracts in + // construction, since the code is only stored at the end of the + // constructor execution. + + uint256 size; + // solhint-disable-next-line no-inline-assembly + assembly { size := extcodesize(account) } + return size > 0; + } + + /** + * @dev Replacement for Solidity's `transfer`: sends `amount` wei to + * `recipient`, forwarding all available gas and reverting on errors. + * + * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost + * of certain opcodes, possibly making contracts go over the 2300 gas limit + * imposed by `transfer`, making them unable to receive funds via + * `transfer`. {sendValue} removes this limitation. + * + * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. + * + * IMPORTANT: because control is transferred to `recipient`, care must be + * taken to not create reentrancy vulnerabilities. Consider using + * {ReentrancyGuard} or the + * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. + */ + function sendValue(address payable recipient, uint256 amount) internal { + require(address(this).balance >= amount, "Address: insufficient balance"); + + // solhint-disable-next-line avoid-low-level-calls, avoid-call-value + (bool success, ) = recipient.call{ value: amount }(""); + require(success, "Address: unable to send value, recipient may have reverted"); + } + + /** + * @dev Performs a Solidity function call using a low level `call`. A + * plain`call` is an unsafe replacement for a function call: use this + * function instead. + * + * If `target` reverts with a revert reason, it is bubbled up by this + * function (like regular Solidity function calls). + * + * Returns the raw returned data. To convert to the expected return value, + * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. + * + * Requirements: + * + * - `target` must be a contract. + * - calling `target` with `data` must not revert. + * + * _Available since v3.1._ + */ + function functionCall(address target, bytes memory data) internal returns (bytes memory) { + return functionCall(target, data, "Address: low-level call failed"); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with + * `errorMessage` as a fallback revert reason when `target` reverts. + * + * _Available since v3.1._ + */ + function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { + return functionCallWithValue(target, data, 0, errorMessage); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], + * but also transferring `value` wei to `target`. + * + * Requirements: + * + * - the calling contract must have an ETH balance of at least `value`. + * - the called Solidity function must be `payable`. + * + * _Available since v3.1._ + */ + function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { + return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); + } + + /** + * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but + * with `errorMessage` as a fallback revert reason when `target` reverts. + * + * _Available since v3.1._ + */ + function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) { + require(address(this).balance >= value, "Address: insufficient balance for call"); + require(isContract(target), "Address: call to non-contract"); + + // solhint-disable-next-line avoid-low-level-calls + (bool success, bytes memory returndata) = target.call{ value: value }(data); + return _verifyCallResult(success, returndata, errorMessage); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], + * but performing a static call. + * + * _Available since v3.3._ + */ + function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { + return functionStaticCall(target, data, "Address: low-level static call failed"); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], + * but performing a static call. + * + * _Available since v3.3._ + */ + function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) { + require(isContract(target), "Address: static call to non-contract"); + + // solhint-disable-next-line avoid-low-level-calls + (bool success, bytes memory returndata) = target.staticcall(data); + return _verifyCallResult(success, returndata, errorMessage); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], + * but performing a delegate call. + * + * _Available since v3.4._ + */ + function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { + return functionDelegateCall(target, data, "Address: low-level delegate call failed"); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], + * but performing a delegate call. + * + * _Available since v3.4._ + */ + function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { + require(isContract(target), "Address: delegate call to non-contract"); + + // solhint-disable-next-line avoid-low-level-calls + (bool success, bytes memory returndata) = target.delegatecall(data); + return _verifyCallResult(success, returndata, errorMessage); + } + + function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) { + if (success) { + return returndata; + } else { + // Look for revert reason and bubble it up if present + if (returndata.length > 0) { + // The easiest way to bubble the revert reason is using memory via assembly + + // solhint-disable-next-line no-inline-assembly + assembly { + let returndata_size := mload(returndata) + revert(add(32, returndata), returndata_size) + } + } else { + revert(errorMessage); + } + } + } +} + +// contracts/upgradeability/UpgradeabilityProxy.sol +/** + * Copyright 2023 Circle Internet Group, Inc. All rights reserved. + * + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @notice This contract implements a proxy that allows to change the + * implementation address to which it will delegate. + * Such a change is called an implementation upgrade. + * @dev Forked from https://github.com/zeppelinos/zos-lib/blob/8a16ef3ad17ec7430e3a9d2b5e3f39b8204f8c8d/contracts/upgradeability/UpgradeabilityProxy.sol + * Modifications: + * 1. Reformat, conform to Solidity 0.6 syntax, and add error messages (5/13/20) + * 2. Use Address utility library from the latest OpenZeppelin (5/13/20) + */ +contract UpgradeabilityProxy is Proxy { + /** + * @dev Emitted when the implementation is upgraded. + * @param implementation Address of the new implementation. + */ + event Upgraded(address implementation); + + /** + * @dev Storage slot with the address of the current implementation. + * This is the keccak-256 hash of "org.zeppelinos.proxy.implementation", and is + * validated in the constructor. + */ + bytes32 + private constant IMPLEMENTATION_SLOT = 0x7050c9e0f4ca769c69bd3a8ef740bc37934f8e2c036e5a723fd8ee048ed3f8c3; + + /** + * @dev Contract constructor. + * @param implementationContract Address of the initial implementation. + */ + constructor(address implementationContract) public { + assert( + IMPLEMENTATION_SLOT == + keccak256("org.zeppelinos.proxy.implementation") + ); + + _setImplementation(implementationContract); + } + + /** + * @dev Returns the current implementation. + * @return impl Address of the current implementation + */ + function _implementation() internal override view returns (address impl) { + bytes32 slot = IMPLEMENTATION_SLOT; + assembly { + impl := sload(slot) + } + } + + /** + * @dev Upgrades the proxy to a new implementation. + * @param newImplementation Address of the new implementation. + */ + function _upgradeTo(address newImplementation) internal { + _setImplementation(newImplementation); + emit Upgraded(newImplementation); + } + + /** + * @dev Sets the implementation address of the proxy. + * @param newImplementation Address of the new implementation. + */ + function _setImplementation(address newImplementation) private { + require( + Address.isContract(newImplementation), + "Cannot set a proxy implementation to a non-contract address" + ); + + bytes32 slot = IMPLEMENTATION_SLOT; + + assembly { + sstore(slot, newImplementation) + } + } +} + +// contracts/upgradeability/AdminUpgradeabilityProxy.sol +/** + * Copyright 2023 Circle Internet Group, Inc. All rights reserved. + * + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @notice This contract combines an upgradeability proxy with an authorization + * mechanism for administrative tasks. + * @dev Forked from https://github.com/zeppelinos/zos-lib/blob/8a16ef3ad17ec7430e3a9d2b5e3f39b8204f8c8d/contracts/upgradeability/AdminUpgradeabilityProxy.sol + * Modifications: + * 1. Reformat, conform to Solidity 0.6 syntax, and add error messages (5/13/20) + * 2. Remove ifAdmin modifier from admin() and implementation() (5/13/20) + */ +contract AdminUpgradeabilityProxy is UpgradeabilityProxy { + /** + * @dev Emitted when the administration has been transferred. + * @param previousAdmin Address of the previous admin. + * @param newAdmin Address of the new admin. + */ + event AdminChanged(address previousAdmin, address newAdmin); + + /** + * @dev Storage slot with the admin of the contract. + * This is the keccak-256 hash of "org.zeppelinos.proxy.admin", and is + * validated in the constructor. + */ + bytes32 + private constant ADMIN_SLOT = 0x10d6a54a4754c8869d6886b5f5d7fbfa5b4522237ea5c60d11bc4e7a1ff9390b; + + /** + * @dev Modifier to check whether the `msg.sender` is the admin. + * If it is, it will run the function. Otherwise, it will delegate the call + * to the implementation. + */ + modifier ifAdmin() { + if (msg.sender == _admin()) { + _; + } else { + _fallback(); + } + } + + /** + * @dev Contract constructor. + * It sets the `msg.sender` as the proxy administrator. + * @param implementationContract address of the initial implementation. + */ + constructor(address implementationContract) + public + UpgradeabilityProxy(implementationContract) + { + assert(ADMIN_SLOT == keccak256("org.zeppelinos.proxy.admin")); + + _setAdmin(msg.sender); + } + + /** + * @return The address of the proxy admin. + */ + function admin() external view returns (address) { + return _admin(); + } + + /** + * @return The address of the implementation. + */ + function implementation() external view returns (address) { + return _implementation(); + } + + /** + * @dev Changes the admin of the proxy. + * Only the current admin can call this function. + * @param newAdmin Address to transfer proxy administration to. + */ + function changeAdmin(address newAdmin) external ifAdmin { + require( + newAdmin != address(0), + "Cannot change the admin of a proxy to the zero address" + ); + emit AdminChanged(_admin(), newAdmin); + _setAdmin(newAdmin); + } + + /** + * @dev Upgrade the backing implementation of the proxy. + * Only the admin can call this function. + * @param newImplementation Address of the new implementation. + */ + function upgradeTo(address newImplementation) external ifAdmin { + _upgradeTo(newImplementation); + } + + /** + * @dev Upgrade the backing implementation of the proxy and call a function + * on the new implementation. + * This is useful to initialize the proxied contract. + * @param newImplementation Address of the new implementation. + * @param data Data to send as msg.data in the low level call. + * It should include the signature and the parameters of the function to be + * called, as described in + * https://solidity.readthedocs.io/en/develop/abi-spec.html#function-selector-and-argument-encoding. + */ + function upgradeToAndCall(address newImplementation, bytes calldata data) + external + payable + ifAdmin + { + _upgradeTo(newImplementation); + // prettier-ignore + // solhint-disable-next-line avoid-low-level-calls + (bool success,) = address(this).call{value: msg.value}(data); + // solhint-disable-next-line reason-string + require(success); + } + + /** + * @return adm The admin slot. + */ + function _admin() internal view returns (address adm) { + bytes32 slot = ADMIN_SLOT; + + assembly { + adm := sload(slot) + } + } + + /** + * @dev Sets the address of the proxy admin. + * @param newAdmin Address of the new proxy admin. + */ + function _setAdmin(address newAdmin) internal { + bytes32 slot = ADMIN_SLOT; + + assembly { + sstore(slot, newAdmin) + } + } + + /** + * @dev Only fall back when the sender is not the admin. + */ + function _willFallback() internal override { + require( + msg.sender != _admin(), + "Cannot call fallback function from the proxy admin" + ); + super._willFallback(); + } +} + +// contracts/v1/FiatTokenProxy.sol +/** + * Copyright 2023 Circle Internet Group, Inc. All rights reserved. + * + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @title FiatTokenProxy + * @dev This contract proxies FiatToken calls and enables FiatToken upgrades + */ +contract FiatTokenProxy is AdminUpgradeabilityProxy { + constructor(address implementationContract) + public + AdminUpgradeabilityProxy(implementationContract) + {} +} \ No newline at end of file diff --git a/contracts/original/weth.sol b/contracts/original/weth.sol new file mode 100644 index 0000000..e2e47c2 --- /dev/null +++ b/contracts/original/weth.sol @@ -0,0 +1,760 @@ +/** + *Submitted for verification at Etherscan.io on 2017-12-12 +*/ + +// Copyright (C) 2015, 2016, 2017 Dapphub + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +pragma solidity ^0.4.18; + +contract WETH9 { + string public name = "Wrapped Ether"; + string public symbol = "WETH"; + uint8 public decimals = 18; + + event Approval(address indexed src, address indexed guy, uint wad); + event Transfer(address indexed src, address indexed dst, uint wad); + event Deposit(address indexed dst, uint wad); + event Withdrawal(address indexed src, uint wad); + + mapping (address => uint) public balanceOf; + mapping (address => mapping (address => uint)) public allowance; + + function() public payable { + deposit(); + } + function deposit() public payable { + balanceOf[msg.sender] += msg.value; + Deposit(msg.sender, msg.value); + } + function withdraw(uint wad) public { + require(balanceOf[msg.sender] >= wad); + balanceOf[msg.sender] -= wad; + msg.sender.transfer(wad); + Withdrawal(msg.sender, wad); + } + + function totalSupply() public view returns (uint) { + return this.balance; + } + + function approve(address guy, uint wad) public returns (bool) { + allowance[msg.sender][guy] = wad; + Approval(msg.sender, guy, wad); + return true; + } + + function transfer(address dst, uint wad) public returns (bool) { + return transferFrom(msg.sender, dst, wad); + } + + function transferFrom(address src, address dst, uint wad) + public + returns (bool) + { + require(balanceOf[src] >= wad); + + if (src != msg.sender && allowance[src][msg.sender] != uint(-1)) { + require(allowance[src][msg.sender] >= wad); + allowance[src][msg.sender] -= wad; + } + + balanceOf[src] -= wad; + balanceOf[dst] += wad; + + Transfer(src, dst, wad); + + return true; + } +} + + +/* + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. + +*/ \ No newline at end of file diff --git a/contracts/original/xen_minter.sol b/contracts/original/xen_minter.sol new file mode 100644 index 0000000..3c30c3b --- /dev/null +++ b/contracts/original/xen_minter.sol @@ -0,0 +1,169 @@ +/** + *Submitted for verification at Etherscan.io on 2022-10-13 +*/ + +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +/* +//https://cointool.app web3 basic tools! +// +// +// _____ _ _______ _ +// / ____| (_) |__ __| | | /\ +//| | ___ _ _ __ | | ___ ___ | | / \ _ __ _ __ +//| | / _ \| | '_ \| |/ _ \ / _ \| | / /\ \ | '_ \| '_ \ +//| |___| (_) | | | | | | (_) | (_) | |_ / ____ \| |_) | |_) | +// \_____\___/|_|_| |_|_|\___/ \___/|_(_)_/ \_\ .__/| .__/ +// | | | | +// |_| |_| +// +*/ + + +interface IERC20 { + function balanceOf(address account) external view returns (uint256); + function transfer(address to, uint256 amount) external returns (bool); +} + +contract CoinTool_App{ + address owner; + address private immutable original; + mapping(address => mapping(bytes =>uint256)) public map; + + constructor() payable { + original = address(this); + owner = tx.origin; + } + receive() external payable {} + fallback() external payable{} + + function t(uint256 total,bytes memory data,bytes calldata _salt) external payable { + require(msg.sender == tx.origin); + bytes memory bytecode = bytes.concat(bytes20(0x3D602d80600A3D3981F3363d3d373d3D3D363d73), bytes20(address(this)), bytes15(0x5af43d82803e903d91602b57fd5bf3)); + uint256 i = map[msg.sender][_salt]+1; + uint256 end = total+i; + for (i; i < end;++i) { + bytes32 salt = keccak256(abi.encodePacked(_salt,i,msg.sender)); + assembly { + let proxy := create2(0, add(bytecode, 32), mload(bytecode), salt) + let succeeded := call( + gas(), + proxy, + 0, + add(data, 0x20), + mload(data), + 0, + 0 + ) + } + } + map[msg.sender][_salt] += total; + } + + + function t_(uint256[] calldata a,bytes memory data,bytes calldata _salt) external payable { + require(msg.sender == tx.origin); + bytes memory bytecode = bytes.concat(bytes20(0x3D602d80600A3D3981F3363d3d373d3D3D363d73), bytes20(address(this)), bytes15(0x5af43d82803e903d91602b57fd5bf3)); + uint256 i = 0; + for (i; i < a.length; ++i) { + bytes32 salt = keccak256(abi.encodePacked(_salt,a[i],msg.sender)); + assembly { + let proxy := create2(0, add(bytecode, 32), mload(bytecode), salt) + let succeeded := call( + gas(), + proxy, + 0, + add(data, 0x20), + mload(data), + 0, + 0 + ) + } + } + uint256 e = a[a.length-1]; + if(e>map[msg.sender][_salt]){ + map[msg.sender][_salt] = e; + } + } + + function f(uint256[] calldata a,bytes memory data,bytes memory _salt) external payable { + require(msg.sender == tx.origin); + bytes32 bytecode = keccak256(abi.encodePacked(bytes.concat(bytes20(0x3D602d80600A3D3981F3363d3d373d3D3D363d73), bytes20(address(this)), bytes15(0x5af43d82803e903d91602b57fd5bf3)))); + uint256 i = 0; + for (i; i < a.length; ++i) { + bytes32 salt = keccak256(abi.encodePacked(_salt,a[i],msg.sender)); + address proxy = address(uint160(uint(keccak256(abi.encodePacked( + hex'ff', + address(this), + salt, + bytecode + ))))); + assembly { + let succeeded := call( + gas(), + proxy, + 0, + add(data, 0x20), + mload(data), + 0, + 0 + ) + } + } + } + + + + function d(address a,bytes memory data) external payable{ + require(msg.sender == original); + a.delegatecall(data); + } + function c(address a,bytes calldata data) external payable { + require(msg.sender == original); + external_call(a,data); + } + + function dKill(address a,bytes memory data) external payable{ + require(msg.sender == original); + a.delegatecall(data); + selfdestruct(payable(msg.sender)); + } + function cKill(address a,bytes calldata data) external payable { + require(msg.sender == original); + external_call(a,data); + selfdestruct(payable(msg.sender)); + } + + function k() external { + require(msg.sender == original); + selfdestruct(payable(msg.sender)); + } + + function external_call(address destination,bytes memory data) internal{ + assembly { + let succeeded := call( + gas(), + destination, + 0, + add(data, 0x20), + mload(data), + 0, + 0 + ) + } + } + + + function claimTokens(address _token) external { + require(owner == msg.sender); + if (_token == address(0x0)) { + payable (owner).transfer(address(this).balance); + return; + } + IERC20 erc20token = IERC20(_token); + uint256 balance = erc20token.balanceOf(address(this)); + erc20token.transfer(owner, balance); + } + +} \ No newline at end of file diff --git a/contracts/original/xen_token.sol b/contracts/original/xen_token.sol new file mode 100644 index 0000000..4a6a7c0 --- /dev/null +++ b/contracts/original/xen_token.sol @@ -0,0 +1,3220 @@ + + +// Sources flattened with hardhat v2.21.0 https://hardhat.org + +// SPDX-License-Identifier: BSD-4-Clause AND MIT + +// File @openzeppelin/contracts/utils/introspection/IERC165.sol@v4.9.6 + +// Original license: SPDX_License_Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Interface of the ERC165 standard, as defined in the + * https://eips.ethereum.org/EIPS/eip-165[EIP]. + * + * Implementers can declare support of contract interfaces, which can then be + * queried by others ({ERC165Checker}). + * + * For an implementation, see {ERC165}. + */ +interface IERC165 { + /** + * @dev Returns true if this contract implements the interface defined by + * `interfaceId`. See the corresponding + * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] + * to learn more about how these ids are created. + * + * This function call must use less than 30 000 gas. + */ + function supportsInterface(bytes4 interfaceId) external view returns (bool); +} + + +// File @openzeppelin/contracts/interfaces/IERC165.sol@v4.9.6 + +// Original license: SPDX_License_Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (interfaces/IERC165.sol) + +pragma solidity ^0.8.0; + + +// File @openzeppelin/contracts/token/ERC20/IERC20.sol@v4.9.6 + +// Original license: SPDX_License_Identifier: MIT +// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Interface of the ERC20 standard as defined in the EIP. + */ +interface IERC20 { + /** + * @dev Emitted when `value` tokens are moved from one account (`from`) to + * another (`to`). + * + * Note that `value` may be zero. + */ + event Transfer(address indexed from, address indexed to, uint256 value); + + /** + * @dev Emitted when the allowance of a `spender` for an `owner` is set by + * a call to {approve}. `value` is the new allowance. + */ + event Approval(address indexed owner, address indexed spender, uint256 value); + + /** + * @dev Returns the amount of tokens in existence. + */ + function totalSupply() external view returns (uint256); + + /** + * @dev Returns the amount of tokens owned by `account`. + */ + function balanceOf(address account) external view returns (uint256); + + /** + * @dev Moves `amount` tokens from the caller's account to `to`. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * Emits a {Transfer} event. + */ + function transfer(address to, uint256 amount) external returns (bool); + + /** + * @dev Returns the remaining number of tokens that `spender` will be + * allowed to spend on behalf of `owner` through {transferFrom}. This is + * zero by default. + * + * This value changes when {approve} or {transferFrom} are called. + */ + function allowance(address owner, address spender) external view returns (uint256); + + /** + * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * IMPORTANT: Beware that changing an allowance with this method brings the risk + * that someone may use both the old and the new allowance by unfortunate + * transaction ordering. One possible solution to mitigate this race + * condition is to first reduce the spender's allowance to 0 and set the + * desired value afterwards: + * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 + * + * Emits an {Approval} event. + */ + function approve(address spender, uint256 amount) external returns (bool); + + /** + * @dev Moves `amount` tokens from `from` to `to` using the + * allowance mechanism. `amount` is then deducted from the caller's + * allowance. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * Emits a {Transfer} event. + */ + function transferFrom(address from, address to, uint256 amount) external returns (bool); +} + + +// File @openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol@v4.9.6 + +// Original license: SPDX_License_Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Interface for the optional metadata functions from the ERC20 standard. + * + * _Available since v4.1._ + */ +interface IERC20Metadata is IERC20 { + /** + * @dev Returns the name of the token. + */ + function name() external view returns (string memory); + + /** + * @dev Returns the symbol of the token. + */ + function symbol() external view returns (string memory); + + /** + * @dev Returns the decimals places of the token. + */ + function decimals() external view returns (uint8); +} + + +// File @openzeppelin/contracts/utils/Context.sol@v4.9.6 + +// Original license: SPDX_License_Identifier: MIT +// OpenZeppelin Contracts (last updated v4.9.4) (utils/Context.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Provides information about the current execution context, including the + * sender of the transaction and its data. While these are generally available + * via msg.sender and msg.data, they should not be accessed in such a direct + * manner, since when dealing with meta-transactions the account sending and + * paying for execution may not be the actual sender (as far as an application + * is concerned). + * + * This contract is only required for intermediate, library-like contracts. + */ +abstract contract Context { + function _msgSender() internal view virtual returns (address) { + return msg.sender; + } + + function _msgData() internal view virtual returns (bytes calldata) { + return msg.data; + } + + function _contextSuffixLength() internal view virtual returns (uint256) { + return 0; + } +} + + +// File @openzeppelin/contracts/token/ERC20/ERC20.sol@v4.9.6 + +// Original license: SPDX_License_Identifier: MIT +// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/ERC20.sol) + +pragma solidity ^0.8.0; + + + +/** + * @dev Implementation of the {IERC20} interface. + * + * This implementation is agnostic to the way tokens are created. This means + * that a supply mechanism has to be added in a derived contract using {_mint}. + * For a generic mechanism see {ERC20PresetMinterPauser}. + * + * TIP: For a detailed writeup see our guide + * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How + * to implement supply mechanisms]. + * + * The default value of {decimals} is 18. To change this, you should override + * this function so it returns a different value. + * + * We have followed general OpenZeppelin Contracts guidelines: functions revert + * instead returning `false` on failure. This behavior is nonetheless + * conventional and does not conflict with the expectations of ERC20 + * applications. + * + * Additionally, an {Approval} event is emitted on calls to {transferFrom}. + * This allows applications to reconstruct the allowance for all accounts just + * by listening to said events. Other implementations of the EIP may not emit + * these events, as it isn't required by the specification. + * + * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} + * functions have been added to mitigate the well-known issues around setting + * allowances. See {IERC20-approve}. + */ +contract ERC20 is Context, IERC20, IERC20Metadata { + mapping(address => uint256) private _balances; + + mapping(address => mapping(address => uint256)) private _allowances; + + uint256 private _totalSupply; + + string private _name; + string private _symbol; + + /** + * @dev Sets the values for {name} and {symbol}. + * + * All two of these values are immutable: they can only be set once during + * construction. + */ + constructor(string memory name_, string memory symbol_) { + _name = name_; + _symbol = symbol_; + } + + /** + * @dev Returns the name of the token. + */ + function name() public view virtual override returns (string memory) { + return _name; + } + + /** + * @dev Returns the symbol of the token, usually a shorter version of the + * name. + */ + function symbol() public view virtual override returns (string memory) { + return _symbol; + } + + /** + * @dev Returns the number of decimals used to get its user representation. + * For example, if `decimals` equals `2`, a balance of `505` tokens should + * be displayed to a user as `5.05` (`505 / 10 ** 2`). + * + * Tokens usually opt for a value of 18, imitating the relationship between + * Ether and Wei. This is the default value returned by this function, unless + * it's overridden. + * + * NOTE: This information is only used for _display_ purposes: it in + * no way affects any of the arithmetic of the contract, including + * {IERC20-balanceOf} and {IERC20-transfer}. + */ + function decimals() public view virtual override returns (uint8) { + return 18; + } + + /** + * @dev See {IERC20-totalSupply}. + */ + function totalSupply() public view virtual override returns (uint256) { + return _totalSupply; + } + + /** + * @dev See {IERC20-balanceOf}. + */ + function balanceOf(address account) public view virtual override returns (uint256) { + return _balances[account]; + } + + /** + * @dev See {IERC20-transfer}. + * + * Requirements: + * + * - `to` cannot be the zero address. + * - the caller must have a balance of at least `amount`. + */ + function transfer(address to, uint256 amount) public virtual override returns (bool) { + address owner = _msgSender(); + _transfer(owner, to, amount); + return true; + } + + /** + * @dev See {IERC20-allowance}. + */ + function allowance(address owner, address spender) public view virtual override returns (uint256) { + return _allowances[owner][spender]; + } + + /** + * @dev See {IERC20-approve}. + * + * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on + * `transferFrom`. This is semantically equivalent to an infinite approval. + * + * Requirements: + * + * - `spender` cannot be the zero address. + */ + function approve(address spender, uint256 amount) public virtual override returns (bool) { + address owner = _msgSender(); + _approve(owner, spender, amount); + return true; + } + + /** + * @dev See {IERC20-transferFrom}. + * + * Emits an {Approval} event indicating the updated allowance. This is not + * required by the EIP. See the note at the beginning of {ERC20}. + * + * NOTE: Does not update the allowance if the current allowance + * is the maximum `uint256`. + * + * Requirements: + * + * - `from` and `to` cannot be the zero address. + * - `from` must have a balance of at least `amount`. + * - the caller must have allowance for ``from``'s tokens of at least + * `amount`. + */ + function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) { + address spender = _msgSender(); + _spendAllowance(from, spender, amount); + _transfer(from, to, amount); + return true; + } + + /** + * @dev Atomically increases the allowance granted to `spender` by the caller. + * + * This is an alternative to {approve} that can be used as a mitigation for + * problems described in {IERC20-approve}. + * + * Emits an {Approval} event indicating the updated allowance. + * + * Requirements: + * + * - `spender` cannot be the zero address. + */ + function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { + address owner = _msgSender(); + _approve(owner, spender, allowance(owner, spender) + addedValue); + return true; + } + + /** + * @dev Atomically decreases the allowance granted to `spender` by the caller. + * + * This is an alternative to {approve} that can be used as a mitigation for + * problems described in {IERC20-approve}. + * + * Emits an {Approval} event indicating the updated allowance. + * + * Requirements: + * + * - `spender` cannot be the zero address. + * - `spender` must have allowance for the caller of at least + * `subtractedValue`. + */ + function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { + address owner = _msgSender(); + uint256 currentAllowance = allowance(owner, spender); + require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero"); + unchecked { + _approve(owner, spender, currentAllowance - subtractedValue); + } + + return true; + } + + /** + * @dev Moves `amount` of tokens from `from` to `to`. + * + * This internal function is equivalent to {transfer}, and can be used to + * e.g. implement automatic token fees, slashing mechanisms, etc. + * + * Emits a {Transfer} event. + * + * Requirements: + * + * - `from` cannot be the zero address. + * - `to` cannot be the zero address. + * - `from` must have a balance of at least `amount`. + */ + function _transfer(address from, address to, uint256 amount) internal virtual { + require(from != address(0), "ERC20: transfer from the zero address"); + require(to != address(0), "ERC20: transfer to the zero address"); + + _beforeTokenTransfer(from, to, amount); + + uint256 fromBalance = _balances[from]; + require(fromBalance >= amount, "ERC20: transfer amount exceeds balance"); + unchecked { + _balances[from] = fromBalance - amount; + // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by + // decrementing then incrementing. + _balances[to] += amount; + } + + emit Transfer(from, to, amount); + + _afterTokenTransfer(from, to, amount); + } + + /** @dev Creates `amount` tokens and assigns them to `account`, increasing + * the total supply. + * + * Emits a {Transfer} event with `from` set to the zero address. + * + * Requirements: + * + * - `account` cannot be the zero address. + */ + function _mint(address account, uint256 amount) internal virtual { + require(account != address(0), "ERC20: mint to the zero address"); + + _beforeTokenTransfer(address(0), account, amount); + + _totalSupply += amount; + unchecked { + // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above. + _balances[account] += amount; + } + emit Transfer(address(0), account, amount); + + _afterTokenTransfer(address(0), account, amount); + } + + /** + * @dev Destroys `amount` tokens from `account`, reducing the + * total supply. + * + * Emits a {Transfer} event with `to` set to the zero address. + * + * Requirements: + * + * - `account` cannot be the zero address. + * - `account` must have at least `amount` tokens. + */ + function _burn(address account, uint256 amount) internal virtual { + require(account != address(0), "ERC20: burn from the zero address"); + + _beforeTokenTransfer(account, address(0), amount); + + uint256 accountBalance = _balances[account]; + require(accountBalance >= amount, "ERC20: burn amount exceeds balance"); + unchecked { + _balances[account] = accountBalance - amount; + // Overflow not possible: amount <= accountBalance <= totalSupply. + _totalSupply -= amount; + } + + emit Transfer(account, address(0), amount); + + _afterTokenTransfer(account, address(0), amount); + } + + /** + * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens. + * + * This internal function is equivalent to `approve`, and can be used to + * e.g. set automatic allowances for certain subsystems, etc. + * + * Emits an {Approval} event. + * + * Requirements: + * + * - `owner` cannot be the zero address. + * - `spender` cannot be the zero address. + */ + function _approve(address owner, address spender, uint256 amount) internal virtual { + require(owner != address(0), "ERC20: approve from the zero address"); + require(spender != address(0), "ERC20: approve to the zero address"); + + _allowances[owner][spender] = amount; + emit Approval(owner, spender, amount); + } + + /** + * @dev Updates `owner` s allowance for `spender` based on spent `amount`. + * + * Does not update the allowance amount in case of infinite allowance. + * Revert if not enough allowance is available. + * + * Might emit an {Approval} event. + */ + function _spendAllowance(address owner, address spender, uint256 amount) internal virtual { + uint256 currentAllowance = allowance(owner, spender); + if (currentAllowance != type(uint256).max) { + require(currentAllowance >= amount, "ERC20: insufficient allowance"); + unchecked { + _approve(owner, spender, currentAllowance - amount); + } + } + } + + /** + * @dev Hook that is called before any transfer of tokens. This includes + * minting and burning. + * + * Calling conditions: + * + * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens + * will be transferred to `to`. + * - when `from` is zero, `amount` tokens will be minted for `to`. + * - when `to` is zero, `amount` of ``from``'s tokens will be burned. + * - `from` and `to` are never both zero. + * + * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. + */ + function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {} + + /** + * @dev Hook that is called after any transfer of tokens. This includes + * minting and burning. + * + * Calling conditions: + * + * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens + * has been transferred to `to`. + * - when `from` is zero, `amount` tokens have been minted for `to`. + * - when `to` is zero, `amount` of ``from``'s tokens have been burned. + * - `from` and `to` are never both zero. + * + * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. + */ + function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {} +} + + +// File contracts/interfaces/IBurnableToken.sol + +// Original license: SPDX_License_Identifier: MIT +pragma solidity ^0.8.10; + +interface IBurnableToken { + function burn(address user, uint256 amount) external; +} + + +// File contracts/interfaces/IBurnRedeemable.sol + +// Original license: SPDX_License_Identifier: MIT +pragma solidity ^0.8.10; + +interface IBurnRedeemable { + event Redeemed( + address indexed user, + address indexed xenContract, + address indexed tokenContract, + uint256 xenAmount, + uint256 tokenAmount + ); + + function onTokenBurned(address user, uint256 amount) external; +} + + +// File contracts/test/BadBurner.sol + +// Original license: SPDX_License_Identifier: MIT +pragma solidity ^0.8.10; + + + + +/** + This contract implements IBurnRedeemable but tries to make reentrant burn call to XENCrypto + */ +contract BadBurner is Context, IBurnRedeemable, IERC165, ERC20("Bad Burner", "BAD") { + IBurnableToken public xenContract; + + constructor(address _xenContractAddress) { + xenContract = IBurnableToken(_xenContractAddress); + } + + function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { + return interfaceId == type(IBurnRedeemable).interfaceId; + } + + function exchangeTokens(uint256 amount) external { + xenContract.burn(_msgSender(), amount); + } + + function onTokenBurned(address, uint256 amount) public { + xenContract.burn(_msgSender(), amount); + } +} + + +// File contracts/test/Burner.sol + +// Original license: SPDX_License_Identifier: MIT +pragma solidity ^0.8.10; + + + + +contract Burner is Context, IBurnRedeemable, IERC165, ERC20("Burner", "BURN") { + IBurnableToken public xenContract; + + constructor(address _xenContractAddress) { + xenContract = IBurnableToken(_xenContractAddress); + } + + function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { + return interfaceId == type(IBurnRedeemable).interfaceId; + } + + function exchangeTokens(uint256 amount) external { + xenContract.burn(_msgSender(), amount); + } + + function onTokenBurned(address user, uint256 amount) public { + require(_msgSender() == address(xenContract), "Burner: wrong caller"); + require(user != address(0), "Burner: zero user address"); + require(amount != 0, "Burner: zero amount"); + + _mint(user, amount); + } +} + + +// File contracts/test/RevertingBurner.sol + +// Original license: SPDX_License_Identifier: MIT +pragma solidity ^0.8.10; + + + + +/** + This contract implements IBurnRedeemable but reverts in the callback hook + */ +contract RevertingBurner is Context, IBurnRedeemable, IERC165, ERC20("Reverting Burner", "REV") { + IBurnableToken public xenContract; + + constructor(address _xenContractAddress) { + xenContract = IBurnableToken(_xenContractAddress); + } + + function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { + return interfaceId == type(IBurnRedeemable).interfaceId; + } + + function exchangeTokens(uint256 amount) external { + xenContract.burn(_msgSender(), amount); + } + + function onTokenBurned(address, uint256) public pure { + revert(); + } +} + + +// File abdk-libraries-solidity/ABDKMath64x64.sol@v3.0.0 + +// Original license: SPDX_License_Identifier: BSD-4-Clause +/* + * ABDK Math 64.64 Smart Contract Library. Copyright © 2019 by ABDK Consulting. + * Author: Mikhail Vladimirov + */ +pragma solidity ^0.8.0; + +/** + * Smart contract library of mathematical functions operating with signed + * 64.64-bit fixed point numbers. Signed 64.64-bit fixed point number is + * basically a simple fraction whose numerator is signed 128-bit integer and + * denominator is 2^64. As long as denominator is always the same, there is no + * need to store it, thus in Solidity signed 64.64-bit fixed point numbers are + * represented by int128 type holding only the numerator. + */ +library ABDKMath64x64 { + /* + * Minimum value signed 64.64-bit fixed point number may have. + */ + int128 private constant MIN_64x64 = -0x80000000000000000000000000000000; + + /* + * Maximum value signed 64.64-bit fixed point number may have. + */ + int128 private constant MAX_64x64 = 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; + + /** + * Convert signed 256-bit integer number into signed 64.64-bit fixed point + * number. Revert on overflow. + * + * @param x signed 256-bit integer number + * @return signed 64.64-bit fixed point number + */ + function fromInt (int256 x) internal pure returns (int128) { + unchecked { + require (x >= -0x8000000000000000 && x <= 0x7FFFFFFFFFFFFFFF); + return int128 (x << 64); + } + } + + /** + * Convert signed 64.64 fixed point number into signed 64-bit integer number + * rounding down. + * + * @param x signed 64.64-bit fixed point number + * @return signed 64-bit integer number + */ + function toInt (int128 x) internal pure returns (int64) { + unchecked { + return int64 (x >> 64); + } + } + + /** + * Convert unsigned 256-bit integer number into signed 64.64-bit fixed point + * number. Revert on overflow. + * + * @param x unsigned 256-bit integer number + * @return signed 64.64-bit fixed point number + */ + function fromUInt (uint256 x) internal pure returns (int128) { + unchecked { + require (x <= 0x7FFFFFFFFFFFFFFF); + return int128 (int256 (x << 64)); + } + } + + /** + * Convert signed 64.64 fixed point number into unsigned 64-bit integer + * number rounding down. Revert on underflow. + * + * @param x signed 64.64-bit fixed point number + * @return unsigned 64-bit integer number + */ + function toUInt (int128 x) internal pure returns (uint64) { + unchecked { + require (x >= 0); + return uint64 (uint128 (x >> 64)); + } + } + + /** + * Convert signed 128.128 fixed point number into signed 64.64-bit fixed point + * number rounding down. Revert on overflow. + * + * @param x signed 128.128-bin fixed point number + * @return signed 64.64-bit fixed point number + */ + function from128x128 (int256 x) internal pure returns (int128) { + unchecked { + int256 result = x >> 64; + require (result >= MIN_64x64 && result <= MAX_64x64); + return int128 (result); + } + } + + /** + * Convert signed 64.64 fixed point number into signed 128.128 fixed point + * number. + * + * @param x signed 64.64-bit fixed point number + * @return signed 128.128 fixed point number + */ + function to128x128 (int128 x) internal pure returns (int256) { + unchecked { + return int256 (x) << 64; + } + } + + /** + * Calculate x + y. Revert on overflow. + * + * @param x signed 64.64-bit fixed point number + * @param y signed 64.64-bit fixed point number + * @return signed 64.64-bit fixed point number + */ + function add (int128 x, int128 y) internal pure returns (int128) { + unchecked { + int256 result = int256(x) + y; + require (result >= MIN_64x64 && result <= MAX_64x64); + return int128 (result); + } + } + + /** + * Calculate x - y. Revert on overflow. + * + * @param x signed 64.64-bit fixed point number + * @param y signed 64.64-bit fixed point number + * @return signed 64.64-bit fixed point number + */ + function sub (int128 x, int128 y) internal pure returns (int128) { + unchecked { + int256 result = int256(x) - y; + require (result >= MIN_64x64 && result <= MAX_64x64); + return int128 (result); + } + } + + /** + * Calculate x * y rounding down. Revert on overflow. + * + * @param x signed 64.64-bit fixed point number + * @param y signed 64.64-bit fixed point number + * @return signed 64.64-bit fixed point number + */ + function mul (int128 x, int128 y) internal pure returns (int128) { + unchecked { + int256 result = int256(x) * y >> 64; + require (result >= MIN_64x64 && result <= MAX_64x64); + return int128 (result); + } + } + + /** + * Calculate x * y rounding towards zero, where x is signed 64.64 fixed point + * number and y is signed 256-bit integer number. Revert on overflow. + * + * @param x signed 64.64 fixed point number + * @param y signed 256-bit integer number + * @return signed 256-bit integer number + */ + function muli (int128 x, int256 y) internal pure returns (int256) { + unchecked { + if (x == MIN_64x64) { + require (y >= -0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF && + y <= 0x1000000000000000000000000000000000000000000000000); + return -y << 63; + } else { + bool negativeResult = false; + if (x < 0) { + x = -x; + negativeResult = true; + } + if (y < 0) { + y = -y; // We rely on overflow behavior here + negativeResult = !negativeResult; + } + uint256 absoluteResult = mulu (x, uint256 (y)); + if (negativeResult) { + require (absoluteResult <= + 0x8000000000000000000000000000000000000000000000000000000000000000); + return -int256 (absoluteResult); // We rely on overflow behavior here + } else { + require (absoluteResult <= + 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); + return int256 (absoluteResult); + } + } + } + } + + /** + * Calculate x * y rounding down, where x is signed 64.64 fixed point number + * and y is unsigned 256-bit integer number. Revert on overflow. + * + * @param x signed 64.64 fixed point number + * @param y unsigned 256-bit integer number + * @return unsigned 256-bit integer number + */ + function mulu (int128 x, uint256 y) internal pure returns (uint256) { + unchecked { + if (y == 0) return 0; + + require (x >= 0); + + uint256 lo = (uint256 (int256 (x)) * (y & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)) >> 64; + uint256 hi = uint256 (int256 (x)) * (y >> 128); + + require (hi <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); + hi <<= 64; + + require (hi <= + 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - lo); + return hi + lo; + } + } + + /** + * Calculate x / y rounding towards zero. Revert on overflow or when y is + * zero. + * + * @param x signed 64.64-bit fixed point number + * @param y signed 64.64-bit fixed point number + * @return signed 64.64-bit fixed point number + */ + function div (int128 x, int128 y) internal pure returns (int128) { + unchecked { + require (y != 0); + int256 result = (int256 (x) << 64) / y; + require (result >= MIN_64x64 && result <= MAX_64x64); + return int128 (result); + } + } + + /** + * Calculate x / y rounding towards zero, where x and y are signed 256-bit + * integer numbers. Revert on overflow or when y is zero. + * + * @param x signed 256-bit integer number + * @param y signed 256-bit integer number + * @return signed 64.64-bit fixed point number + */ + function divi (int256 x, int256 y) internal pure returns (int128) { + unchecked { + require (y != 0); + + bool negativeResult = false; + if (x < 0) { + x = -x; // We rely on overflow behavior here + negativeResult = true; + } + if (y < 0) { + y = -y; // We rely on overflow behavior here + negativeResult = !negativeResult; + } + uint128 absoluteResult = divuu (uint256 (x), uint256 (y)); + if (negativeResult) { + require (absoluteResult <= 0x80000000000000000000000000000000); + return -int128 (absoluteResult); // We rely on overflow behavior here + } else { + require (absoluteResult <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); + return int128 (absoluteResult); // We rely on overflow behavior here + } + } + } + + /** + * Calculate x / y rounding towards zero, where x and y are unsigned 256-bit + * integer numbers. Revert on overflow or when y is zero. + * + * @param x unsigned 256-bit integer number + * @param y unsigned 256-bit integer number + * @return signed 64.64-bit fixed point number + */ + function divu (uint256 x, uint256 y) internal pure returns (int128) { + unchecked { + require (y != 0); + uint128 result = divuu (x, y); + require (result <= uint128 (MAX_64x64)); + return int128 (result); + } + } + + /** + * Calculate -x. Revert on overflow. + * + * @param x signed 64.64-bit fixed point number + * @return signed 64.64-bit fixed point number + */ + function neg (int128 x) internal pure returns (int128) { + unchecked { + require (x != MIN_64x64); + return -x; + } + } + + /** + * Calculate |x|. Revert on overflow. + * + * @param x signed 64.64-bit fixed point number + * @return signed 64.64-bit fixed point number + */ + function abs (int128 x) internal pure returns (int128) { + unchecked { + require (x != MIN_64x64); + return x < 0 ? -x : x; + } + } + + /** + * Calculate 1 / x rounding towards zero. Revert on overflow or when x is + * zero. + * + * @param x signed 64.64-bit fixed point number + * @return signed 64.64-bit fixed point number + */ + function inv (int128 x) internal pure returns (int128) { + unchecked { + require (x != 0); + int256 result = int256 (0x100000000000000000000000000000000) / x; + require (result >= MIN_64x64 && result <= MAX_64x64); + return int128 (result); + } + } + + /** + * Calculate arithmetics average of x and y, i.e. (x + y) / 2 rounding down. + * + * @param x signed 64.64-bit fixed point number + * @param y signed 64.64-bit fixed point number + * @return signed 64.64-bit fixed point number + */ + function avg (int128 x, int128 y) internal pure returns (int128) { + unchecked { + return int128 ((int256 (x) + int256 (y)) >> 1); + } + } + + /** + * Calculate geometric average of x and y, i.e. sqrt (x * y) rounding down. + * Revert on overflow or in case x * y is negative. + * + * @param x signed 64.64-bit fixed point number + * @param y signed 64.64-bit fixed point number + * @return signed 64.64-bit fixed point number + */ + function gavg (int128 x, int128 y) internal pure returns (int128) { + unchecked { + int256 m = int256 (x) * int256 (y); + require (m >= 0); + require (m < + 0x4000000000000000000000000000000000000000000000000000000000000000); + return int128 (sqrtu (uint256 (m))); + } + } + + /** + * Calculate x^y assuming 0^0 is 1, where x is signed 64.64 fixed point number + * and y is unsigned 256-bit integer number. Revert on overflow. + * + * @param x signed 64.64-bit fixed point number + * @param y uint256 value + * @return signed 64.64-bit fixed point number + */ + function pow (int128 x, uint256 y) internal pure returns (int128) { + unchecked { + bool negative = x < 0 && y & 1 == 1; + + uint256 absX = uint128 (x < 0 ? -x : x); + uint256 absResult; + absResult = 0x100000000000000000000000000000000; + + if (absX <= 0x10000000000000000) { + absX <<= 63; + while (y != 0) { + if (y & 0x1 != 0) { + absResult = absResult * absX >> 127; + } + absX = absX * absX >> 127; + + if (y & 0x2 != 0) { + absResult = absResult * absX >> 127; + } + absX = absX * absX >> 127; + + if (y & 0x4 != 0) { + absResult = absResult * absX >> 127; + } + absX = absX * absX >> 127; + + if (y & 0x8 != 0) { + absResult = absResult * absX >> 127; + } + absX = absX * absX >> 127; + + y >>= 4; + } + + absResult >>= 64; + } else { + uint256 absXShift = 63; + if (absX < 0x1000000000000000000000000) { absX <<= 32; absXShift -= 32; } + if (absX < 0x10000000000000000000000000000) { absX <<= 16; absXShift -= 16; } + if (absX < 0x1000000000000000000000000000000) { absX <<= 8; absXShift -= 8; } + if (absX < 0x10000000000000000000000000000000) { absX <<= 4; absXShift -= 4; } + if (absX < 0x40000000000000000000000000000000) { absX <<= 2; absXShift -= 2; } + if (absX < 0x80000000000000000000000000000000) { absX <<= 1; absXShift -= 1; } + + uint256 resultShift = 0; + while (y != 0) { + require (absXShift < 64); + + if (y & 0x1 != 0) { + absResult = absResult * absX >> 127; + resultShift += absXShift; + if (absResult > 0x100000000000000000000000000000000) { + absResult >>= 1; + resultShift += 1; + } + } + absX = absX * absX >> 127; + absXShift <<= 1; + if (absX >= 0x100000000000000000000000000000000) { + absX >>= 1; + absXShift += 1; + } + + y >>= 1; + } + + require (resultShift < 64); + absResult >>= 64 - resultShift; + } + int256 result = negative ? -int256 (absResult) : int256 (absResult); + require (result >= MIN_64x64 && result <= MAX_64x64); + return int128 (result); + } + } + + /** + * Calculate sqrt (x) rounding down. Revert if x < 0. + * + * @param x signed 64.64-bit fixed point number + * @return signed 64.64-bit fixed point number + */ + function sqrt (int128 x) internal pure returns (int128) { + unchecked { + require (x >= 0); + return int128 (sqrtu (uint256 (int256 (x)) << 64)); + } + } + + /** + * Calculate binary logarithm of x. Revert if x <= 0. + * + * @param x signed 64.64-bit fixed point number + * @return signed 64.64-bit fixed point number + */ + function log_2 (int128 x) internal pure returns (int128) { + unchecked { + require (x > 0); + + int256 msb = 0; + int256 xc = x; + if (xc >= 0x10000000000000000) { xc >>= 64; msb += 64; } + if (xc >= 0x100000000) { xc >>= 32; msb += 32; } + if (xc >= 0x10000) { xc >>= 16; msb += 16; } + if (xc >= 0x100) { xc >>= 8; msb += 8; } + if (xc >= 0x10) { xc >>= 4; msb += 4; } + if (xc >= 0x4) { xc >>= 2; msb += 2; } + if (xc >= 0x2) msb += 1; // No need to shift xc anymore + + int256 result = msb - 64 << 64; + uint256 ux = uint256 (int256 (x)) << uint256 (127 - msb); + for (int256 bit = 0x8000000000000000; bit > 0; bit >>= 1) { + ux *= ux; + uint256 b = ux >> 255; + ux >>= 127 + b; + result += bit * int256 (b); + } + + return int128 (result); + } + } + + /** + * Calculate natural logarithm of x. Revert if x <= 0. + * + * @param x signed 64.64-bit fixed point number + * @return signed 64.64-bit fixed point number + */ + function ln (int128 x) internal pure returns (int128) { + unchecked { + require (x > 0); + + return int128 (int256 ( + uint256 (int256 (log_2 (x))) * 0xB17217F7D1CF79ABC9E3B39803F2F6AF >> 128)); + } + } + + /** + * Calculate binary exponent of x. Revert on overflow. + * + * @param x signed 64.64-bit fixed point number + * @return signed 64.64-bit fixed point number + */ + function exp_2 (int128 x) internal pure returns (int128) { + unchecked { + require (x < 0x400000000000000000); // Overflow + + if (x < -0x400000000000000000) return 0; // Underflow + + uint256 result = 0x80000000000000000000000000000000; + + if (x & 0x8000000000000000 > 0) + result = result * 0x16A09E667F3BCC908B2FB1366EA957D3E >> 128; + if (x & 0x4000000000000000 > 0) + result = result * 0x1306FE0A31B7152DE8D5A46305C85EDEC >> 128; + if (x & 0x2000000000000000 > 0) + result = result * 0x1172B83C7D517ADCDF7C8C50EB14A791F >> 128; + if (x & 0x1000000000000000 > 0) + result = result * 0x10B5586CF9890F6298B92B71842A98363 >> 128; + if (x & 0x800000000000000 > 0) + result = result * 0x1059B0D31585743AE7C548EB68CA417FD >> 128; + if (x & 0x400000000000000 > 0) + result = result * 0x102C9A3E778060EE6F7CACA4F7A29BDE8 >> 128; + if (x & 0x200000000000000 > 0) + result = result * 0x10163DA9FB33356D84A66AE336DCDFA3F >> 128; + if (x & 0x100000000000000 > 0) + result = result * 0x100B1AFA5ABCBED6129AB13EC11DC9543 >> 128; + if (x & 0x80000000000000 > 0) + result = result * 0x10058C86DA1C09EA1FF19D294CF2F679B >> 128; + if (x & 0x40000000000000 > 0) + result = result * 0x1002C605E2E8CEC506D21BFC89A23A00F >> 128; + if (x & 0x20000000000000 > 0) + result = result * 0x100162F3904051FA128BCA9C55C31E5DF >> 128; + if (x & 0x10000000000000 > 0) + result = result * 0x1000B175EFFDC76BA38E31671CA939725 >> 128; + if (x & 0x8000000000000 > 0) + result = result * 0x100058BA01FB9F96D6CACD4B180917C3D >> 128; + if (x & 0x4000000000000 > 0) + result = result * 0x10002C5CC37DA9491D0985C348C68E7B3 >> 128; + if (x & 0x2000000000000 > 0) + result = result * 0x1000162E525EE054754457D5995292026 >> 128; + if (x & 0x1000000000000 > 0) + result = result * 0x10000B17255775C040618BF4A4ADE83FC >> 128; + if (x & 0x800000000000 > 0) + result = result * 0x1000058B91B5BC9AE2EED81E9B7D4CFAB >> 128; + if (x & 0x400000000000 > 0) + result = result * 0x100002C5C89D5EC6CA4D7C8ACC017B7C9 >> 128; + if (x & 0x200000000000 > 0) + result = result * 0x10000162E43F4F831060E02D839A9D16D >> 128; + if (x & 0x100000000000 > 0) + result = result * 0x100000B1721BCFC99D9F890EA06911763 >> 128; + if (x & 0x80000000000 > 0) + result = result * 0x10000058B90CF1E6D97F9CA14DBCC1628 >> 128; + if (x & 0x40000000000 > 0) + result = result * 0x1000002C5C863B73F016468F6BAC5CA2B >> 128; + if (x & 0x20000000000 > 0) + result = result * 0x100000162E430E5A18F6119E3C02282A5 >> 128; + if (x & 0x10000000000 > 0) + result = result * 0x1000000B1721835514B86E6D96EFD1BFE >> 128; + if (x & 0x8000000000 > 0) + result = result * 0x100000058B90C0B48C6BE5DF846C5B2EF >> 128; + if (x & 0x4000000000 > 0) + result = result * 0x10000002C5C8601CC6B9E94213C72737A >> 128; + if (x & 0x2000000000 > 0) + result = result * 0x1000000162E42FFF037DF38AA2B219F06 >> 128; + if (x & 0x1000000000 > 0) + result = result * 0x10000000B17217FBA9C739AA5819F44F9 >> 128; + if (x & 0x800000000 > 0) + result = result * 0x1000000058B90BFCDEE5ACD3C1CEDC823 >> 128; + if (x & 0x400000000 > 0) + result = result * 0x100000002C5C85FE31F35A6A30DA1BE50 >> 128; + if (x & 0x200000000 > 0) + result = result * 0x10000000162E42FF0999CE3541B9FFFCF >> 128; + if (x & 0x100000000 > 0) + result = result * 0x100000000B17217F80F4EF5AADDA45554 >> 128; + if (x & 0x80000000 > 0) + result = result * 0x10000000058B90BFBF8479BD5A81B51AD >> 128; + if (x & 0x40000000 > 0) + result = result * 0x1000000002C5C85FDF84BD62AE30A74CC >> 128; + if (x & 0x20000000 > 0) + result = result * 0x100000000162E42FEFB2FED257559BDAA >> 128; + if (x & 0x10000000 > 0) + result = result * 0x1000000000B17217F7D5A7716BBA4A9AE >> 128; + if (x & 0x8000000 > 0) + result = result * 0x100000000058B90BFBE9DDBAC5E109CCE >> 128; + if (x & 0x4000000 > 0) + result = result * 0x10000000002C5C85FDF4B15DE6F17EB0D >> 128; + if (x & 0x2000000 > 0) + result = result * 0x1000000000162E42FEFA494F1478FDE05 >> 128; + if (x & 0x1000000 > 0) + result = result * 0x10000000000B17217F7D20CF927C8E94C >> 128; + if (x & 0x800000 > 0) + result = result * 0x1000000000058B90BFBE8F71CB4E4B33D >> 128; + if (x & 0x400000 > 0) + result = result * 0x100000000002C5C85FDF477B662B26945 >> 128; + if (x & 0x200000 > 0) + result = result * 0x10000000000162E42FEFA3AE53369388C >> 128; + if (x & 0x100000 > 0) + result = result * 0x100000000000B17217F7D1D351A389D40 >> 128; + if (x & 0x80000 > 0) + result = result * 0x10000000000058B90BFBE8E8B2D3D4EDE >> 128; + if (x & 0x40000 > 0) + result = result * 0x1000000000002C5C85FDF4741BEA6E77E >> 128; + if (x & 0x20000 > 0) + result = result * 0x100000000000162E42FEFA39FE95583C2 >> 128; + if (x & 0x10000 > 0) + result = result * 0x1000000000000B17217F7D1CFB72B45E1 >> 128; + if (x & 0x8000 > 0) + result = result * 0x100000000000058B90BFBE8E7CC35C3F0 >> 128; + if (x & 0x4000 > 0) + result = result * 0x10000000000002C5C85FDF473E242EA38 >> 128; + if (x & 0x2000 > 0) + result = result * 0x1000000000000162E42FEFA39F02B772C >> 128; + if (x & 0x1000 > 0) + result = result * 0x10000000000000B17217F7D1CF7D83C1A >> 128; + if (x & 0x800 > 0) + result = result * 0x1000000000000058B90BFBE8E7BDCBE2E >> 128; + if (x & 0x400 > 0) + result = result * 0x100000000000002C5C85FDF473DEA871F >> 128; + if (x & 0x200 > 0) + result = result * 0x10000000000000162E42FEFA39EF44D91 >> 128; + if (x & 0x100 > 0) + result = result * 0x100000000000000B17217F7D1CF79E949 >> 128; + if (x & 0x80 > 0) + result = result * 0x10000000000000058B90BFBE8E7BCE544 >> 128; + if (x & 0x40 > 0) + result = result * 0x1000000000000002C5C85FDF473DE6ECA >> 128; + if (x & 0x20 > 0) + result = result * 0x100000000000000162E42FEFA39EF366F >> 128; + if (x & 0x10 > 0) + result = result * 0x1000000000000000B17217F7D1CF79AFA >> 128; + if (x & 0x8 > 0) + result = result * 0x100000000000000058B90BFBE8E7BCD6D >> 128; + if (x & 0x4 > 0) + result = result * 0x10000000000000002C5C85FDF473DE6B2 >> 128; + if (x & 0x2 > 0) + result = result * 0x1000000000000000162E42FEFA39EF358 >> 128; + if (x & 0x1 > 0) + result = result * 0x10000000000000000B17217F7D1CF79AB >> 128; + + result >>= uint256 (int256 (63 - (x >> 64))); + require (result <= uint256 (int256 (MAX_64x64))); + + return int128 (int256 (result)); + } + } + + /** + * Calculate natural exponent of x. Revert on overflow. + * + * @param x signed 64.64-bit fixed point number + * @return signed 64.64-bit fixed point number + */ + function exp (int128 x) internal pure returns (int128) { + unchecked { + require (x < 0x400000000000000000); // Overflow + + if (x < -0x400000000000000000) return 0; // Underflow + + return exp_2 ( + int128 (int256 (x) * 0x171547652B82FE1777D0FFDA0D23A7D12 >> 128)); + } + } + + /** + * Calculate x / y rounding towards zero, where x and y are unsigned 256-bit + * integer numbers. Revert on overflow or when y is zero. + * + * @param x unsigned 256-bit integer number + * @param y unsigned 256-bit integer number + * @return unsigned 64.64-bit fixed point number + */ + function divuu (uint256 x, uint256 y) private pure returns (uint128) { + unchecked { + require (y != 0); + + uint256 result; + + if (x <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) + result = (x << 64) / y; + else { + uint256 msb = 192; + uint256 xc = x >> 192; + if (xc >= 0x100000000) { xc >>= 32; msb += 32; } + if (xc >= 0x10000) { xc >>= 16; msb += 16; } + if (xc >= 0x100) { xc >>= 8; msb += 8; } + if (xc >= 0x10) { xc >>= 4; msb += 4; } + if (xc >= 0x4) { xc >>= 2; msb += 2; } + if (xc >= 0x2) msb += 1; // No need to shift xc anymore + + result = (x << 255 - msb) / ((y - 1 >> msb - 191) + 1); + require (result <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); + + uint256 hi = result * (y >> 128); + uint256 lo = result * (y & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); + + uint256 xh = x >> 192; + uint256 xl = x << 64; + + if (xl < lo) xh -= 1; + xl -= lo; // We rely on overflow behavior here + lo = hi << 128; + if (xl < lo) xh -= 1; + xl -= lo; // We rely on overflow behavior here + + assert (xh == hi >> 128); + + result += xl / y; + } + + require (result <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); + return uint128 (result); + } + } + + /** + * Calculate sqrt (x) rounding down, where x is unsigned 256-bit integer + * number. + * + * @param x unsigned 256-bit integer number + * @return unsigned 128-bit integer number + */ + function sqrtu (uint256 x) private pure returns (uint128) { + unchecked { + if (x == 0) return 0; + else { + uint256 xx = x; + uint256 r = 1; + if (xx >= 0x100000000000000000000000000000000) { xx >>= 128; r <<= 64; } + if (xx >= 0x10000000000000000) { xx >>= 64; r <<= 32; } + if (xx >= 0x100000000) { xx >>= 32; r <<= 16; } + if (xx >= 0x10000) { xx >>= 16; r <<= 8; } + if (xx >= 0x100) { xx >>= 8; r <<= 4; } + if (xx >= 0x10) { xx >>= 4; r <<= 2; } + if (xx >= 0x8) { r <<= 1; } + r = (r + x / r) >> 1; + r = (r + x / r) >> 1; + r = (r + x / r) >> 1; + r = (r + x / r) >> 1; + r = (r + x / r) >> 1; + r = (r + x / r) >> 1; + r = (r + x / r) >> 1; // Seven iterations should be enough + uint256 r1 = x / r; + return uint128 (r < r1 ? r : r1); + } + } + } +} + + +// File contracts/interfaces/IRankedMintingToken.sol + +// Original license: SPDX_License_Identifier: MIT +pragma solidity ^0.8.10; + +interface IRankedMintingToken { + event RankClaimed(address indexed user, uint256 term, uint256 rank); + + event MintClaimed(address indexed user, uint256 rewardAmount); + + function claimRank(uint256 term) external; + + function claimMintReward() external; +} + + +// File contracts/interfaces/IStakingToken.sol + +// Original license: SPDX_License_Identifier: MIT +pragma solidity ^0.8.10; + +interface IStakingToken { + event Staked(address indexed user, uint256 amount, uint256 term); + + event Withdrawn(address indexed user, uint256 amount, uint256 reward); + + function stake(uint256 amount, uint256 term) external; + + function withdraw() external; +} + + +// File contracts/test/XENCryptoRank100001.sol + +// Original license: SPDX_License_Identifier: MIT +pragma solidity ^0.8.10; + + + + + + + +contract XENCrypto100001 is + Context, + IRankedMintingToken, + IStakingToken, + IBurnableToken, + ERC20("XEN Crypto 100k", "XEN100K") +{ + using ABDKMath64x64 for int128; + using ABDKMath64x64 for uint256; + + // INTERNAL TYPE TO DESCRIBE A RANK STAKE + struct MintInfo { + address user; + uint256 term; + uint256 maturityTs; + uint256 rank; + uint256 amplifier; + uint256 eaaRate; + } + + // INTERNAL TYPE TO DESCRIBE A XEN STAKE + struct StakeInfo { + uint256 term; + uint256 maturityTs; + uint256 amount; + uint256 apy; + } + + // PUBLIC CONSTANTS + + uint256 public constant SECONDS_IN_DAY = 3_600 * 24; + uint256 public constant DAYS_IN_YEAR = 365; + + // N.B. modified original contract to test MaxTerm after Global Rank > 100_001 + uint256 public constant GENESIS_RANK = 100_001; + + uint256 public constant MIN_TERM = 1 * SECONDS_IN_DAY - 1; + uint256 public constant MAX_TERM_START = 100 * SECONDS_IN_DAY; + uint256 public constant MAX_TERM_END = 1_000 * SECONDS_IN_DAY; + uint256 public constant TERM_AMPLIFIER = 15; + uint256 public constant TERM_AMPLIFIER_THRESHOLD = 5_000; + uint256 public constant REWARD_AMPLIFIER_START = 3_000; + uint256 public constant REWARD_AMPLIFIER_END = 1; + uint256 public constant EAA_PM_START = 100; + uint256 public constant EAA_PM_STEP = 1; + uint256 public constant EAA_RANK_STEP = 100_000; + uint256 public constant WITHDRAWAL_WINDOW_DAYS = 7; + + uint256 public constant XEN_MIN_STAKE = 0; + + uint256 public constant XEN_MIN_BURN = 0; + + uint256 public constant XEN_APY_START = 20; + uint256 public constant XEN_APY_DAYS_STEP = 90; + uint256 public constant XEN_APY_END = 2; + + string public constant AUTHORS = "@MrJackLevin @lbelyaev faircrypto.org"; + + // PUBLIC STATE, READABLE VIA NAMESAKE GETTERS + + uint256 public immutable genesisTs; + uint256 public globalRank = GENESIS_RANK; + uint256 public activeMinters; + uint256 public activeStakes; + uint256 public totalXenStaked; + // user address => XEN mint info + mapping(address => MintInfo) public userMints; + // user address => XEN stake info + mapping(address => StakeInfo) public userStakes; + // user address => XEN burn amount + mapping(address => uint256) public userBurns; + + // CONSTRUCTOR + constructor() { + genesisTs = block.timestamp; + } + + function _min(uint256 a, uint256 b) private pure returns (uint256) { + if (a > b) return b; + return a; + } + + function _max(uint256 a, uint256 b) private pure returns (uint256) { + if (a > b) return a; + return b; + } + + // PRIVATE METHODS + + /** + * @dev calculates current MaxTerm based on GlobalTank + * (if GlobalTank crosses over TERM_AMPLIFIER_THRESHOLD) + */ + function _calculateMaxTerm() private view returns (uint256) { + if (globalRank > TERM_AMPLIFIER_THRESHOLD) { + uint256 delta = globalRank.fromUInt().log_2().mul(TERM_AMPLIFIER.fromUInt()).toUInt(); + uint256 newMax = MAX_TERM_START + delta * SECONDS_IN_DAY; + return _min(newMax, MAX_TERM_END); + } + return MAX_TERM_START; + } + + /** + * @dev calculates Withdrawal Penalty depending on lateness + */ + function _penalty(uint256 secsLate) private pure returns (uint256) { + // =MIN(2^(daysLate+3)/window-1,100) + uint256 daysLate = secsLate / SECONDS_IN_DAY; + if (daysLate > WITHDRAWAL_WINDOW_DAYS - 1) return 100; + uint256 penalty = (uint256(1) << (daysLate + 3)) / WITHDRAWAL_WINDOW_DAYS - 1; + return _min(penalty, 100); + } + + /** + * @dev calculates net Mint Reward (adjusted for Penalty) + */ + function _calculateMintReward( + uint256 cRank, + uint256 term, + uint256 maturityTs, + uint256 amplifier, + uint256 eeaRate + ) private view returns (uint256) { + uint256 secsLate = block.timestamp - maturityTs; + uint256 penalty = _penalty(secsLate); + uint256 rankDelta = _max(globalRank - cRank, 2); + uint256 EAA = (1_000 + eeaRate); + uint256 reward = getGrossReward(rankDelta, amplifier, term, EAA); + return (reward * (100 - penalty)) / 100; + } + + /** + * @dev cleans up Stake storage (gets some Gas credit;)) + */ + function _cleanUpStake() private { + delete userMints[_msgSender()]; + activeMinters--; + } + + /** + * @dev calculates XEN Stake Reward + */ + function _calculateStakeReward( + uint256 amount, + uint256 term, + uint256 maturityTs, + uint256 apy + ) private view returns (uint256) { + if (block.timestamp > maturityTs) { + uint256 rate = (apy * term * 1_000_000) / DAYS_IN_YEAR; + return (amount * rate) / 100_000_000; + } + return 0; + } + + /** + * @dev calculates Reward Amplifier + */ + function _calculateRewardAmplifier() private view returns (uint256) { + uint256 amplifierDecrease = (block.timestamp - genesisTs) / SECONDS_IN_DAY; + if (amplifierDecrease < REWARD_AMPLIFIER_START) { + return _max(REWARD_AMPLIFIER_START - amplifierDecrease, REWARD_AMPLIFIER_END); + } else { + return REWARD_AMPLIFIER_END; + } + } + + /** + * @dev calculates Early Adopter Amplifier Rate (in 1/000ths) + * actual EAA is (1_000 + EAAR) / 1_000 + */ + function _calculateEAARate() private view returns (uint256) { + uint256 decrease = (EAA_PM_STEP * globalRank) / EAA_RANK_STEP; + if (decrease > EAA_PM_START) return 0; + return EAA_PM_START - decrease; + } + + /** + * @dev calculates APY (in %) + */ + function _calculateAPY() private view returns (uint256) { + uint256 decrease = (block.timestamp - genesisTs) / (SECONDS_IN_DAY * XEN_APY_DAYS_STEP); + if (XEN_APY_START - XEN_APY_END < decrease) return XEN_APY_END; + return XEN_APY_START - decrease; + } + + /** + * @dev calculates Reward Amplifier + */ + function _createStake(uint256 amount, uint256 term) private { + userStakes[_msgSender()] = StakeInfo({ + term: term, + maturityTs: block.timestamp + term * SECONDS_IN_DAY, + amount: amount, + apy: _calculateAPY() + }); + activeStakes++; + totalXenStaked += amount; + } + + // PUBLIC CONVENIENCE GETTERS + + /** + * @dev calculates gross Mint Reward + */ + function getGrossReward( + uint256 rankDelta, + uint256 amplifier, + uint256 term, + uint256 eaa + ) public pure returns (uint256) { + int128 log128 = rankDelta.fromUInt().log_2(); + int128 reward128 = log128.mul(amplifier.fromUInt()).mul(term.fromUInt()).mul(eaa.fromUInt()); + return reward128.div(uint256(1_000).fromUInt()).toUInt(); + } + + /** + * @dev returns Rank Stake object associated with User account address + */ + function getUserMint() external view returns (MintInfo memory) { + return userMints[_msgSender()]; + } + + /** + * @dev returns XEN Stake object associated with User account address + */ + function getUserStake() external view returns (StakeInfo memory) { + return userStakes[_msgSender()]; + } + + /** + * @dev returns current AMP + */ + function getCurrentAMP() external view returns (uint256) { + return _calculateRewardAmplifier(); + } + + /** + * @dev returns current EAA Rate + */ + function getCurrentEAAR() external view returns (uint256) { + return _calculateEAARate(); + } + + /** + * @dev returns current APY + */ + function getCurrentAPY() external view returns (uint256) { + return _calculateAPY(); + } + + // PUBLIC STATE-CHANGING METHODS + + /** + * @dev accepts User Rank Stake provided all checks pass (incl. no current Stake) + */ + function claimRank(uint256 term) external { + uint256 termSec = term * SECONDS_IN_DAY; + require(termSec > MIN_TERM, "CRank: Term less than min"); + require(termSec < _calculateMaxTerm() + 1, "CRank: Term more than current max term"); + require(userMints[_msgSender()].rank == 0, "CRank: Mint already in progress"); + + // create and store new MintInfo + MintInfo memory mintInfo = MintInfo({ + user: _msgSender(), + term: term, + maturityTs: block.timestamp + termSec, + rank: globalRank, + amplifier: _calculateRewardAmplifier(), + eaaRate: _calculateEAARate() + }); + userMints[_msgSender()] = mintInfo; + activeMinters++; + emit RankClaimed(_msgSender(), term, globalRank++); + } + + /** + * @dev ends minting upon maturity (and within permitted Withdrawal Time Window), gets minted XEN + */ + function claimMintReward() external { + MintInfo memory mintInfo = userMints[_msgSender()]; + require(mintInfo.rank > 0, "CRank: No mint exists"); + require(block.timestamp > mintInfo.maturityTs, "CRank: Mint maturity not reached"); + + // calculate reward and mint tokens + uint256 rewardAmount = _calculateMintReward( + mintInfo.rank, + mintInfo.term, + mintInfo.maturityTs, + mintInfo.amplifier, + mintInfo.eaaRate + ) * 1 ether; + _mint(_msgSender(), rewardAmount); + + _cleanUpStake(); + emit MintClaimed(_msgSender(), rewardAmount); + } + + /** + * @dev ends Rank Stake upon maturity (and within permitted Withdrawal time Window) + * mints XEN coins and splits them between User and designated other address + */ + function claimMintRewardAndShare(address other, uint256 pct) external { + MintInfo memory mintInfo = userMints[_msgSender()]; + require(other != address(0), "CRank: Cannot share with zero address"); + require(pct > 0, "CRank: Cannot share zero percent"); + require(pct < 101, "CRank: Cannot share 100+ percent"); + require(mintInfo.rank > 0, "CRank: No mint exists"); + require(block.timestamp > mintInfo.maturityTs, "CRank: Mint maturity not reached"); + + // calculate reward + uint256 rewardAmount = _calculateMintReward( + mintInfo.rank, + mintInfo.term, + mintInfo.maturityTs, + mintInfo.amplifier, + mintInfo.eaaRate + ) * 1 ether; + uint256 sharedReward = (rewardAmount * pct) / 100; + uint256 ownReward = rewardAmount - sharedReward; + + // mint reward tokens + _mint(_msgSender(), ownReward); + _mint(other, sharedReward); + + _cleanUpStake(); + emit MintClaimed(_msgSender(), rewardAmount); + } + + /** + * @dev ends Rank Stake upon maturity (and within permitted Withdrawal time Window) + * mints XEN coins and stakes 'pct' of it for 'term' + */ + function claimMintRewardAndStake(uint256 pct, uint256 term) external { + MintInfo memory mintInfo = userMints[_msgSender()]; + // require(pct > 0, "CRank: Cannot share zero percent"); + require(pct < 101, "CRank: Cannot share >100 percent"); + require(mintInfo.rank > 0, "CRank: No mint exists"); + require(block.timestamp > mintInfo.maturityTs, "CRank: Mint maturity not reached"); + + // calculate reward + uint256 rewardAmount = _calculateMintReward( + mintInfo.rank, + mintInfo.term, + mintInfo.maturityTs, + mintInfo.amplifier, + mintInfo.eaaRate + ) * 1 ether; + uint256 stakedReward = (rewardAmount * pct) / 100; + uint256 ownReward = rewardAmount - stakedReward; + + // mint reward tokens part + _mint(_msgSender(), ownReward); + _cleanUpStake(); + emit MintClaimed(_msgSender(), rewardAmount); + + // nothing to burn since we haven't minted this part yet + // stake extra tokens part + require(stakedReward > XEN_MIN_STAKE, "XEN: Below min stake"); + require(term * SECONDS_IN_DAY > MIN_TERM, "XEN: Below min stake term"); + require(term * SECONDS_IN_DAY < MAX_TERM_END + 1, "XEN: Above max stake term"); + require(userStakes[_msgSender()].amount == 0, "XEN: stake exists"); + + _createStake(stakedReward, term); + emit Staked(_msgSender(), stakedReward, term); + } + + /** + * @dev initiates XEN Stake in amount for a term (days) + */ + function stake(uint256 amount, uint256 term) external { + require(balanceOf(_msgSender()) >= amount, "XEN: not enough balance"); + require(amount > XEN_MIN_STAKE, "XEN: Below min stake"); + require(term * SECONDS_IN_DAY > MIN_TERM, "XEN: Below min stake term"); + require(term * SECONDS_IN_DAY < MAX_TERM_END + 1, "XEN: Above max stake term"); + require(userStakes[_msgSender()].amount == 0, "XEN: stake exists"); + + // burn staked XEN + _burn(_msgSender(), amount); + // create XEN Stake + _createStake(amount, term); + emit Staked(_msgSender(), amount, term); + } + + /** + * @dev ends XEN Stake and gets reward if the Stake is mature + */ + function withdraw() external { + StakeInfo memory userStake = userStakes[_msgSender()]; + require(userStake.amount > 0, "XEN: no stake exists"); + + uint256 xenReward = _calculateStakeReward( + userStake.amount, + userStake.term, + userStake.maturityTs, + userStake.apy + ); + activeStakes--; + totalXenStaked -= userStake.amount; + + // mint staked XEN (+ reward) + _mint(_msgSender(), userStake.amount + xenReward); + emit Withdrawn(_msgSender(), userStake.amount, xenReward); + delete userStakes[_msgSender()]; + } + + /** + * @dev burns XEN tokens and creates Proof-Of-Burn record to be used by connected DeFi services + */ + function burn(address user, uint256 amount) public { + require(amount > XEN_MIN_BURN, "Burn: Below min limit"); + require( + IERC165(_msgSender()).supportsInterface(type(IBurnRedeemable).interfaceId), + "Burn: not a supported contract" + ); + + _spendAllowance(user, _msgSender(), amount); + _burn(user, amount); + userBurns[user] += amount; + IBurnRedeemable(_msgSender()).onTokenBurned(user, amount); + } +} + + +// File contracts/test/XENCryptoRank25mm1.sol + +// Original license: SPDX_License_Identifier: MIT +pragma solidity ^0.8.10; + + + + + + + +contract XENCrypto25mm1 is + Context, + IRankedMintingToken, + IStakingToken, + IBurnableToken, + ERC20("XEN Crypto 25mm", "XEN25MM") +{ + using ABDKMath64x64 for int128; + using ABDKMath64x64 for uint256; + + // INTERNAL TYPE TO DESCRIBE A RANK STAKE + struct MintInfo { + address user; + uint256 term; + uint256 maturityTs; + uint256 rank; + uint256 amplifier; + uint256 eaaRate; + } + + // INTERNAL TYPE TO DESCRIBE A XEN STAKE + struct StakeInfo { + uint256 term; + uint256 maturityTs; + uint256 amount; + uint256 apy; + } + + // PUBLIC CONSTANTS + + uint256 public constant SECONDS_IN_DAY = 3_600 * 24; + uint256 public constant DAYS_IN_YEAR = 365; + + // N.B. modified original contract to test MaxTerm after Global Rank > 100_001 + uint256 public constant GENESIS_RANK = 25_000_001; + + uint256 public constant MIN_TERM = 1 * SECONDS_IN_DAY - 1; + uint256 public constant MAX_TERM_START = 100 * SECONDS_IN_DAY; + uint256 public constant MAX_TERM_END = 1_000 * SECONDS_IN_DAY; + uint256 public constant TERM_AMPLIFIER = 15; + uint256 public constant TERM_AMPLIFIER_THRESHOLD = 5_000; + uint256 public constant REWARD_AMPLIFIER_START = 3_000; + uint256 public constant REWARD_AMPLIFIER_END = 1; + uint256 public constant EAA_PM_START = 100; + uint256 public constant EAA_PM_STEP = 1; + uint256 public constant EAA_RANK_STEP = 100_000; + uint256 public constant WITHDRAWAL_WINDOW_DAYS = 7; + + uint256 public constant XEN_MIN_STAKE = 0; + + uint256 public constant XEN_MIN_BURN = 0; + + uint256 public constant XEN_APY_START = 20; + uint256 public constant XEN_APY_DAYS_STEP = 90; + uint256 public constant XEN_APY_END = 2; + + string public constant AUTHORS = "@MrJackLevin @lbelyaev faircrypto.org"; + + // PUBLIC STATE, READABLE VIA NAMESAKE GETTERS + + uint256 public immutable genesisTs; + uint256 public globalRank = GENESIS_RANK; + uint256 public activeMinters; + uint256 public activeStakes; + uint256 public totalXenStaked; + // user address => XEN mint info + mapping(address => MintInfo) public userMints; + // user address => XEN stake info + mapping(address => StakeInfo) public userStakes; + // user address => XEN burn amount + mapping(address => uint256) public userBurns; + + // CONSTRUCTOR + constructor() { + genesisTs = block.timestamp; + } + + function _min(uint256 a, uint256 b) private pure returns (uint256) { + if (a > b) return b; + return a; + } + + function _max(uint256 a, uint256 b) private pure returns (uint256) { + if (a > b) return a; + return b; + } + + // PRIVATE METHODS + + /** + * @dev calculates current MaxTerm based on GlobalTank + * (if GlobalTank crosses over TERM_AMPLIFIER_THRESHOLD) + */ + function _calculateMaxTerm() private view returns (uint256) { + if (globalRank > TERM_AMPLIFIER_THRESHOLD) { + uint256 delta = globalRank.fromUInt().log_2().mul(TERM_AMPLIFIER.fromUInt()).toUInt(); + uint256 newMax = MAX_TERM_START + delta * SECONDS_IN_DAY; + return _min(newMax, MAX_TERM_END); + } + return MAX_TERM_START; + } + + /** + * @dev calculates Withdrawal Penalty depending on lateness + */ + function _penalty(uint256 secsLate) private pure returns (uint256) { + // =MIN(2^(daysLate+3)/window-1,100) + uint256 daysLate = secsLate / SECONDS_IN_DAY; + if (daysLate > WITHDRAWAL_WINDOW_DAYS - 1) return 100; + uint256 penalty = (uint256(1) << (daysLate + 3)) / WITHDRAWAL_WINDOW_DAYS - 1; + return _min(penalty, 100); + } + + /** + * @dev calculates net Mint Reward (adjusted for Penalty) + */ + function _calculateMintReward( + uint256 cRank, + uint256 term, + uint256 maturityTs, + uint256 amplifier, + uint256 eeaRate + ) private view returns (uint256) { + uint256 secsLate = block.timestamp - maturityTs; + uint256 penalty = _penalty(secsLate); + uint256 rankDelta = _max(globalRank - cRank, 2); + uint256 EAA = (1_000 + eeaRate); + uint256 reward = getGrossReward(rankDelta, amplifier, term, EAA); + return (reward * (100 - penalty)) / 100; + } + + /** + * @dev cleans up Stake storage (gets some Gas credit;)) + */ + function _cleanUpStake() private { + delete userMints[_msgSender()]; + activeMinters--; + } + + /** + * @dev calculates XEN Stake Reward + */ + function _calculateStakeReward( + uint256 amount, + uint256 term, + uint256 maturityTs, + uint256 apy + ) private view returns (uint256) { + if (block.timestamp > maturityTs) { + uint256 rate = (apy * term * 1_000_000) / DAYS_IN_YEAR; + return (amount * rate) / 100_000_000; + } + return 0; + } + + /** + * @dev calculates Reward Amplifier + */ + function _calculateRewardAmplifier() private view returns (uint256) { + uint256 amplifierDecrease = (block.timestamp - genesisTs) / SECONDS_IN_DAY; + if (amplifierDecrease < REWARD_AMPLIFIER_START) { + return _max(REWARD_AMPLIFIER_START - amplifierDecrease, REWARD_AMPLIFIER_END); + } else { + return REWARD_AMPLIFIER_END; + } + } + + /** + * @dev calculates Early Adopter Amplifier Rate (in 1/000ths) + * actual EAA is (1_000 + EAAR) / 1_000 + */ + function _calculateEAARate() private view returns (uint256) { + uint256 decrease = (EAA_PM_STEP * globalRank) / EAA_RANK_STEP; + if (decrease > EAA_PM_START) return 0; + return EAA_PM_START - decrease; + } + + /** + * @dev calculates APY (in %) + */ + function _calculateAPY() private view returns (uint256) { + uint256 decrease = (block.timestamp - genesisTs) / (SECONDS_IN_DAY * XEN_APY_DAYS_STEP); + if (XEN_APY_START - XEN_APY_END < decrease) return XEN_APY_END; + return XEN_APY_START - decrease; + } + + /** + * @dev calculates Reward Amplifier + */ + function _createStake(uint256 amount, uint256 term) private { + userStakes[_msgSender()] = StakeInfo({ + term: term, + maturityTs: block.timestamp + term * SECONDS_IN_DAY, + amount: amount, + apy: _calculateAPY() + }); + activeStakes++; + totalXenStaked += amount; + } + + // PUBLIC CONVENIENCE GETTERS + + /** + * @dev calculates gross Mint Reward + */ + function getGrossReward( + uint256 rankDelta, + uint256 amplifier, + uint256 term, + uint256 eaa + ) public pure returns (uint256) { + int128 log128 = rankDelta.fromUInt().log_2(); + int128 reward128 = log128.mul(amplifier.fromUInt()).mul(term.fromUInt()).mul(eaa.fromUInt()); + return reward128.div(uint256(1_000).fromUInt()).toUInt(); + } + + /** + * @dev returns Rank Stake object associated with User account address + */ + function getUserMint() external view returns (MintInfo memory) { + return userMints[_msgSender()]; + } + + /** + * @dev returns XEN Stake object associated with User account address + */ + function getUserStake() external view returns (StakeInfo memory) { + return userStakes[_msgSender()]; + } + + /** + * @dev returns current AMP + */ + function getCurrentAMP() external view returns (uint256) { + return _calculateRewardAmplifier(); + } + + /** + * @dev returns current EAA Rate + */ + function getCurrentEAAR() external view returns (uint256) { + return _calculateEAARate(); + } + + /** + * @dev returns current APY + */ + function getCurrentAPY() external view returns (uint256) { + return _calculateAPY(); + } + + // PUBLIC STATE-CHANGING METHODS + + /** + * @dev accepts User Rank Stake provided all checks pass (incl. no current Stake) + */ + function claimRank(uint256 term) external { + uint256 termSec = term * SECONDS_IN_DAY; + require(termSec > MIN_TERM, "CRank: Term less than min"); + require(termSec < _calculateMaxTerm() + 1, "CRank: Term more than current max term"); + require(userMints[_msgSender()].rank == 0, "CRank: Mint already in progress"); + + // create and store new MintInfo + MintInfo memory mintInfo = MintInfo({ + user: _msgSender(), + term: term, + maturityTs: block.timestamp + termSec, + rank: globalRank, + amplifier: _calculateRewardAmplifier(), + eaaRate: _calculateEAARate() + }); + userMints[_msgSender()] = mintInfo; + activeMinters++; + emit RankClaimed(_msgSender(), term, globalRank++); + } + + /** + * @dev ends minting upon maturity (and within permitted Withdrawal Time Window), gets minted XEN + */ + function claimMintReward() external { + MintInfo memory mintInfo = userMints[_msgSender()]; + require(mintInfo.rank > 0, "CRank: No mint exists"); + require(block.timestamp > mintInfo.maturityTs, "CRank: Mint maturity not reached"); + + // calculate reward and mint tokens + uint256 rewardAmount = _calculateMintReward( + mintInfo.rank, + mintInfo.term, + mintInfo.maturityTs, + mintInfo.amplifier, + mintInfo.eaaRate + ) * 1 ether; + _mint(_msgSender(), rewardAmount); + + _cleanUpStake(); + emit MintClaimed(_msgSender(), rewardAmount); + } + + /** + * @dev ends Rank Stake upon maturity (and within permitted Withdrawal time Window) + * mints XEN coins and splits them between User and designated other address + */ + function claimMintRewardAndShare(address other, uint256 pct) external { + MintInfo memory mintInfo = userMints[_msgSender()]; + require(other != address(0), "CRank: Cannot share with zero address"); + require(pct > 0, "CRank: Cannot share zero percent"); + require(pct < 101, "CRank: Cannot share 100+ percent"); + require(mintInfo.rank > 0, "CRank: No mint exists"); + require(block.timestamp > mintInfo.maturityTs, "CRank: Mint maturity not reached"); + + // calculate reward + uint256 rewardAmount = _calculateMintReward( + mintInfo.rank, + mintInfo.term, + mintInfo.maturityTs, + mintInfo.amplifier, + mintInfo.eaaRate + ) * 1 ether; + uint256 sharedReward = (rewardAmount * pct) / 100; + uint256 ownReward = rewardAmount - sharedReward; + + // mint reward tokens + _mint(_msgSender(), ownReward); + _mint(other, sharedReward); + + _cleanUpStake(); + emit MintClaimed(_msgSender(), rewardAmount); + } + + /** + * @dev ends Rank Stake upon maturity (and within permitted Withdrawal time Window) + * mints XEN coins and stakes 'pct' of it for 'term' + */ + function claimMintRewardAndStake(uint256 pct, uint256 term) external { + MintInfo memory mintInfo = userMints[_msgSender()]; + // require(pct > 0, "CRank: Cannot share zero percent"); + require(pct < 101, "CRank: Cannot share >100 percent"); + require(mintInfo.rank > 0, "CRank: No mint exists"); + require(block.timestamp > mintInfo.maturityTs, "CRank: Mint maturity not reached"); + + // calculate reward + uint256 rewardAmount = _calculateMintReward( + mintInfo.rank, + mintInfo.term, + mintInfo.maturityTs, + mintInfo.amplifier, + mintInfo.eaaRate + ) * 1 ether; + uint256 stakedReward = (rewardAmount * pct) / 100; + uint256 ownReward = rewardAmount - stakedReward; + + // mint reward tokens part + _mint(_msgSender(), ownReward); + _cleanUpStake(); + emit MintClaimed(_msgSender(), rewardAmount); + + // nothing to burn since we haven't minted this part yet + // stake extra tokens part + require(stakedReward > XEN_MIN_STAKE, "XEN: Below min stake"); + require(term * SECONDS_IN_DAY > MIN_TERM, "XEN: Below min stake term"); + require(term * SECONDS_IN_DAY < MAX_TERM_END + 1, "XEN: Above max stake term"); + require(userStakes[_msgSender()].amount == 0, "XEN: stake exists"); + + _createStake(stakedReward, term); + emit Staked(_msgSender(), stakedReward, term); + } + + /** + * @dev initiates XEN Stake in amount for a term (days) + */ + function stake(uint256 amount, uint256 term) external { + require(balanceOf(_msgSender()) >= amount, "XEN: not enough balance"); + require(amount > XEN_MIN_STAKE, "XEN: Below min stake"); + require(term * SECONDS_IN_DAY > MIN_TERM, "XEN: Below min stake term"); + require(term * SECONDS_IN_DAY < MAX_TERM_END + 1, "XEN: Above max stake term"); + require(userStakes[_msgSender()].amount == 0, "XEN: stake exists"); + + // burn staked XEN + _burn(_msgSender(), amount); + // create XEN Stake + _createStake(amount, term); + emit Staked(_msgSender(), amount, term); + } + + /** + * @dev ends XEN Stake and gets reward if the Stake is mature + */ + function withdraw() external { + StakeInfo memory userStake = userStakes[_msgSender()]; + require(userStake.amount > 0, "XEN: no stake exists"); + + uint256 xenReward = _calculateStakeReward( + userStake.amount, + userStake.term, + userStake.maturityTs, + userStake.apy + ); + activeStakes--; + totalXenStaked -= userStake.amount; + + // mint staked XEN (+ reward) + _mint(_msgSender(), userStake.amount + xenReward); + emit Withdrawn(_msgSender(), userStake.amount, xenReward); + delete userStakes[_msgSender()]; + } + + /** + * @dev burns XEN tokens and creates Proof-Of-Burn record to be used by connected DeFi services + */ + function burn(address user, uint256 amount) public { + require(amount > XEN_MIN_BURN, "Burn: Below min limit"); + require( + IERC165(_msgSender()).supportsInterface(type(IBurnRedeemable).interfaceId), + "Burn: not a supported contract" + ); + + _spendAllowance(user, _msgSender(), amount); + _burn(user, amount); + userBurns[user] += amount; + IBurnRedeemable(_msgSender()).onTokenBurned(user, amount); + } +} + + +// File contracts/test/XENCryptoRank5001.sol + +// Original license: SPDX_License_Identifier: MIT +pragma solidity ^0.8.10; + + + + + + + +contract XENCrypto5001 is + Context, + IRankedMintingToken, + IStakingToken, + IBurnableToken, + ERC20("XEN Crypto 5001", "XEN5001") +{ + using ABDKMath64x64 for int128; + using ABDKMath64x64 for uint256; + + // INTERNAL TYPE TO DESCRIBE A RANK STAKE + struct MintInfo { + address user; + uint256 term; + uint256 maturityTs; + uint256 rank; + uint256 amplifier; + uint256 eaaRate; + } + + // INTERNAL TYPE TO DESCRIBE A XEN STAKE + struct StakeInfo { + uint256 term; + uint256 maturityTs; + uint256 amount; + uint256 apy; + } + + // PUBLIC CONSTANTS + + uint256 public constant SECONDS_IN_DAY = 3_600 * 24; + uint256 public constant DAYS_IN_YEAR = 365; + + // N.B. modified original contract to test MaxTerm after Global Rank > 5000 + uint256 public constant GENESIS_RANK = 5_001; + + uint256 public constant MIN_TERM = 1 * SECONDS_IN_DAY - 1; + uint256 public constant MAX_TERM_START = 100 * SECONDS_IN_DAY; + uint256 public constant MAX_TERM_END = 1_000 * SECONDS_IN_DAY; + uint256 public constant TERM_AMPLIFIER = 15; + uint256 public constant TERM_AMPLIFIER_THRESHOLD = 5_000; + uint256 public constant REWARD_AMPLIFIER_START = 3_000; + uint256 public constant REWARD_AMPLIFIER_END = 1; + uint256 public constant EAA_PM_START = 100; + uint256 public constant EAA_PM_STEP = 1; + uint256 public constant EAA_RANK_STEP = 100_000; + uint256 public constant WITHDRAWAL_WINDOW_DAYS = 7; + + uint256 public constant XEN_MIN_STAKE = 0; + + uint256 public constant XEN_MIN_BURN = 0; + + uint256 public constant XEN_APY_START = 20; + uint256 public constant XEN_APY_DAYS_STEP = 90; + uint256 public constant XEN_APY_END = 2; + + string public constant AUTHORS = "@MrJackLevin @lbelyaev faircrypto.org"; + + // PUBLIC STATE, READABLE VIA NAMESAKE GETTERS + + uint256 public immutable genesisTs; + uint256 public globalRank = GENESIS_RANK; + uint256 public activeMinters; + uint256 public activeStakes; + uint256 public totalXenStaked; + // user address => XEN mint info + mapping(address => MintInfo) public userMints; + // user address => XEN stake info + mapping(address => StakeInfo) public userStakes; + // user address => XEN burn amount + mapping(address => uint256) public userBurns; + + // CONSTRUCTOR + constructor() { + genesisTs = block.timestamp; + } + + function _min(uint256 a, uint256 b) private pure returns (uint256) { + if (a > b) return b; + return a; + } + + function _max(uint256 a, uint256 b) private pure returns (uint256) { + if (a > b) return a; + return b; + } + + // PRIVATE METHODS + + /** + * @dev calculates current MaxTerm based on GlobalTank + * (if GlobalTank crosses over TERM_AMPLIFIER_THRESHOLD) + */ + function _calculateMaxTerm() private view returns (uint256) { + if (globalRank > TERM_AMPLIFIER_THRESHOLD) { + uint256 delta = globalRank.fromUInt().log_2().mul(TERM_AMPLIFIER.fromUInt()).toUInt(); + uint256 newMax = MAX_TERM_START + delta * SECONDS_IN_DAY; + return _min(newMax, MAX_TERM_END); + } + return MAX_TERM_START; + } + + /** + * @dev calculates Withdrawal Penalty depending on lateness + */ + function _penalty(uint256 secsLate) private pure returns (uint256) { + // =MIN(2^(daysLate+3)/window-1,100) + uint256 daysLate = secsLate / SECONDS_IN_DAY; + if (daysLate > WITHDRAWAL_WINDOW_DAYS - 1) return 100; + uint256 penalty = (uint256(1) << (daysLate + 3)) / WITHDRAWAL_WINDOW_DAYS - 1; + return _min(penalty, 100); + } + + /** + * @dev calculates net Mint Reward (adjusted for Penalty) + */ + function _calculateMintReward( + uint256 cRank, + uint256 term, + uint256 maturityTs, + uint256 amplifier, + uint256 eeaRate + ) private view returns (uint256) { + uint256 secsLate = block.timestamp - maturityTs; + uint256 penalty = _penalty(secsLate); + uint256 rankDelta = _max(globalRank - cRank, 2); + uint256 EAA = (1_000 + eeaRate); + uint256 reward = getGrossReward(rankDelta, amplifier, term, EAA); + return (reward * (100 - penalty)) / 100; + } + + /** + * @dev cleans up Stake storage (gets some Gas credit;)) + */ + function _cleanUpStake() private { + delete userMints[_msgSender()]; + activeMinters--; + } + + /** + * @dev calculates XEN Stake Reward + */ + function _calculateStakeReward( + uint256 amount, + uint256 term, + uint256 maturityTs, + uint256 apy + ) private view returns (uint256) { + if (block.timestamp > maturityTs) { + uint256 rate = (apy * term * 1_000_000) / DAYS_IN_YEAR; + return (amount * rate) / 100_000_000; + } + return 0; + } + + /** + * @dev calculates Reward Amplifier + */ + function _calculateRewardAmplifier() private view returns (uint256) { + uint256 amplifierDecrease = (block.timestamp - genesisTs) / SECONDS_IN_DAY; + if (amplifierDecrease < REWARD_AMPLIFIER_START) { + return _max(REWARD_AMPLIFIER_START - amplifierDecrease, REWARD_AMPLIFIER_END); + } else { + return REWARD_AMPLIFIER_END; + } + } + + /** + * @dev calculates Early Adopter Amplifier Rate (in 1/000ths) + * actual EAA is (1_000 + EAAR) / 1_000 + */ + function _calculateEAARate() private view returns (uint256) { + uint256 decrease = (EAA_PM_STEP * globalRank) / EAA_RANK_STEP; + if (decrease > EAA_PM_START) return 0; + return EAA_PM_START - decrease; + } + + /** + * @dev calculates APY (in %) + */ + function _calculateAPY() private view returns (uint256) { + uint256 decrease = (block.timestamp - genesisTs) / (SECONDS_IN_DAY * XEN_APY_DAYS_STEP); + if (XEN_APY_START - XEN_APY_END < decrease) return XEN_APY_END; + return XEN_APY_START - decrease; + } + + /** + * @dev calculates Reward Amplifier + */ + function _createStake(uint256 amount, uint256 term) private { + userStakes[_msgSender()] = StakeInfo({ + term: term, + maturityTs: block.timestamp + term * SECONDS_IN_DAY, + amount: amount, + apy: _calculateAPY() + }); + activeStakes++; + totalXenStaked += amount; + } + + // PUBLIC CONVENIENCE GETTERS + + /** + * @dev calculates gross Mint Reward + */ + function getGrossReward( + uint256 rankDelta, + uint256 amplifier, + uint256 term, + uint256 eaa + ) public pure returns (uint256) { + int128 log128 = rankDelta.fromUInt().log_2(); + int128 reward128 = log128.mul(amplifier.fromUInt()).mul(term.fromUInt()).mul(eaa.fromUInt()); + return reward128.div(uint256(1_000).fromUInt()).toUInt(); + } + + /** + * @dev returns Rank Stake object associated with User account address + */ + function getUserMint() external view returns (MintInfo memory) { + return userMints[_msgSender()]; + } + + /** + * @dev returns XEN Stake object associated with User account address + */ + function getUserStake() external view returns (StakeInfo memory) { + return userStakes[_msgSender()]; + } + + /** + * @dev returns current AMP + */ + function getCurrentAMP() external view returns (uint256) { + return _calculateRewardAmplifier(); + } + + /** + * @dev returns current EAA Rate + */ + function getCurrentEAAR() external view returns (uint256) { + return _calculateEAARate(); + } + + /** + * @dev returns current APY + */ + function getCurrentAPY() external view returns (uint256) { + return _calculateAPY(); + } + + // PUBLIC STATE-CHANGING METHODS + + /** + * @dev accepts User Rank Stake provided all checks pass (incl. no current Stake) + */ + function claimRank(uint256 term) external { + uint256 termSec = term * SECONDS_IN_DAY; + require(termSec > MIN_TERM, "CRank: Term less than min"); + require(termSec < _calculateMaxTerm() + 1, "CRank: Term more than current max term"); + require(userMints[_msgSender()].rank == 0, "CRank: Mint already in progress"); + + // create and store new MintInfo + MintInfo memory mintInfo = MintInfo({ + user: _msgSender(), + term: term, + maturityTs: block.timestamp + termSec, + rank: globalRank, + amplifier: _calculateRewardAmplifier(), + eaaRate: _calculateEAARate() + }); + userMints[_msgSender()] = mintInfo; + activeMinters++; + emit RankClaimed(_msgSender(), term, globalRank++); + } + + /** + * @dev ends minting upon maturity (and within permitted Withdrawal Time Window), gets minted XEN + */ + function claimMintReward() external { + MintInfo memory mintInfo = userMints[_msgSender()]; + require(mintInfo.rank > 0, "CRank: No mint exists"); + require(block.timestamp > mintInfo.maturityTs, "CRank: Mint maturity not reached"); + + // calculate reward and mint tokens + uint256 rewardAmount = _calculateMintReward( + mintInfo.rank, + mintInfo.term, + mintInfo.maturityTs, + mintInfo.amplifier, + mintInfo.eaaRate + ) * 1 ether; + _mint(_msgSender(), rewardAmount); + + _cleanUpStake(); + emit MintClaimed(_msgSender(), rewardAmount); + } + + /** + * @dev ends Rank Stake upon maturity (and within permitted Withdrawal time Window) + * mints XEN coins and splits them between User and designated other address + */ + function claimMintRewardAndShare(address other, uint256 pct) external { + MintInfo memory mintInfo = userMints[_msgSender()]; + require(other != address(0), "CRank: Cannot share with zero address"); + require(pct > 0, "CRank: Cannot share zero percent"); + require(pct < 101, "CRank: Cannot share 100+ percent"); + require(mintInfo.rank > 0, "CRank: No mint exists"); + require(block.timestamp > mintInfo.maturityTs, "CRank: Mint maturity not reached"); + + // calculate reward + uint256 rewardAmount = _calculateMintReward( + mintInfo.rank, + mintInfo.term, + mintInfo.maturityTs, + mintInfo.amplifier, + mintInfo.eaaRate + ) * 1 ether; + uint256 sharedReward = (rewardAmount * pct) / 100; + uint256 ownReward = rewardAmount - sharedReward; + + // mint reward tokens + _mint(_msgSender(), ownReward); + _mint(other, sharedReward); + + _cleanUpStake(); + emit MintClaimed(_msgSender(), rewardAmount); + } + + /** + * @dev ends Rank Stake upon maturity (and within permitted Withdrawal time Window) + * mints XEN coins and stakes 'pct' of it for 'term' + */ + function claimMintRewardAndStake(uint256 pct, uint256 term) external { + MintInfo memory mintInfo = userMints[_msgSender()]; + // require(pct > 0, "CRank: Cannot share zero percent"); + require(pct < 101, "CRank: Cannot share >100 percent"); + require(mintInfo.rank > 0, "CRank: No mint exists"); + require(block.timestamp > mintInfo.maturityTs, "CRank: Mint maturity not reached"); + + // calculate reward + uint256 rewardAmount = _calculateMintReward( + mintInfo.rank, + mintInfo.term, + mintInfo.maturityTs, + mintInfo.amplifier, + mintInfo.eaaRate + ) * 1 ether; + uint256 stakedReward = (rewardAmount * pct) / 100; + uint256 ownReward = rewardAmount - stakedReward; + + // mint reward tokens part + _mint(_msgSender(), ownReward); + _cleanUpStake(); + emit MintClaimed(_msgSender(), rewardAmount); + + // nothing to burn since we haven't minted this part yet + // stake extra tokens part + require(stakedReward > XEN_MIN_STAKE, "XEN: Below min stake"); + require(term * SECONDS_IN_DAY > MIN_TERM, "XEN: Below min stake term"); + require(term * SECONDS_IN_DAY < MAX_TERM_END + 1, "XEN: Above max stake term"); + require(userStakes[_msgSender()].amount == 0, "XEN: stake exists"); + + _createStake(stakedReward, term); + emit Staked(_msgSender(), stakedReward, term); + } + + /** + * @dev initiates XEN Stake in amount for a term (days) + */ + function stake(uint256 amount, uint256 term) external { + require(balanceOf(_msgSender()) >= amount, "XEN: not enough balance"); + require(amount > XEN_MIN_STAKE, "XEN: Below min stake"); + require(term * SECONDS_IN_DAY > MIN_TERM, "XEN: Below min stake term"); + require(term * SECONDS_IN_DAY < MAX_TERM_END + 1, "XEN: Above max stake term"); + require(userStakes[_msgSender()].amount == 0, "XEN: stake exists"); + + // burn staked XEN + _burn(_msgSender(), amount); + // create XEN Stake + _createStake(amount, term); + emit Staked(_msgSender(), amount, term); + } + + /** + * @dev ends XEN Stake and gets reward if the Stake is mature + */ + function withdraw() external { + StakeInfo memory userStake = userStakes[_msgSender()]; + require(userStake.amount > 0, "XEN: no stake exists"); + + uint256 xenReward = _calculateStakeReward( + userStake.amount, + userStake.term, + userStake.maturityTs, + userStake.apy + ); + activeStakes--; + totalXenStaked -= userStake.amount; + + // mint staked XEN (+ reward) + _mint(_msgSender(), userStake.amount + xenReward); + emit Withdrawn(_msgSender(), userStake.amount, xenReward); + delete userStakes[_msgSender()]; + } + + /** + * @dev burns XEN tokens and creates Proof-Of-Burn record to be used by connected DeFi services + */ + function burn(address user, uint256 amount) public { + require(amount > XEN_MIN_BURN, "Burn: Below min limit"); + require( + IERC165(_msgSender()).supportsInterface(type(IBurnRedeemable).interfaceId), + "Burn: not a supported contract" + ); + + _spendAllowance(user, _msgSender(), amount); + _burn(user, amount); + userBurns[user] += amount; + IBurnRedeemable(_msgSender()).onTokenBurned(user, amount); + } +} + + +// File contracts/XENMath.sol + +// Original license: SPDX_License_Identifier: MIT +pragma solidity ^0.8.10; + +library XENMath { + function min(uint256 a, uint256 b) external pure returns (uint256) { + if (a > b) return b; + return a; + } + + function max(uint256 a, uint256 b) external pure returns (uint256) { + if (a > b) return a; + return b; + } + + function logX64(uint256 x) external pure returns (int128) { + return ABDKMath64x64.log_2(ABDKMath64x64.fromUInt(x)); + } +} + + +// File contracts/XENCrypto.sol + +// Original license: SPDX_License_Identifier: MIT +pragma solidity ^0.8.10; + + + + + + + + +contract XENCrypto is Context, IRankedMintingToken, IStakingToken, IBurnableToken, ERC20("XEN Crypto", "XEN") { + using XENMath for uint256; + using ABDKMath64x64 for int128; + using ABDKMath64x64 for uint256; + + // INTERNAL TYPE TO DESCRIBE A XEN MINT INFO + struct MintInfo { + address user; + uint256 term; + uint256 maturityTs; + uint256 rank; + uint256 amplifier; + uint256 eaaRate; + } + + // INTERNAL TYPE TO DESCRIBE A XEN STAKE + struct StakeInfo { + uint256 term; + uint256 maturityTs; + uint256 amount; + uint256 apy; + } + + // PUBLIC CONSTANTS + + uint256 public constant SECONDS_IN_DAY = 3_600 * 24; + uint256 public constant DAYS_IN_YEAR = 365; + + uint256 public constant GENESIS_RANK = 1; + + uint256 public constant MIN_TERM = 1 * SECONDS_IN_DAY - 1; + uint256 public constant MAX_TERM_START = 100 * SECONDS_IN_DAY; + uint256 public constant MAX_TERM_END = 1_000 * SECONDS_IN_DAY; + uint256 public constant TERM_AMPLIFIER = 15; + uint256 public constant TERM_AMPLIFIER_THRESHOLD = 5_000; + uint256 public constant REWARD_AMPLIFIER_START = 3_000; + uint256 public constant REWARD_AMPLIFIER_END = 1; + uint256 public constant EAA_PM_START = 100; + uint256 public constant EAA_PM_STEP = 1; + uint256 public constant EAA_RANK_STEP = 100_000; + uint256 public constant WITHDRAWAL_WINDOW_DAYS = 7; + uint256 public constant MAX_PENALTY_PCT = 99; + + uint256 public constant XEN_MIN_STAKE = 0; + + uint256 public constant XEN_MIN_BURN = 0; + + uint256 public constant XEN_APY_START = 20; + uint256 public constant XEN_APY_DAYS_STEP = 90; + uint256 public constant XEN_APY_END = 2; + + string public constant AUTHORS = "@MrJackLevin @lbelyaev faircrypto.org"; + + // PUBLIC STATE, READABLE VIA NAMESAKE GETTERS + + uint256 public immutable genesisTs; + uint256 public globalRank = GENESIS_RANK; + uint256 public activeMinters; + uint256 public activeStakes; + uint256 public totalXenStaked; + // user address => XEN mint info + mapping(address => MintInfo) public userMints; + // user address => XEN stake info + mapping(address => StakeInfo) public userStakes; + // user address => XEN burn amount + mapping(address => uint256) public userBurns; + + // CONSTRUCTOR + constructor() { + genesisTs = block.timestamp; + } + + // PRIVATE METHODS + + /** + * @dev calculates current MaxTerm based on Global Rank + * (if Global Rank crosses over TERM_AMPLIFIER_THRESHOLD) + */ + function _calculateMaxTerm() private view returns (uint256) { + if (globalRank > TERM_AMPLIFIER_THRESHOLD) { + uint256 delta = globalRank.fromUInt().log_2().mul(TERM_AMPLIFIER.fromUInt()).toUInt(); + uint256 newMax = MAX_TERM_START + delta * SECONDS_IN_DAY; + return XENMath.min(newMax, MAX_TERM_END); + } + return MAX_TERM_START; + } + + /** + * @dev calculates Withdrawal Penalty depending on lateness + */ + function _penalty(uint256 secsLate) private pure returns (uint256) { + // =MIN(2^(daysLate+3)/window-1,99) + uint256 daysLate = secsLate / SECONDS_IN_DAY; + if (daysLate > WITHDRAWAL_WINDOW_DAYS - 1) return MAX_PENALTY_PCT; + uint256 penalty = (uint256(1) << (daysLate + 3)) / WITHDRAWAL_WINDOW_DAYS - 1; + return XENMath.min(penalty, MAX_PENALTY_PCT); + } + + /** + * @dev calculates net Mint Reward (adjusted for Penalty) + */ + function _calculateMintReward( + uint256 cRank, + uint256 term, + uint256 maturityTs, + uint256 amplifier, + uint256 eeaRate + ) private view returns (uint256) { + uint256 secsLate = block.timestamp - maturityTs; + uint256 penalty = _penalty(secsLate); + uint256 rankDelta = XENMath.max(globalRank - cRank, 2); + uint256 EAA = (1_000 + eeaRate); + uint256 reward = getGrossReward(rankDelta, amplifier, term, EAA); + return (reward * (100 - penalty)) / 100; + } + + /** + * @dev cleans up User Mint storage (gets some Gas credit;)) + */ + function _cleanUpUserMint() private { + delete userMints[_msgSender()]; + activeMinters--; + } + + /** + * @dev calculates XEN Stake Reward + */ + function _calculateStakeReward( + uint256 amount, + uint256 term, + uint256 maturityTs, + uint256 apy + ) private view returns (uint256) { + if (block.timestamp > maturityTs) { + uint256 rate = (apy * term * 1_000_000) / DAYS_IN_YEAR; + return (amount * rate) / 100_000_000; + } + return 0; + } + + /** + * @dev calculates Reward Amplifier + */ + function _calculateRewardAmplifier() private view returns (uint256) { + uint256 amplifierDecrease = (block.timestamp - genesisTs) / SECONDS_IN_DAY; + if (amplifierDecrease < REWARD_AMPLIFIER_START) { + return XENMath.max(REWARD_AMPLIFIER_START - amplifierDecrease, REWARD_AMPLIFIER_END); + } else { + return REWARD_AMPLIFIER_END; + } + } + + /** + * @dev calculates Early Adopter Amplifier Rate (in 1/000ths) + * actual EAA is (1_000 + EAAR) / 1_000 + */ + function _calculateEAARate() private view returns (uint256) { + uint256 decrease = (EAA_PM_STEP * globalRank) / EAA_RANK_STEP; + if (decrease > EAA_PM_START) return 0; + return EAA_PM_START - decrease; + } + + /** + * @dev calculates APY (in %) + */ + function _calculateAPY() private view returns (uint256) { + uint256 decrease = (block.timestamp - genesisTs) / (SECONDS_IN_DAY * XEN_APY_DAYS_STEP); + if (XEN_APY_START - XEN_APY_END < decrease) return XEN_APY_END; + return XEN_APY_START - decrease; + } + + /** + * @dev creates User Stake + */ + function _createStake(uint256 amount, uint256 term) private { + userStakes[_msgSender()] = StakeInfo({ + term: term, + maturityTs: block.timestamp + term * SECONDS_IN_DAY, + amount: amount, + apy: _calculateAPY() + }); + activeStakes++; + totalXenStaked += amount; + } + + // PUBLIC CONVENIENCE GETTERS + + /** + * @dev calculates gross Mint Reward + */ + function getGrossReward( + uint256 rankDelta, + uint256 amplifier, + uint256 term, + uint256 eaa + ) public pure returns (uint256) { + int128 log128 = rankDelta.fromUInt().log_2(); + int128 reward128 = log128.mul(amplifier.fromUInt()).mul(term.fromUInt()).mul(eaa.fromUInt()); + return reward128.div(uint256(1_000).fromUInt()).toUInt(); + } + + /** + * @dev returns User Mint object associated with User account address + */ + function getUserMint() external view returns (MintInfo memory) { + return userMints[_msgSender()]; + } + + /** + * @dev returns XEN Stake object associated with User account address + */ + function getUserStake() external view returns (StakeInfo memory) { + return userStakes[_msgSender()]; + } + + /** + * @dev returns current AMP + */ + function getCurrentAMP() external view returns (uint256) { + return _calculateRewardAmplifier(); + } + + /** + * @dev returns current EAA Rate + */ + function getCurrentEAAR() external view returns (uint256) { + return _calculateEAARate(); + } + + /** + * @dev returns current APY + */ + function getCurrentAPY() external view returns (uint256) { + return _calculateAPY(); + } + + /** + * @dev returns current MaxTerm + */ + function getCurrentMaxTerm() external view returns (uint256) { + return _calculateMaxTerm(); + } + + // PUBLIC STATE-CHANGING METHODS + + /** + * @dev accepts User cRank claim provided all checks pass (incl. no current claim exists) + */ + function claimRank(uint256 term) external { + uint256 termSec = term * SECONDS_IN_DAY; + require(termSec > MIN_TERM, "CRank: Term less than min"); + require(termSec < _calculateMaxTerm() + 1, "CRank: Term more than current max term"); + require(userMints[_msgSender()].rank == 0, "CRank: Mint already in progress"); + + // create and store new MintInfo + MintInfo memory mintInfo = MintInfo({ + user: _msgSender(), + term: term, + maturityTs: block.timestamp + termSec, + rank: globalRank, + amplifier: _calculateRewardAmplifier(), + eaaRate: _calculateEAARate() + }); + userMints[_msgSender()] = mintInfo; + activeMinters++; + emit RankClaimed(_msgSender(), term, globalRank++); + } + + /** + * @dev ends minting upon maturity (and within permitted Withdrawal Time Window), gets minted XEN + */ + function claimMintReward() external { + MintInfo memory mintInfo = userMints[_msgSender()]; + require(mintInfo.rank > 0, "CRank: No mint exists"); + require(block.timestamp > mintInfo.maturityTs, "CRank: Mint maturity not reached"); + + // calculate reward and mint tokens + uint256 rewardAmount = _calculateMintReward( + mintInfo.rank, + mintInfo.term, + mintInfo.maturityTs, + mintInfo.amplifier, + mintInfo.eaaRate + ) * 1 ether; + _mint(_msgSender(), rewardAmount); + + _cleanUpUserMint(); + emit MintClaimed(_msgSender(), rewardAmount); + } + + /** + * @dev ends minting upon maturity (and within permitted Withdrawal time Window) + * mints XEN coins and splits them between User and designated other address + */ + function claimMintRewardAndShare(address other, uint256 pct) external { + MintInfo memory mintInfo = userMints[_msgSender()]; + require(other != address(0), "CRank: Cannot share with zero address"); + require(pct > 0, "CRank: Cannot share zero percent"); + require(pct < 101, "CRank: Cannot share 100+ percent"); + require(mintInfo.rank > 0, "CRank: No mint exists"); + require(block.timestamp > mintInfo.maturityTs, "CRank: Mint maturity not reached"); + + // calculate reward + uint256 rewardAmount = _calculateMintReward( + mintInfo.rank, + mintInfo.term, + mintInfo.maturityTs, + mintInfo.amplifier, + mintInfo.eaaRate + ) * 1 ether; + uint256 sharedReward = (rewardAmount * pct) / 100; + uint256 ownReward = rewardAmount - sharedReward; + + // mint reward tokens + _mint(_msgSender(), ownReward); + _mint(other, sharedReward); + + _cleanUpUserMint(); + emit MintClaimed(_msgSender(), rewardAmount); + } + + /** + * @dev ends minting upon maturity (and within permitted Withdrawal time Window) + * mints XEN coins and stakes 'pct' of it for 'term' + */ + function claimMintRewardAndStake(uint256 pct, uint256 term) external { + MintInfo memory mintInfo = userMints[_msgSender()]; + // require(pct > 0, "CRank: Cannot share zero percent"); + require(pct < 101, "CRank: Cannot share >100 percent"); + require(mintInfo.rank > 0, "CRank: No mint exists"); + require(block.timestamp > mintInfo.maturityTs, "CRank: Mint maturity not reached"); + + // calculate reward + uint256 rewardAmount = _calculateMintReward( + mintInfo.rank, + mintInfo.term, + mintInfo.maturityTs, + mintInfo.amplifier, + mintInfo.eaaRate + ) * 1 ether; + uint256 stakedReward = (rewardAmount * pct) / 100; + uint256 ownReward = rewardAmount - stakedReward; + + // mint reward tokens part + _mint(_msgSender(), ownReward); + _cleanUpUserMint(); + emit MintClaimed(_msgSender(), rewardAmount); + + // nothing to burn since we haven't minted this part yet + // stake extra tokens part + require(stakedReward > XEN_MIN_STAKE, "XEN: Below min stake"); + require(term * SECONDS_IN_DAY > MIN_TERM, "XEN: Below min stake term"); + require(term * SECONDS_IN_DAY < MAX_TERM_END + 1, "XEN: Above max stake term"); + require(userStakes[_msgSender()].amount == 0, "XEN: stake exists"); + + _createStake(stakedReward, term); + emit Staked(_msgSender(), stakedReward, term); + } + + /** + * @dev initiates XEN Stake in amount for a term (days) + */ + function stake(uint256 amount, uint256 term) external { + require(balanceOf(_msgSender()) >= amount, "XEN: not enough balance"); + require(amount > XEN_MIN_STAKE, "XEN: Below min stake"); + require(term * SECONDS_IN_DAY > MIN_TERM, "XEN: Below min stake term"); + require(term * SECONDS_IN_DAY < MAX_TERM_END + 1, "XEN: Above max stake term"); + require(userStakes[_msgSender()].amount == 0, "XEN: stake exists"); + + // burn staked XEN + _burn(_msgSender(), amount); + // create XEN Stake + _createStake(amount, term); + emit Staked(_msgSender(), amount, term); + } + + /** + * @dev ends XEN Stake and gets reward if the Stake is mature + */ + function withdraw() external { + StakeInfo memory userStake = userStakes[_msgSender()]; + require(userStake.amount > 0, "XEN: no stake exists"); + + uint256 xenReward = _calculateStakeReward( + userStake.amount, + userStake.term, + userStake.maturityTs, + userStake.apy + ); + activeStakes--; + totalXenStaked -= userStake.amount; + + // mint staked XEN (+ reward) + _mint(_msgSender(), userStake.amount + xenReward); + emit Withdrawn(_msgSender(), userStake.amount, xenReward); + delete userStakes[_msgSender()]; + } + + /** + * @dev burns XEN tokens and creates Proof-Of-Burn record to be used by connected DeFi services + */ + function burn(address user, uint256 amount) public { + require(amount > XEN_MIN_BURN, "Burn: Below min limit"); + require( + IERC165(_msgSender()).supportsInterface(type(IBurnRedeemable).interfaceId), + "Burn: not a supported contract" + ); + + _spendAllowance(user, _msgSender(), amount); + _burn(user, amount); + userBurns[user] += amount; + IBurnRedeemable(_msgSender()).onTokenBurned(user, amount); + } +} \ No newline at end of file diff --git a/contracts/tether.sol b/contracts/tether.sol new file mode 100644 index 0000000..a9e8de8 --- /dev/null +++ b/contracts/tether.sol @@ -0,0 +1,509 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.30; + +/** + *Submitted for verification at Etherscan.io on 2017-11-28 +*/ + +/** + * @title SafeMath + * @dev Math operations with safety checks that throw on error + * NOTE: 0.8.x has built-in overflow checks, we add unchecked blocks to preserve the behavior of original contract v0.4.17. + */ +library SafeMath { + function mul(uint256 a, uint256 b) internal pure returns (uint256) { + if (a == 0) return 0; + uint256 c; + unchecked { c = a * b; } + assert(c / a == b); + return c; + } + + function div(uint256 a, uint256 b) internal pure returns (uint256) { + uint256 c; + unchecked { c = a / b; } + return c; + } + + function sub(uint256 a, uint256 b) internal pure returns (uint256) { + assert(b <= a); + unchecked { return a - b; } + } + + function add(uint256 a, uint256 b) internal pure returns (uint256) { + uint256 c; + unchecked { c = a + b; } + assert(c >= a); + return c; + } +} + +/** + * @title Ownable + * @dev The Ownable contract has an owner address, and provides basic authorization control + * functions, this simplifies the implementation of "user permissions". + */ +contract Ownable { + address public owner; + + /** + * @dev The Ownable constructor sets the original `owner` of the contract to the sender + * account. + */ + constructor() { + owner = msg.sender; + } + + /** + * @dev Throws if called by any account other than the owner. + */ + modifier onlyOwner() { + require(msg.sender == owner); + _; + } + + /** + * @dev Allows the current owner to transfer control of the contract to a newOwner. + * @param newOwner The address to transfer ownership to. + */ + function transferOwnership(address newOwner) public onlyOwner { + if (newOwner != address(0)) { + owner = newOwner; + } + } + +} + +/** + * @title ERC20Basic + * @dev Simpler version of ERC20 interface + * @dev see https://github.com/ethereum/EIPs/issues/20 + */ +abstract contract ERC20Basic { + uint public _totalSupply; + function totalSupply() public view virtual returns (uint); + function balanceOf(address who) public view virtual returns (uint); + function transfer(address to, uint value) public virtual; + event Transfer(address indexed from, address indexed to, uint value); +} + +/** + * @title ERC20 interface + * @dev see https://github.com/ethereum/EIPs/issues/20 + */ +abstract contract ERC20 is ERC20Basic { + function allowance(address owner, address spender) public view virtual returns (uint); + function transferFrom(address from, address to, uint value) public virtual; + function approve(address spender, uint value) public virtual; + event Approval(address indexed owner, address indexed spender, uint value); +} + +/** + * @title Basic token + * @dev Basic version of StandardToken, with no allowances. + */ +contract BasicToken is Ownable, ERC20Basic { + using SafeMath for uint; + + mapping(address => uint) public balances; + + // additional variables for use if transaction fees ever became necessary + uint public basisPointsRate = 0; + uint public maximumFee = 0; + + /** + * @dev Fix for the ERC20 short address attack. + */ + modifier onlyPayloadSize(uint size) { + unchecked { + require(!(msg.data.length < size + 4)); + } + _; + } + + /** + * @dev transfer token for a specified address + * @param _to The address to transfer to. + * @param _value The amount to be transferred. + */ + function transfer(address _to, uint _value) public virtual override onlyPayloadSize(2 * 32) { + uint fee = (_value.mul(basisPointsRate)).div(10000); + if (fee > maximumFee) { + fee = maximumFee; + } + uint sendAmount = _value.sub(fee); + balances[msg.sender] = balances[msg.sender].sub(_value); + balances[_to] = balances[_to].add(sendAmount); + if (fee > 0) { + balances[owner] = balances[owner].add(fee); + emit Transfer(msg.sender, owner, fee); + } + emit Transfer(msg.sender, _to, sendAmount); + } + + /** + * @dev Gets the balance of the specified address. + * @param _owner The address to query the the balance of. + * @return balance An uint representing the amount owned by the passed address. + */ + function balanceOf(address _owner) public view virtual override returns (uint balance) { + return balances[_owner]; + } + + // original code relies on _totalSupply variable directly, but keep totalSupply() correct + function totalSupply() public view virtual override returns (uint) { + return _totalSupply; + } +} + +/** + * @title Standard ERC20 token + * + * @dev Implementation of the basic standard token. + * @dev https://github.com/ethereum/EIPs/issues/20 + * @dev Based oncode by FirstBlood: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol + */ +contract StandardToken is BasicToken, ERC20 { + using SafeMath for uint; + + mapping (address => mapping (address => uint)) public allowed; + + uint public constant MAX_UINT = 2**256 - 1; + + /** + * @dev Transfer tokens from one address to another + * @param _from address The address which you want to send tokens from + * @param _to address The address which you want to transfer to + * @param _value uint the amount of tokens to be transferred + */ + function transferFrom(address _from, address _to, uint _value) + public + virtual + override + onlyPayloadSize(3 * 32) + { + uint _allowance = allowed[_from][msg.sender]; + + // Check is not needed because sub(_allowance, _value) will already throw if this condition is not met + // if (_value > _allowance) throw; + + uint fee = (_value.mul(basisPointsRate)).div(10000); + if (fee > maximumFee) { + fee = maximumFee; + } + if (_allowance < MAX_UINT) { + allowed[_from][msg.sender] = _allowance.sub(_value); + } + uint sendAmount = _value.sub(fee); + balances[_from] = balances[_from].sub(_value); + balances[_to] = balances[_to].add(sendAmount); + if (fee > 0) { + balances[owner] = balances[owner].add(fee); + emit Transfer(_from, owner, fee); + } + emit Transfer(_from, _to, sendAmount); + } + + /** + * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender. + * @param _spender The address which will spend the funds. + * @param _value The amount of tokens to be spent. + */ + function approve(address _spender, uint _value) + public + virtual + override + onlyPayloadSize(2 * 32) + { + + // To change the approve amount you first have to reduce the addresses` + // allowance to zero by calling `approve(_spender, 0)` if it is not + // already 0 to mitigate the race condition described here: + // https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 + require(!((_value != 0) && (allowed[msg.sender][_spender] != 0))); + + allowed[msg.sender][_spender] = _value; + emit Approval(msg.sender, _spender, _value); + } + + /** + * @dev Function to check the amount of tokens than an owner allowed to a spender. + * @param _owner address The address which owns the funds. + * @param _spender address The address which will spend the funds. + * @return remaining A uint specifying the amount of tokens still available for the spender. + */ + function allowance(address _owner, address _spender) + public + view + virtual + override + returns (uint remaining) + { + return allowed[_owner][_spender]; + } + +} + + +/** + * @title Pausable + * @dev Base contract which allows children to implement an emergency stop mechanism. + */ +contract Pausable is Ownable { + event Pause(); + event Unpause(); + + bool public paused = false; + + + /** + * @dev Modifier to make a function callable only when the contract is not paused. + */ + modifier whenNotPaused() { + require(!paused); + _; + } + + /** + * @dev Modifier to make a function callable only when the contract is paused. + */ + modifier whenPaused() { + require(paused); + _; + } + + /** + * @dev called by the owner to pause, triggers stopped state + */ + function pause() onlyOwner whenNotPaused public { + paused = true; + emit Pause(); + } + + /** + * @dev called by the owner to unpause, returns to normal state + */ + function unpause() onlyOwner whenPaused public { + paused = false; + emit Unpause(); + } +} + +contract BlackList is Ownable, BasicToken { + + /////// Getters to allow the same blacklist to be used also by other contracts (including upgraded Tether) /////// + function getBlackListStatus(address _maker) external view returns (bool) { + return isBlackListed[_maker]; + } + + function getOwner() external view returns (address) { + return owner; + } + + mapping (address => bool) public isBlackListed; + + function addBlackList (address _evilUser) public onlyOwner { + isBlackListed[_evilUser] = true; + emit AddedBlackList(_evilUser); + } + + function removeBlackList (address _clearedUser) public onlyOwner { + isBlackListed[_clearedUser] = false; + emit RemovedBlackList(_clearedUser); + } + + function destroyBlackFunds (address _blackListedUser) public onlyOwner { + require(isBlackListed[_blackListedUser]); + uint dirtyFunds = balanceOf(_blackListedUser); + balances[_blackListedUser] = 0; + unchecked { _totalSupply -= dirtyFunds; } + emit DestroyedBlackFunds(_blackListedUser, dirtyFunds); + } + + event DestroyedBlackFunds(address _blackListedUser, uint _balance); + + event AddedBlackList(address _user); + + event RemovedBlackList(address _user); + +} + +abstract contract UpgradedStandardToken is StandardToken{ + // those methods are called by the legacy contract + // and they must ensure msg.sender to be the contract address + function transferByLegacy(address from, address to, uint value) public virtual; + function transferFromByLegacy(address sender, address from, address spender, uint value) public virtual; + function approveByLegacy(address from, address spender, uint value) public virtual; +} + +contract TetherToken is Pausable, StandardToken, BlackList { + + using SafeMath for uint; + + string public name; + string public symbol; + uint public decimals; + address public upgradedAddress; + bool public deprecated; + + // The contract can be initialized with a number of tokens + // All the tokens are deposited to the owner address + // + // @param _balance Initial supply of the contract + // @param _name Token Name + // @param _symbol Token symbol + // @param _decimals Token decimals + constructor(uint _initialSupply, string memory _name, string memory _symbol, uint _decimals) { + _totalSupply = _initialSupply; + name = _name; + symbol = _symbol; + decimals = _decimals; + balances[owner] = _initialSupply; + deprecated = false; + } + + // Forward ERC20 methods to upgraded contract if this one is deprecated + function transfer(address _to, uint _value) + public + override(BasicToken, ERC20Basic) + whenNotPaused + { + require(!isBlackListed[msg.sender]); + if (deprecated) { + return UpgradedStandardToken(upgradedAddress).transferByLegacy(msg.sender, _to, _value); + } else { + return super.transfer(_to, _value); + } + } + +// Forward ERC20 methods to upgraded contract if this one is deprecated + function transferFrom(address _from, address _to, uint _value) + public + override(StandardToken) + whenNotPaused + { + require(!isBlackListed[_from]); + if (deprecated) { + return UpgradedStandardToken(upgradedAddress).transferFromByLegacy(msg.sender, _from, _to, _value); + } else { + return super.transferFrom(_from, _to, _value); + } + } + + // Forward ERC20 methods to upgraded contract if this one is deprecated + function balanceOf(address who) + public + view + override(BasicToken, ERC20Basic) + returns (uint) + { + if (deprecated) { + return UpgradedStandardToken(upgradedAddress).balanceOf(who); + } else { + return super.balanceOf(who); + } + } + + // Forward ERC20 methods to upgraded contract if this one is deprecated + function approve(address _spender, uint _value) + public + override(StandardToken) + onlyPayloadSize(2 * 32) + { + if (deprecated) { + return UpgradedStandardToken(upgradedAddress).approveByLegacy(msg.sender, _spender, _value); + } else { + return super.approve(_spender, _value); + } + } + + // Forward ERC20 methods to upgraded contract if this one is deprecated + function allowance(address _owner, address _spender) + public + view + override(StandardToken) + returns (uint remaining) + { + if (deprecated) { + return StandardToken(upgradedAddress).allowance(_owner, _spender); + } else { + return super.allowance(_owner, _spender); + } + } + + // deprecate current contract in favour of a new one + function deprecate(address _upgradedAddress) public onlyOwner { + deprecated = true; + upgradedAddress = _upgradedAddress; + emit Deprecate(_upgradedAddress); + } + + function totalSupply() + public + view + override(BasicToken, ERC20Basic) + returns (uint) + { + if (deprecated) { + return StandardToken(upgradedAddress).totalSupply(); + } else { + return _totalSupply; + } + } + + // Issue a new amount of tokens + // these tokens are deposited into the owner address + // + // @param _amount Number of tokens to be issued + function issue(uint amount) public onlyOwner { + unchecked { + require(_totalSupply + amount > _totalSupply); + require(balances[owner] + amount > balances[owner]); + + balances[owner] += amount; + _totalSupply += amount; + emit Issue(amount); + } + } + + // Redeem tokens. + // These tokens are withdrawn from the owner address + // if the balance must be enough to cover the redeem + // or the call will fail. + // @param _amount Number of tokens to be issued + function redeem(uint amount) public onlyOwner { + unchecked { + require(_totalSupply >= amount); + require(balances[owner] >= amount); + + _totalSupply -= amount; + balances[owner] -= amount; + emit Redeem(amount); + } + } + + function setParams(uint newBasisPoints, uint newMaxFee) public onlyOwner { + // Ensure transparency by hardcoding limit beyond which fees can never be added + require(newBasisPoints < 20); + require(newMaxFee < 50); + + basisPointsRate = newBasisPoints; + unchecked { + maximumFee = newMaxFee.mul(10**decimals); + } + + emit Params(basisPointsRate, maximumFee); + } + + // Called when new token are issued + event Issue(uint amount); + + // Called when tokens are redeemed + event Redeem(uint amount); + + // Called when contract is deprecated + event Deprecate(address newAddress); + + // Called if contract ever adds fees + event Params(uint feeBasisPoints, uint maxFee); +} \ No newline at end of file diff --git a/contracts/usdc_implementation.sol b/contracts/usdc_implementation.sol new file mode 100644 index 0000000..c6a2a1b --- /dev/null +++ b/contracts/usdc_implementation.sol @@ -0,0 +1,2887 @@ +//* SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.30; + +// node_modules/@openzeppelin/contracts/utils/Address.sol + +/** + * @dev Collection of functions related to the address type + */ +library Address { + /** + * @dev Returns true if `account` is a contract. + * + * [IMPORTANT] + * ==== + * It is unsafe to assume that an address for which this function returns + * false is an externally-owned account (EOA) and not a contract. + * + * Among others, `isContract` will return false for the following + * types of addresses: + * + * - an externally-owned account + * - a contract in construction + * - an address where a contract will be created + * - an address where a contract lived, but was destroyed + * ==== + */ + function isContract(address account) internal view returns (bool) { + // This method relies on extcodesize, which returns 0 for contracts in + // construction, since the code is only stored at the end of the + // constructor execution. + + uint256 size; + // solhint-disable-next-line no-inline-assembly + assembly { size := extcodesize(account) } + return size > 0; + } + + /** + * @dev Replacement for Solidity's `transfer`: sends `amount` wei to + * `recipient`, forwarding all available gas and reverting on errors. + * + * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost + * of certain opcodes, possibly making contracts go over the 2300 gas limit + * imposed by `transfer`, making them unable to receive funds via + * `transfer`. {sendValue} removes this limitation. + * + * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. + * + * IMPORTANT: because control is transferred to `recipient`, care must be + * taken to not create reentrancy vulnerabilities. Consider using + * {ReentrancyGuard} or the + * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. + */ + function sendValue(address payable recipient, uint256 amount) internal { + require(address(this).balance >= amount, "Address: insufficient balance"); + + // solhint-disable-next-line avoid-low-level-calls, avoid-call-value + (bool success, ) = recipient.call{ value: amount }(""); + require(success, "Address: unable to send value, recipient may have reverted"); + } + + /** + * @dev Performs a Solidity function call using a low level `call`. A + * plain`call` is an unsafe replacement for a function call: use this + * function instead. + * + * If `target` reverts with a revert reason, it is bubbled up by this + * function (like regular Solidity function calls). + * + * Returns the raw returned data. To convert to the expected return value, + * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. + * + * Requirements: + * + * - `target` must be a contract. + * - calling `target` with `data` must not revert. + * + * _Available since v3.1._ + */ + function functionCall(address target, bytes memory data) internal returns (bytes memory) { + return functionCall(target, data, "Address: low-level call failed"); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with + * `errorMessage` as a fallback revert reason when `target` reverts. + * + * _Available since v3.1._ + */ + function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { + return functionCallWithValue(target, data, 0, errorMessage); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], + * but also transferring `value` wei to `target`. + * + * Requirements: + * + * - the calling contract must have an ETH balance of at least `value`. + * - the called Solidity function must be `payable`. + * + * _Available since v3.1._ + */ + function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { + return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); + } + + /** + * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but + * with `errorMessage` as a fallback revert reason when `target` reverts. + * + * _Available since v3.1._ + */ + function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) { + require(address(this).balance >= value, "Address: insufficient balance for call"); + require(isContract(target), "Address: call to non-contract"); + + // solhint-disable-next-line avoid-low-level-calls + (bool success, bytes memory returndata) = target.call{ value: value }(data); + return _verifyCallResult(success, returndata, errorMessage); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], + * but performing a static call. + * + * _Available since v3.3._ + */ + function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { + return functionStaticCall(target, data, "Address: low-level static call failed"); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], + * but performing a static call. + * + * _Available since v3.3._ + */ + function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) { + require(isContract(target), "Address: static call to non-contract"); + + // solhint-disable-next-line avoid-low-level-calls + (bool success, bytes memory returndata) = target.staticcall(data); + return _verifyCallResult(success, returndata, errorMessage); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], + * but performing a delegate call. + * + * _Available since v3.4._ + */ + function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { + return functionDelegateCall(target, data, "Address: low-level delegate call failed"); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], + * but performing a delegate call. + * + * _Available since v3.4._ + */ + function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { + require(isContract(target), "Address: delegate call to non-contract"); + + // solhint-disable-next-line avoid-low-level-calls + (bool success, bytes memory returndata) = target.delegatecall(data); + return _verifyCallResult(success, returndata, errorMessage); + } + + function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) { + if (success) { + return returndata; + } else { + // Look for revert reason and bubble it up if present + if (returndata.length > 0) { + // The easiest way to bubble the revert reason is using memory via assembly + + // solhint-disable-next-line no-inline-assembly + assembly { + let returndata_size := mload(returndata) + revert(add(32, returndata), returndata_size) + } + } else { + revert(errorMessage); + } + } + } +} + + +// contracts/interface/IERC1271.sol +/** + * Copyright 2023 Circle Internet Group, Inc. All rights reserved. + * + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @dev Interface of the ERC1271 standard signature validation method for + * contracts as defined in https://eips.ethereum.org/EIPS/eip-1271[ERC-1271]. + */ +interface IERC1271 { + /** + * @dev Should return whether the signature provided is valid for the provided data + * @param hash Hash of the data to be signed + * @param signature Signature byte array associated with the provided data hash + * @return magicValue bytes4 magic value 0x1626ba7e when function passes + */ + function isValidSignature(bytes32 hash, bytes memory signature) + external + view + returns (bytes4 magicValue); +} + +// contracts/util/ECRecover.sol +/** + * Copyright 2023 Circle Internet Group, Inc. All rights reserved. + * + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @title ECRecover + * @notice A library that provides a safe ECDSA recovery function + */ +library ECRecover { + /** + * @notice Recover signer's address from a signed message + * @dev Adapted from: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/65e4ffde586ec89af3b7e9140bdc9235d1254853/contracts/cryptography/ECDSA.sol + * Modifications: Accept v, r, and s as separate arguments + * @param digest Keccak-256 hash digest of the signed message + * @param v v of the signature + * @param r r of the signature + * @param s s of the signature + * @return Signer address + */ + function recover( + bytes32 digest, + uint8 v, + bytes32 r, + bytes32 s + ) internal pure returns (address) { + // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature + // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines + // the valid range for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most + // signatures from current libraries generate a unique signature with an s-value in the lower half order. + // + // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value + // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or + // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept + // these malleable signatures as well. + if ( + uint256(s) > + 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0 + ) { + revert("ECRecover: invalid signature 's' value"); + } + + if (v != 27 && v != 28) { + revert("ECRecover: invalid signature 'v' value"); + } + + // If the signature is valid (and not malleable), return the signer address + address signer = ecrecover(digest, v, r, s); + require(signer != address(0), "ECRecover: invalid signature"); + + return signer; + } + + /** + * @notice Recover signer's address from a signed message + * @dev Adapted from: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/0053ee040a7ff1dbc39691c9e67a69f564930a88/contracts/utils/cryptography/ECDSA.sol + * @param digest Keccak-256 hash digest of the signed message + * @param signature Signature byte array associated with hash + * @return Signer address + */ + function recover(bytes32 digest, bytes memory signature) + internal + pure + returns (address) + { + require(signature.length == 65, "ECRecover: invalid signature length"); + + bytes32 r; + bytes32 s; + uint8 v; + + // ecrecover takes the signature parameters, and the only way to get them + // currently is to use assembly. + /// @solidity memory-safe-assembly + assembly { + r := mload(add(signature, 0x20)) + s := mload(add(signature, 0x40)) + v := byte(0, mload(add(signature, 0x60))) + } + return recover(digest, v, r, s); + } +} + +// contracts/util/EIP712.sol +/** + * Copyright 2023 Circle Internet Group, Inc. All rights reserved. + * + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @title EIP712 + * @notice A library that provides EIP712 helper functions + */ +library EIP712 { + /** + * @notice Make EIP712 domain separator + * @param name Contract name + * @param version Contract version + * @param chainId Blockchain ID + * @return Domain separator + */ + function makeDomainSeparator( + string memory name, + string memory version, + uint256 chainId + ) internal view returns (bytes32) { + return + keccak256( + abi.encode( + // keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)") + 0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f, + keccak256(bytes(name)), + keccak256(bytes(version)), + chainId, + address(this) + ) + ); + } + + /** + * @notice Make EIP712 domain separator + * @param name Contract name + * @param version Contract version + * @return Domain separator + */ + function makeDomainSeparator(string memory name, string memory version) + internal + view + returns (bytes32) + { + uint256 chainId; + assembly { + chainId := chainid() + } + return makeDomainSeparator(name, version, chainId); + } +} + +// contracts/util/MessageHashUtils.sol +/** + * Copyright 2023 Circle Internet Group, Inc. All rights reserved. + * + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @dev Signature message hash utilities for producing digests to be consumed by {ECDSA} recovery or signing. + * + * The library provides methods for generating a hash of a message that conforms to the + * https://eips.ethereum.org/EIPS/eip-191[EIP 191] and https://eips.ethereum.org/EIPS/eip-712[EIP 712] + * specifications. + */ +library MessageHashUtils { + /** + * @dev Returns the keccak256 digest of an EIP-712 typed data (EIP-191 version `0x01`). + * Adapted from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/21bb89ef5bfc789b9333eb05e3ba2b7b284ac77c/contracts/utils/cryptography/MessageHashUtils.sol + * + * The digest is calculated from a `domainSeparator` and a `structHash`, by prefixing them with + * `\x19\x01` and hashing the result. It corresponds to the hash signed by the + * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] JSON-RPC method as part of EIP-712. + * + * @param domainSeparator Domain separator + * @param structHash Hashed EIP-712 data struct + * @return digest The keccak256 digest of an EIP-712 typed data + */ + function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) + internal + pure + returns (bytes32 digest) + { + assembly { + let ptr := mload(0x40) + mstore(ptr, "\x19\x01") + mstore(add(ptr, 0x02), domainSeparator) + mstore(add(ptr, 0x22), structHash) + digest := keccak256(ptr, 0x42) + } + } +} + +// contracts/v1/Ownable.sol +/** + + * + * Copyright (c) 2018 zOS Global Limited. + * Copyright (c) 2018-2020 CENTRE SECZ + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/** + * @notice The Ownable contract has an owner address, and provides basic + * authorization control functions + * @dev Forked from https://github.com/OpenZeppelin/openzeppelin-labs/blob/3887ab77b8adafba4a26ace002f3a684c1a3388b/upgradeability_ownership/contracts/ownership/Ownable.sol + * Modifications: + * 1. Consolidate OwnableStorage into this contract (7/13/18) + * 2. Reformat, conform to Solidity 0.6 syntax, and add error messages (5/13/20) + * 3. Make public functions external (5/27/20) + */ +contract Ownable { + // Owner of the contract + address private _owner; + + /** + * @dev Event to show ownership has been transferred + * @param previousOwner representing the address of the previous owner + * @param newOwner representing the address of the new owner + */ + event OwnershipTransferred(address previousOwner, address newOwner); + + /** + * @dev The constructor sets the original owner of the contract to the sender account. + */ + constructor() { + setOwner(msg.sender); + } + + /** + * @dev Tells the address of the owner + * @return the address of the owner + */ + function owner() external view returns (address) { + return _owner; + } + + /** + * @dev Sets a new owner address + */ + function setOwner(address newOwner) internal { + _owner = newOwner; + } + + /** + * @dev Throws if called by any account other than the owner. + */ + modifier onlyOwner() { + require(msg.sender == _owner, "Ownable: caller is not the owner"); + _; + } + + /** + * @dev Allows the current owner to transfer control of the contract to a newOwner. + * @param newOwner The address to transfer ownership to. + */ + function transferOwnership(address newOwner) external onlyOwner { + require( + newOwner != address(0), + "Ownable: new owner is the zero address" + ); + emit OwnershipTransferred(_owner, newOwner); + setOwner(newOwner); + } +} + +// contracts/v2/EIP712Domain.sol +/** + * Copyright 2023 Circle Internet Group, Inc. All rights reserved. + * + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// solhint-disable func-name-mixedcase + +/** + * @title EIP712 Domain + */ +contract EIP712Domain { + // was originally DOMAIN_SEPARATOR + // but that has been moved to a method so we can override it in V2_2+ + bytes32 internal _DEPRECATED_CACHED_DOMAIN_SEPARATOR; + + /** + * @notice Get the EIP712 Domain Separator. + * @return The bytes32 EIP712 domain separator. + */ + function DOMAIN_SEPARATOR() external view returns (bytes32) { + return _domainSeparator(); + } + + /** + * @dev Internal method to get the EIP712 Domain Separator. + * @return The bytes32 EIP712 domain separator. + */ + function _domainSeparator() internal virtual view returns (bytes32) { + return _DEPRECATED_CACHED_DOMAIN_SEPARATOR; + } +} + +// node_modules/@openzeppelin/contracts/math/SafeMath.sol + +/** + * @dev Wrappers over Solidity's arithmetic operations with added overflow + * checks. + * + * Arithmetic operations in Solidity wrap on overflow. This can easily result + * in bugs, because programmers usually assume that an overflow raises an + * error, which is the standard behavior in high level programming languages. + * `SafeMath` restores this intuition by reverting the transaction when an + * operation overflows. + * + * Using this library instead of the unchecked operations eliminates an entire + * class of bugs, so it's recommended to use it always. + */ +library SafeMath { + /** + * @dev Returns the addition of two unsigned integers, with an overflow flag. + * + * _Available since v3.4._ + */ + function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) { + unchecked { + uint256 c = a + b; + if (c < a) return (false, 0); + return (true, c); + } + } + + /** + * @dev Returns the substraction of two unsigned integers, with an overflow flag. + * + * _Available since v3.4._ + */ + function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) { + if (b > a) return (false, 0); + unchecked { return (true, a - b); } + } + + /** + * @dev Returns the multiplication of two unsigned integers, with an overflow flag. + * + * _Available since v3.4._ + */ + function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) { + unchecked { + // Gas optimization: this is cheaper than requiring 'a' not being zero, but the + // benefit is lost if 'b' is also tested. + // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 + if (a == 0) return (true, 0); + uint256 c = a * b; + if (c / a != b) return (false, 0); + return (true, c); + } + } + + /** + * @dev Returns the division of two unsigned integers, with a division by zero flag. + * + * _Available since v3.4._ + */ + function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) { + if (b == 0) return (false, 0); + unchecked { return (true, a / b); } + } + + /** + * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag. + * + * _Available since v3.4._ + */ + function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) { + if (b == 0) return (false, 0); + unchecked { return (true, a % b); } + } + + /** + * @dev Returns the addition of two unsigned integers, reverting on + * overflow. + * + * Counterpart to Solidity's `+` operator. + * + * Requirements: + * + * - Addition cannot overflow. + */ + function add(uint256 a, uint256 b) internal pure returns (uint256) { + unchecked { + uint256 c = a + b; + require(c >= a, "SafeMath: addition overflow"); + return c; + } + } + + /** + * @dev Returns the subtraction of two unsigned integers, reverting on + * overflow (when the result is negative). + * + * Counterpart to Solidity's `-` operator. + * + * Requirements: + * + * - Subtraction cannot overflow. + */ + function sub(uint256 a, uint256 b) internal pure returns (uint256) { + require(b <= a, "SafeMath: subtraction overflow"); + unchecked { return a - b; } + } + + /** + * @dev Returns the multiplication of two unsigned integers, reverting on + * overflow. + * + * Counterpart to Solidity's `*` operator. + * + * Requirements: + * + * - Multiplication cannot overflow. + */ + function mul(uint256 a, uint256 b) internal pure returns (uint256) { + if (a == 0) return 0; + unchecked { + uint256 c = a * b; + require(c / a == b, "SafeMath: multiplication overflow"); + return c; + } + } + + /** + * @dev Returns the integer division of two unsigned integers, reverting on + * division by zero. The result is rounded towards zero. + * + * Counterpart to Solidity's `/` operator. Note: this function uses a + * `revert` opcode (which leaves remaining gas untouched) while Solidity + * uses an invalid opcode to revert (consuming all remaining gas). + * + * Requirements: + * + * - The divisor cannot be zero. + */ + function div(uint256 a, uint256 b) internal pure returns (uint256) { + require(b > 0, "SafeMath: division by zero"); + unchecked { return a / b; } + } + + /** + * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), + * reverting when dividing by zero. + * + * Counterpart to Solidity's `%` operator. This function uses a `revert` + * opcode (which leaves remaining gas untouched) while Solidity uses an + * invalid opcode to revert (consuming all remaining gas). + * + * Requirements: + * + * - The divisor cannot be zero. + */ + function mod(uint256 a, uint256 b) internal pure returns (uint256) { + require(b > 0, "SafeMath: modulo by zero"); + unchecked { return a % b; } + } + + /** + * @dev Returns the subtraction of two unsigned integers, reverting with custom message on + * overflow (when the result is negative). + * + * CAUTION: This function is deprecated because it requires allocating memory for the error + * message unnecessarily. For custom revert reasons use {trySub}. + * + * Counterpart to Solidity's `-` operator. + * + * Requirements: + * + * - Subtraction cannot overflow. + */ + function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { + require(b <= a, errorMessage); + unchecked { return a - b; } + } + + /** + * @dev Returns the integer division of two unsigned integers, reverting with custom message on + * division by zero. The result is rounded towards zero. + * + * CAUTION: This function is deprecated because it requires allocating memory for the error + * message unnecessarily. For custom revert reasons use {tryDiv}. + * + * Counterpart to Solidity's `/` operator. Note: this function uses a + * `revert` opcode (which leaves remaining gas untouched) while Solidity + * uses an invalid opcode to revert (consuming all remaining gas). + * + * Requirements: + * + * - The divisor cannot be zero. + */ + function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { + require(b > 0, errorMessage); + unchecked { return a / b; } + } + + /** + * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), + * reverting with custom message when dividing by zero. + * + * CAUTION: This function is deprecated because it requires allocating memory for the error + * message unnecessarily. For custom revert reasons use {tryMod}. + * + * Counterpart to Solidity's `%` operator. This function uses a `revert` + * opcode (which leaves remaining gas untouched) while Solidity uses an + * invalid opcode to revert (consuming all remaining gas). + * + * Requirements: + * + * - The divisor cannot be zero. + */ + function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { + require(b > 0, errorMessage); + unchecked { return a % b;} + } +} + +// node_modules/@openzeppelin/contracts/token/ERC20/IERC20.sol + +/** + * @dev Interface of the ERC20 standard as defined in the EIP. + */ +interface IERC20 { + /** + * @dev Returns the amount of tokens in existence. + */ + function totalSupply() external view returns (uint256); + + /** + * @dev Returns the amount of tokens owned by `account`. + */ + function balanceOf(address account) external view returns (uint256); + + /** + * @dev Moves `amount` tokens from the caller's account to `recipient`. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * Emits a {Transfer} event. + */ + function transfer(address recipient, uint256 amount) external returns (bool); + + /** + * @dev Returns the remaining number of tokens that `spender` will be + * allowed to spend on behalf of `owner` through {transferFrom}. This is + * zero by default. + * + * This value changes when {approve} or {transferFrom} are called. + */ + function allowance(address owner, address spender) external view returns (uint256); + + /** + * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * IMPORTANT: Beware that changing an allowance with this method brings the risk + * that someone may use both the old and the new allowance by unfortunate + * transaction ordering. One possible solution to mitigate this race + * condition is to first reduce the spender's allowance to 0 and set the + * desired value afterwards: + * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 + * + * Emits an {Approval} event. + */ + function approve(address spender, uint256 amount) external returns (bool); + + /** + * @dev Moves `amount` tokens from `sender` to `recipient` using the + * allowance mechanism. `amount` is then deducted from the caller's + * allowance. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * Emits a {Transfer} event. + */ + function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); + + /** + * @dev Emitted when `value` tokens are moved from one account (`from`) to + * another (`to`). + * + * Note that `value` may be zero. + */ + event Transfer(address indexed from, address indexed to, uint256 value); + + /** + * @dev Emitted when the allowance of a `spender` for an `owner` is set by + * a call to {approve}. `value` is the new allowance. + */ + event Approval(address indexed owner, address indexed spender, uint256 value); +} + +// contracts/v1/AbstractFiatTokenV1.sol +/** + * Copyright 2023 Circle Internet Group, Inc. All rights reserved. + * + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +abstract contract AbstractFiatTokenV1 is IERC20 { + function _approve( + address owner, + address spender, + uint256 value + ) internal virtual; + + function _transfer( + address from, + address to, + uint256 value + ) internal virtual; +} + +// contracts/v1/Blacklistable.sol +/** + * Copyright 2023 Circle Internet Group, Inc. All rights reserved. + * + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @title Blacklistable Token + * @dev Allows accounts to be blacklisted by a "blacklister" role + */ +abstract contract Blacklistable is Ownable { + address public blacklister; + mapping(address => bool) internal _deprecatedBlacklisted; + + event Blacklisted(address indexed _account); + event UnBlacklisted(address indexed _account); + event BlacklisterChanged(address indexed newBlacklister); + + /** + * @dev Throws if called by any account other than the blacklister. + */ + modifier onlyBlacklister() { + require( + msg.sender == blacklister, + "Blacklistable: caller is not the blacklister" + ); + _; + } + + /** + * @dev Throws if argument account is blacklisted. + * @param _account The address to check. + */ + modifier notBlacklisted(address _account) { + require( + !_isBlacklisted(_account), + "Blacklistable: account is blacklisted" + ); + _; + } + + /** + * @notice Checks if account is blacklisted. + * @param _account The address to check. + * @return True if the account is blacklisted, false if the account is not blacklisted. + */ + function isBlacklisted(address _account) external view returns (bool) { + return _isBlacklisted(_account); + } + + /** + * @notice Adds account to blacklist. + * @param _account The address to blacklist. + */ + function blacklist(address _account) external onlyBlacklister { + _blacklist(_account); + emit Blacklisted(_account); + } + + /** + * @notice Removes account from blacklist. + * @param _account The address to remove from the blacklist. + */ + function unBlacklist(address _account) external onlyBlacklister { + _unBlacklist(_account); + emit UnBlacklisted(_account); + } + + /** + * @notice Updates the blacklister address. + * @param _newBlacklister The address of the new blacklister. + */ + function updateBlacklister(address _newBlacklister) external onlyOwner { + require( + _newBlacklister != address(0), + "Blacklistable: new blacklister is the zero address" + ); + blacklister = _newBlacklister; + emit BlacklisterChanged(blacklister); + } + + /** + * @dev Checks if account is blacklisted. + * @param _account The address to check. + * @return true if the account is blacklisted, false otherwise. + */ + function _isBlacklisted(address _account) + internal + virtual + view + returns (bool); + + /** + * @dev Helper method that blacklists an account. + * @param _account The address to blacklist. + */ + function _blacklist(address _account) internal virtual; + + /** + * @dev Helper method that unblacklists an account. + * @param _account The address to unblacklist. + */ + function _unBlacklist(address _account) internal virtual; +} + +// contracts/v1/Pausable.sol +/** + + * + * Copyright (c) 2016 Smart Contract Solutions, Inc. + * Copyright (c) 2018-2020 CENTRE SECZ + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/** + * @notice Base contract which allows children to implement an emergency stop + * mechanism + * @dev Forked from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/feb665136c0dae9912e08397c1a21c4af3651ef3/contracts/lifecycle/Pausable.sol + * Modifications: + * 1. Added pauser role, switched pause/unpause to be onlyPauser (6/14/2018) + * 2. Removed whenNotPause/whenPaused from pause/unpause (6/14/2018) + * 3. Removed whenPaused (6/14/2018) + * 4. Switches ownable library to use ZeppelinOS (7/12/18) + * 5. Remove constructor (7/13/18) + * 6. Reformat, conform to Solidity 0.6 syntax and add error messages (5/13/20) + * 7. Make public functions external (5/27/20) + */ +contract Pausable is Ownable { + event Pause(); + event Unpause(); + event PauserChanged(address indexed newAddress); + + address public pauser; + bool public paused = false; + + /** + * @dev Modifier to make a function callable only when the contract is not paused. + */ + modifier whenNotPaused() { + require(!paused, "Pausable: paused"); + _; + } + + /** + * @dev throws if called by any account other than the pauser + */ + modifier onlyPauser() { + require(msg.sender == pauser, "Pausable: caller is not the pauser"); + _; + } + + /** + * @dev called by the owner to pause, triggers stopped state + */ + function pause() external onlyPauser { + paused = true; + emit Pause(); + } + + /** + * @dev called by the owner to unpause, returns to normal state + */ + function unpause() external onlyPauser { + paused = false; + emit Unpause(); + } + + /** + * @notice Updates the pauser address. + * @param _newPauser The address of the new pauser. + */ + function updatePauser(address _newPauser) external onlyOwner { + require( + _newPauser != address(0), + "Pausable: new pauser is the zero address" + ); + pauser = _newPauser; + emit PauserChanged(pauser); + } +} + +// contracts/util/SignatureChecker.sol +/** + * Copyright 2023 Circle Internet Group, Inc. All rights reserved. + * + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @dev Signature verification helper that can be used instead of `ECRecover.recover` to seamlessly support both ECDSA + * signatures from externally owned accounts (EOAs) as well as ERC1271 signatures from smart contract wallets. + * + * Adapted from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/21bb89ef5bfc789b9333eb05e3ba2b7b284ac77c/contracts/utils/cryptography/SignatureChecker.sol + */ +library SignatureChecker { + /** + * @dev Checks if a signature is valid for a given signer and data hash. If the signer is a smart contract, the + * signature is validated against that smart contract using ERC1271, otherwise it's validated using `ECRecover.recover`. + * @param signer Address of the claimed signer + * @param digest Keccak-256 hash digest of the signed message + * @param signature Signature byte array associated with hash + */ + function isValidSignatureNow( + address signer, + bytes32 digest, + bytes memory signature + ) external view returns (bool) { + if (!isContract(signer)) { + return ECRecover.recover(digest, signature) == signer; + } + return isValidERC1271SignatureNow(signer, digest, signature); + } + + /** + * @dev Checks if a signature is valid for a given signer and data hash. The signature is validated + * against the signer smart contract using ERC1271. + * @param signer Address of the claimed signer + * @param digest Keccak-256 hash digest of the signed message + * @param signature Signature byte array associated with hash + * + * NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus + * change through time. It could return true at block N and false at block N+1 (or the opposite). + */ + function isValidERC1271SignatureNow( + address signer, + bytes32 digest, + bytes memory signature + ) internal view returns (bool) { + (bool success, bytes memory result) = signer.staticcall( + abi.encodeWithSelector( + IERC1271.isValidSignature.selector, + digest, + signature + ) + ); + return (success && + result.length >= 32 && + abi.decode(result, (bytes32)) == + bytes32(IERC1271.isValidSignature.selector)); + } + + /** + * @dev Checks if the input address is a smart contract. + */ + function isContract(address addr) internal view returns (bool) { + uint256 size; + assembly { + size := extcodesize(addr) + } + return size > 0; + } +} + +// contracts/v2/AbstractFiatTokenV2.sol +/** + * Copyright 2023 Circle Internet Group, Inc. All rights reserved. + * + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +abstract contract AbstractFiatTokenV2 is AbstractFiatTokenV1 { + function _increaseAllowance( + address owner, + address spender, + uint256 increment + ) internal virtual; + + function _decreaseAllowance( + address owner, + address spender, + uint256 decrement + ) internal virtual; +} + +// node_modules/@openzeppelin/contracts/token/ERC20/SafeERC20.sol + +/** + * @title SafeERC20 + * @dev Wrappers around ERC20 operations that throw on failure (when the token + * contract returns false). Tokens that return no value (and instead revert or + * throw on failure) are also supported, non-reverting calls are assumed to be + * successful. + * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, + * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. + */ +library SafeERC20 { + using SafeMath for uint256; + using Address for address; + + function safeTransfer(IERC20 token, address to, uint256 value) internal { + _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); + } + + function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { + _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); + } + + /** + * @dev Deprecated. This function has issues similar to the ones found in + * {IERC20-approve}, and its usage is discouraged. + * + * Whenever possible, use {safeIncreaseAllowance} and + * {safeDecreaseAllowance} instead. + */ + function safeApprove(IERC20 token, address spender, uint256 value) internal { + // safeApprove should only be called when setting an initial allowance, + // or when resetting it to zero. To increase and decrease it, use + // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' + // solhint-disable-next-line max-line-length + require((value == 0) || (token.allowance(address(this), spender) == 0), + "SafeERC20: approve from non-zero to non-zero allowance" + ); + _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); + } + + function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { + uint256 newAllowance = token.allowance(address(this), spender).add(value); + _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); + } + + function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal { + uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero"); + _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); + } + + /** + * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement + * on the return value: the return value is optional (but if data is returned, it must not be false). + * @param token The token targeted by the call. + * @param data The call data (encoded using abi.encode or one of its variants). + */ + function _callOptionalReturn(IERC20 token, bytes memory data) private { + // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since + // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that + // the target address contains contract code and also asserts for success in the low-level call. + + bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); + if (returndata.length > 0) { // Return data is optional + // solhint-disable-next-line max-line-length + require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); + } + } +} + +// contracts/v1.1/Rescuable.sol +/** + * Copyright 2023 Circle Internet Group, Inc. All rights reserved. + * + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +contract Rescuable is Ownable { + using SafeERC20 for IERC20; + + address private _rescuer; + + event RescuerChanged(address indexed newRescuer); + + /** + * @notice Returns current rescuer + * @return Rescuer's address + */ + function rescuer() external view returns (address) { + return _rescuer; + } + + /** + * @notice Revert if called by any account other than the rescuer. + */ + modifier onlyRescuer() { + require(msg.sender == _rescuer, "Rescuable: caller is not the rescuer"); + _; + } + + /** + * @notice Rescue ERC20 tokens locked up in this contract. + * @param tokenContract ERC20 token contract address + * @param to Recipient address + * @param amount Amount to withdraw + */ + function rescueERC20( + IERC20 tokenContract, + address to, + uint256 amount + ) external onlyRescuer { + tokenContract.safeTransfer(to, amount); + } + + /** + * @notice Updates the rescuer address. + * @param newRescuer The address of the new rescuer. + */ + function updateRescuer(address newRescuer) external onlyOwner { + require( + newRescuer != address(0), + "Rescuable: new rescuer is the zero address" + ); + _rescuer = newRescuer; + emit RescuerChanged(newRescuer); + } +} + +// contracts/v1/FiatTokenV1.sol +/** + * Copyright 2023 Circle Internet Group, Inc. All rights reserved. + * + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @title FiatToken + * @dev ERC20 Token backed by fiat reserves + */ +contract FiatTokenV1 is AbstractFiatTokenV1, Ownable, Pausable, Blacklistable { + using SafeMath for uint256; + + string public name; + string public symbol; + uint8 public decimals; + string public currency; + address public masterMinter; + bool internal initialized; + + /// @dev A mapping that stores the balance and blacklist states for a given address. + /// The first bit defines whether the address is blacklisted (1 if blacklisted, 0 otherwise). + /// The last 255 bits define the balance for the address. + mapping(address => uint256) internal balanceAndBlacklistStates; + mapping(address => mapping(address => uint256)) internal allowed; + uint256 internal totalSupply_ = 0; + mapping(address => bool) internal minters; + mapping(address => uint256) internal minterAllowed; + + event Mint(address indexed minter, address indexed to, uint256 amount); + event Burn(address indexed burner, uint256 amount); + event MinterConfigured(address indexed minter, uint256 minterAllowedAmount); + event MinterRemoved(address indexed oldMinter); + event MasterMinterChanged(address indexed newMasterMinter); + + /** + * @notice Initializes the fiat token contract. + * @param tokenName The name of the fiat token. + * @param tokenSymbol The symbol of the fiat token. + * @param tokenCurrency The fiat currency that the token represents. + * @param tokenDecimals The number of decimals that the token uses. + * @param newMasterMinter The masterMinter address for the fiat token. + * @param newPauser The pauser address for the fiat token. + * @param newBlacklister The blacklister address for the fiat token. + * @param newOwner The owner of the fiat token. + */ + function initialize( + string memory tokenName, + string memory tokenSymbol, + string memory tokenCurrency, + uint8 tokenDecimals, + address newMasterMinter, + address newPauser, + address newBlacklister, + address newOwner + ) public { + require(!initialized, "FiatToken: contract is already initialized"); + require( + newMasterMinter != address(0), + "FiatToken: new masterMinter is the zero address" + ); + require( + newPauser != address(0), + "FiatToken: new pauser is the zero address" + ); + require( + newBlacklister != address(0), + "FiatToken: new blacklister is the zero address" + ); + require( + newOwner != address(0), + "FiatToken: new owner is the zero address" + ); + + name = tokenName; + symbol = tokenSymbol; + currency = tokenCurrency; + decimals = tokenDecimals; + masterMinter = newMasterMinter; + pauser = newPauser; + blacklister = newBlacklister; + setOwner(newOwner); + initialized = true; + } + + /** + * @dev Throws if called by any account other than a minter. + */ + modifier onlyMinters() { + require(minters[msg.sender], "FiatToken: caller is not a minter"); + _; + } + + /** + * @notice Mints fiat tokens to an address. + * @param _to The address that will receive the minted tokens. + * @param _amount The amount of tokens to mint. Must be less than or equal + * to the minterAllowance of the caller. + * @return True if the operation was successful. + */ + function mint(address _to, uint256 _amount) + external + whenNotPaused + onlyMinters + notBlacklisted(msg.sender) + notBlacklisted(_to) + returns (bool) + { + require(_to != address(0), "FiatToken: mint to the zero address"); + require(_amount > 0, "FiatToken: mint amount not greater than 0"); + + uint256 mintingAllowedAmount = minterAllowed[msg.sender]; + require( + _amount <= mintingAllowedAmount, + "FiatToken: mint amount exceeds minterAllowance" + ); + + totalSupply_ = totalSupply_.add(_amount); + _setBalance(_to, _balanceOf(_to).add(_amount)); + minterAllowed[msg.sender] = mintingAllowedAmount.sub(_amount); + emit Mint(msg.sender, _to, _amount); + emit Transfer(address(0), _to, _amount); + return true; + } + + /** + * @dev Throws if called by any account other than the masterMinter + */ + modifier onlyMasterMinter() { + require( + msg.sender == masterMinter, + "FiatToken: caller is not the masterMinter" + ); + _; + } + + /** + * @notice Gets the minter allowance for an account. + * @param minter The address to check. + * @return The remaining minter allowance for the account. + */ + function minterAllowance(address minter) external view returns (uint256) { + return minterAllowed[minter]; + } + + /** + * @notice Checks if an account is a minter. + * @param account The address to check. + * @return True if the account is a minter, false if the account is not a minter. + */ + function isMinter(address account) external view returns (bool) { + return minters[account]; + } + + /** + * @notice Gets the remaining amount of fiat tokens a spender is allowed to transfer on + * behalf of the token owner. + * @param owner The token owner's address. + * @param spender The spender's address. + * @return The remaining allowance. + */ + function allowance(address owner, address spender) + external + override + view + returns (uint256) + { + return allowed[owner][spender]; + } + + /** + * @notice Gets the totalSupply of the fiat token. + * @return The totalSupply of the fiat token. + */ + function totalSupply() external override view returns (uint256) { + return totalSupply_; + } + + /** + * @notice Gets the fiat token balance of an account. + * @param account The address to check. + * @return balance The fiat token balance of the account. + */ + function balanceOf(address account) + external + override + view + returns (uint256) + { + return _balanceOf(account); + } + + /** + * @notice Sets a fiat token allowance for a spender to spend on behalf of the caller. + * @param spender The spender's address. + * @param value The allowance amount. + * @return True if the operation was successful. + */ + function approve(address spender, uint256 value) + external + virtual + override + whenNotPaused + notBlacklisted(msg.sender) + notBlacklisted(spender) + returns (bool) + { + _approve(msg.sender, spender, value); + return true; + } + + /** + * @dev Internal function to set allowance. + * @param owner Token owner's address. + * @param spender Spender's address. + * @param value Allowance amount. + */ + function _approve( + address owner, + address spender, + uint256 value + ) internal override { + require(owner != address(0), "ERC20: approve from the zero address"); + require(spender != address(0), "ERC20: approve to the zero address"); + allowed[owner][spender] = value; + emit Approval(owner, spender, value); + } + + /** + * @notice Transfers tokens from an address to another by spending the caller's allowance. + * @dev The caller must have some fiat token allowance on the payer's tokens. + * @param from Payer's address. + * @param to Payee's address. + * @param value Transfer amount. + * @return True if the operation was successful. + */ + function transferFrom( + address from, + address to, + uint256 value + ) + external + override + whenNotPaused + notBlacklisted(msg.sender) + notBlacklisted(from) + notBlacklisted(to) + returns (bool) + { + require( + value <= allowed[from][msg.sender], + "ERC20: transfer amount exceeds allowance" + ); + _transfer(from, to, value); + allowed[from][msg.sender] = allowed[from][msg.sender].sub(value); + return true; + } + + /** + * @notice Transfers tokens from the caller. + * @param to Payee's address. + * @param value Transfer amount. + * @return True if the operation was successful. + */ + function transfer(address to, uint256 value) + external + override + whenNotPaused + notBlacklisted(msg.sender) + notBlacklisted(to) + returns (bool) + { + _transfer(msg.sender, to, value); + return true; + } + + /** + * @dev Internal function to process transfers. + * @param from Payer's address. + * @param to Payee's address. + * @param value Transfer amount. + */ + function _transfer( + address from, + address to, + uint256 value + ) internal override { + require(from != address(0), "ERC20: transfer from the zero address"); + require(to != address(0), "ERC20: transfer to the zero address"); + require( + value <= _balanceOf(from), + "ERC20: transfer amount exceeds balance" + ); + + _setBalance(from, _balanceOf(from).sub(value)); + _setBalance(to, _balanceOf(to).add(value)); + emit Transfer(from, to, value); + } + + /** + * @notice Adds or updates a new minter with a mint allowance. + * @param minter The address of the minter. + * @param minterAllowedAmount The minting amount allowed for the minter. + * @return True if the operation was successful. + */ + function configureMinter(address minter, uint256 minterAllowedAmount) + external + whenNotPaused + onlyMasterMinter + returns (bool) + { + minters[minter] = true; + minterAllowed[minter] = minterAllowedAmount; + emit MinterConfigured(minter, minterAllowedAmount); + return true; + } + + /** + * @notice Removes a minter. + * @param minter The address of the minter to remove. + * @return True if the operation was successful. + */ + function removeMinter(address minter) + external + onlyMasterMinter + returns (bool) + { + minters[minter] = false; + minterAllowed[minter] = 0; + emit MinterRemoved(minter); + return true; + } + + /** + * @notice Allows a minter to burn some of its own tokens. + * @dev The caller must be a minter, must not be blacklisted, and the amount to burn + * should be less than or equal to the account's balance. + * @param _amount the amount of tokens to be burned. + */ + function burn(uint256 _amount) + external + whenNotPaused + onlyMinters + notBlacklisted(msg.sender) + { + uint256 balance = _balanceOf(msg.sender); + require(_amount > 0, "FiatToken: burn amount not greater than 0"); + require(balance >= _amount, "FiatToken: burn amount exceeds balance"); + + totalSupply_ = totalSupply_.sub(_amount); + _setBalance(msg.sender, balance.sub(_amount)); + emit Burn(msg.sender, _amount); + emit Transfer(msg.sender, address(0), _amount); + } + + /** + * @notice Updates the master minter address. + * @param _newMasterMinter The address of the new master minter. + */ + function updateMasterMinter(address _newMasterMinter) external onlyOwner { + require( + _newMasterMinter != address(0), + "FiatToken: new masterMinter is the zero address" + ); + masterMinter = _newMasterMinter; + emit MasterMinterChanged(masterMinter); + } + + /** + * + */ + function _blacklist(address _account) internal override { + _setBlacklistState(_account, true); + } + + /** + * + */ + function _unBlacklist(address _account) internal override { + _setBlacklistState(_account, false); + } + + /** + * @dev Helper method that sets the blacklist state of an account. + * @param _account The address of the account. + * @param _shouldBlacklist True if the account should be blacklisted, false if the account should be unblacklisted. + */ + function _setBlacklistState(address _account, bool _shouldBlacklist) + internal + virtual + { + _deprecatedBlacklisted[_account] = _shouldBlacklist; + } + + /** + * @dev Helper method that sets the balance of an account. + * @param _account The address of the account. + * @param _balance The new fiat token balance of the account. + */ + function _setBalance(address _account, uint256 _balance) internal virtual { + balanceAndBlacklistStates[_account] = _balance; + } + + /** + * + */ + function _isBlacklisted(address _account) + internal + virtual + override + view + returns (bool) + { + return _deprecatedBlacklisted[_account]; + } + + /** + * @dev Helper method to obtain the balance of an account. + * @param _account The address of the account. + * @return The fiat token balance of the account. + */ + function _balanceOf(address _account) + internal + virtual + view + returns (uint256) + { + return balanceAndBlacklistStates[_account]; + } +} + +// contracts/v2/EIP2612.sol +/** + * Copyright 2023 Circle Internet Group, Inc. All rights reserved. + * + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @title EIP-2612 + * @notice Provide internal implementation for gas-abstracted approvals + */ +abstract contract EIP2612 is AbstractFiatTokenV2, EIP712Domain { + // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)") + bytes32 + public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9; + + mapping(address => uint256) private _permitNonces; + + /** + * @notice Nonces for permit + * @param owner Token owner's address (Authorizer) + * @return Next nonce + */ + function nonces(address owner) external view returns (uint256) { + return _permitNonces[owner]; + } + + /** + * @notice Verify a signed approval permit and execute if valid + * @param owner Token owner's address (Authorizer) + * @param spender Spender's address + * @param value Amount of allowance + * @param deadline The time at which the signature expires (unix time), or max uint256 value to signal no expiration + * @param v v of the signature + * @param r r of the signature + * @param s s of the signature + */ + function _permit( + address owner, + address spender, + uint256 value, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) internal { + _permit(owner, spender, value, deadline, abi.encodePacked(r, s, v)); + } + + /** + * @notice Verify a signed approval permit and execute if valid + * @dev EOA wallet signatures should be packed in the order of r, s, v. + * @param owner Token owner's address (Authorizer) + * @param spender Spender's address + * @param value Amount of allowance + * @param deadline The time at which the signature expires (unix time), or max uint256 value to signal no expiration + * @param signature Signature byte array signed by an EOA wallet or a contract wallet + */ + function _permit( + address owner, + address spender, + uint256 value, + uint256 deadline, + bytes memory signature + ) internal { + require( + deadline == type(uint256).max || deadline >= block.timestamp, + "FiatTokenV2: permit is expired" + ); + unchecked { + bytes32 typedDataHash = MessageHashUtils.toTypedDataHash( + _domainSeparator(), + keccak256( + abi.encode( + PERMIT_TYPEHASH, + owner, + spender, + value, + _permitNonces[owner]++, + deadline + ) + ) + ); + require( + SignatureChecker.isValidSignatureNow( + owner, + typedDataHash, + signature + ), + "EIP2612: invalid signature" + ); + } + + _approve(owner, spender, value); + } +} + +// contracts/v2/EIP3009.sol +/** + * Copyright 2023 Circle Internet Group, Inc. All rights reserved. + * + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @title EIP-3009 + * @notice Provide internal implementation for gas-abstracted transfers + * @dev Contracts that inherit from this must wrap these with publicly + * accessible functions, optionally adding modifiers where necessary + */ +abstract contract EIP3009 is AbstractFiatTokenV2, EIP712Domain { + // keccak256("TransferWithAuthorization(address from,address to,uint256 value,uint256 validAfter,uint256 validBefore,bytes32 nonce)") + bytes32 + public constant TRANSFER_WITH_AUTHORIZATION_TYPEHASH = 0x7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a2267; + + // keccak256("ReceiveWithAuthorization(address from,address to,uint256 value,uint256 validAfter,uint256 validBefore,bytes32 nonce)") + bytes32 + public constant RECEIVE_WITH_AUTHORIZATION_TYPEHASH = 0xd099cc98ef71107a616c4f0f941f04c322d8e254fe26b3c6668db87aae413de8; + + // keccak256("CancelAuthorization(address authorizer,bytes32 nonce)") + bytes32 + public constant CANCEL_AUTHORIZATION_TYPEHASH = 0x158b0a9edf7a828aad02f63cd515c68ef2f50ba807396f6d12842833a1597429; + + /** + * @dev authorizer address => nonce => bool (true if nonce is used) + */ + mapping(address => mapping(bytes32 => bool)) private _authorizationStates; + + event AuthorizationUsed(address indexed authorizer, bytes32 indexed nonce); + event AuthorizationCanceled( + address indexed authorizer, + bytes32 indexed nonce + ); + + /** + * @notice Returns the state of an authorization + * @dev Nonces are randomly generated 32-byte data unique to the + * authorizer's address + * @param authorizer Authorizer's address + * @param nonce Nonce of the authorization + * @return True if the nonce is used + */ + function authorizationState(address authorizer, bytes32 nonce) + external + view + returns (bool) + { + return _authorizationStates[authorizer][nonce]; + } + + /** + * @notice Execute a transfer with a signed authorization + * @param from Payer's address (Authorizer) + * @param to Payee's address + * @param value Amount to be transferred + * @param validAfter The time after which this is valid (unix time) + * @param validBefore The time before which this is valid (unix time) + * @param nonce Unique nonce + * @param v v of the signature + * @param r r of the signature + * @param s s of the signature + */ + function _transferWithAuthorization( + address from, + address to, + uint256 value, + uint256 validAfter, + uint256 validBefore, + bytes32 nonce, + uint8 v, + bytes32 r, + bytes32 s + ) internal { + _transferWithAuthorization( + from, + to, + value, + validAfter, + validBefore, + nonce, + abi.encodePacked(r, s, v) + ); + } + + /** + * @notice Execute a transfer with a signed authorization + * @dev EOA wallet signatures should be packed in the order of r, s, v. + * @param from Payer's address (Authorizer) + * @param to Payee's address + * @param value Amount to be transferred + * @param validAfter The time after which this is valid (unix time) + * @param validBefore The time before which this is valid (unix time) + * @param nonce Unique nonce + * @param signature Signature byte array produced by an EOA wallet or a contract wallet + */ + function _transferWithAuthorization( + address from, + address to, + uint256 value, + uint256 validAfter, + uint256 validBefore, + bytes32 nonce, + bytes memory signature + ) internal { + _requireValidAuthorization(from, nonce, validAfter, validBefore); + _requireValidSignature( + from, + keccak256( + abi.encode( + TRANSFER_WITH_AUTHORIZATION_TYPEHASH, + from, + to, + value, + validAfter, + validBefore, + nonce + ) + ), + signature + ); + + _markAuthorizationAsUsed(from, nonce); + _transfer(from, to, value); + } + + /** + * @notice Receive a transfer with a signed authorization from the payer + * @dev This has an additional check to ensure that the payee's address + * matches the caller of this function to prevent front-running attacks. + * @param from Payer's address (Authorizer) + * @param to Payee's address + * @param value Amount to be transferred + * @param validAfter The time after which this is valid (unix time) + * @param validBefore The time before which this is valid (unix time) + * @param nonce Unique nonce + * @param v v of the signature + * @param r r of the signature + * @param s s of the signature + */ + function _receiveWithAuthorization( + address from, + address to, + uint256 value, + uint256 validAfter, + uint256 validBefore, + bytes32 nonce, + uint8 v, + bytes32 r, + bytes32 s + ) internal { + _receiveWithAuthorization( + from, + to, + value, + validAfter, + validBefore, + nonce, + abi.encodePacked(r, s, v) + ); + } + + /** + * @notice Receive a transfer with a signed authorization from the payer + * @dev This has an additional check to ensure that the payee's address + * matches the caller of this function to prevent front-running attacks. + * EOA wallet signatures should be packed in the order of r, s, v. + * @param from Payer's address (Authorizer) + * @param to Payee's address + * @param value Amount to be transferred + * @param validAfter The time after which this is valid (unix time) + * @param validBefore The time before which this is valid (unix time) + * @param nonce Unique nonce + * @param signature Signature byte array produced by an EOA wallet or a contract wallet + */ + function _receiveWithAuthorization( + address from, + address to, + uint256 value, + uint256 validAfter, + uint256 validBefore, + bytes32 nonce, + bytes memory signature + ) internal { + require(to == msg.sender, "FiatTokenV2: caller must be the payee"); + _requireValidAuthorization(from, nonce, validAfter, validBefore); + _requireValidSignature( + from, + keccak256( + abi.encode( + RECEIVE_WITH_AUTHORIZATION_TYPEHASH, + from, + to, + value, + validAfter, + validBefore, + nonce + ) + ), + signature + ); + + _markAuthorizationAsUsed(from, nonce); + _transfer(from, to, value); + } + + /** + * @notice Attempt to cancel an authorization + * @param authorizer Authorizer's address + * @param nonce Nonce of the authorization + * @param v v of the signature + * @param r r of the signature + * @param s s of the signature + */ + function _cancelAuthorization( + address authorizer, + bytes32 nonce, + uint8 v, + bytes32 r, + bytes32 s + ) internal { + _cancelAuthorization(authorizer, nonce, abi.encodePacked(r, s, v)); + } + + /** + * @notice Attempt to cancel an authorization + * @dev EOA wallet signatures should be packed in the order of r, s, v. + * @param authorizer Authorizer's address + * @param nonce Nonce of the authorization + * @param signature Signature byte array produced by an EOA wallet or a contract wallet + */ + function _cancelAuthorization( + address authorizer, + bytes32 nonce, + bytes memory signature + ) internal { + _requireUnusedAuthorization(authorizer, nonce); + _requireValidSignature( + authorizer, + keccak256( + abi.encode(CANCEL_AUTHORIZATION_TYPEHASH, authorizer, nonce) + ), + signature + ); + + _authorizationStates[authorizer][nonce] = true; + emit AuthorizationCanceled(authorizer, nonce); + } + + /** + * @notice Validates that signature against input data struct + * @param signer Signer's address + * @param dataHash Hash of encoded data struct + * @param signature Signature byte array produced by an EOA wallet or a contract wallet + */ + function _requireValidSignature( + address signer, + bytes32 dataHash, + bytes memory signature + ) private view { + require( + SignatureChecker.isValidSignatureNow( + signer, + MessageHashUtils.toTypedDataHash(_domainSeparator(), dataHash), + signature + ), + "FiatTokenV2: invalid signature" + ); + } + + /** + * @notice Check that an authorization is unused + * @param authorizer Authorizer's address + * @param nonce Nonce of the authorization + */ + function _requireUnusedAuthorization(address authorizer, bytes32 nonce) + private + view + { + require( + !_authorizationStates[authorizer][nonce], + "FiatTokenV2: authorization is used or canceled" + ); + } + + /** + * @notice Check that authorization is valid + * @param authorizer Authorizer's address + * @param nonce Nonce of the authorization + * @param validAfter The time after which this is valid (unix time) + * @param validBefore The time before which this is valid (unix time) + */ + function _requireValidAuthorization( + address authorizer, + bytes32 nonce, + uint256 validAfter, + uint256 validBefore + ) private view { + require( + block.timestamp > validAfter, + "FiatTokenV2: authorization is not yet valid" + ); + require(block.timestamp < validBefore, "FiatTokenV2: authorization is expired"); + _requireUnusedAuthorization(authorizer, nonce); + } + + /** + * @notice Mark an authorization as used + * @param authorizer Authorizer's address + * @param nonce Nonce of the authorization + */ + function _markAuthorizationAsUsed(address authorizer, bytes32 nonce) + private + { + _authorizationStates[authorizer][nonce] = true; + emit AuthorizationUsed(authorizer, nonce); + } +} + +// contracts/v1.1/FiatTokenV1_1.sol +/** + * Copyright 2023 Circle Internet Group, Inc. All rights reserved. + * + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @title FiatTokenV1_1 + * @dev ERC20 Token backed by fiat reserves + */ +contract FiatTokenV1_1 is FiatTokenV1, Rescuable { + +} + +// contracts/v2/FiatTokenV2.sol +/** + * Copyright 2023 Circle Internet Group, Inc. All rights reserved. + * + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @title FiatToken V2 + * @notice ERC20 Token backed by fiat reserves, version 2 + */ +contract FiatTokenV2 is FiatTokenV1_1, EIP3009, EIP2612 { + using SafeMath for uint256; + uint8 internal _initializedVersion; + + /** + * @notice Initialize v2 + * @param newName New token name + */ + function initializeV2(string calldata newName) external { + // solhint-disable-next-line reason-string + require(initialized && _initializedVersion == 0); + name = newName; + _DEPRECATED_CACHED_DOMAIN_SEPARATOR = EIP712.makeDomainSeparator( + newName, + "2" + ); + _initializedVersion = 1; + } + + /** + * @notice Increase the allowance by a given increment + * @param spender Spender's address + * @param increment Amount of increase in allowance + * @return True if successful + */ + function increaseAllowance(address spender, uint256 increment) + external + virtual + whenNotPaused + notBlacklisted(msg.sender) + notBlacklisted(spender) + returns (bool) + { + _increaseAllowance(msg.sender, spender, increment); + return true; + } + + /** + * @notice Decrease the allowance by a given decrement + * @param spender Spender's address + * @param decrement Amount of decrease in allowance + * @return True if successful + */ + function decreaseAllowance(address spender, uint256 decrement) + external + virtual + whenNotPaused + notBlacklisted(msg.sender) + notBlacklisted(spender) + returns (bool) + { + _decreaseAllowance(msg.sender, spender, decrement); + return true; + } + + /** + * @notice Execute a transfer with a signed authorization + * @param from Payer's address (Authorizer) + * @param to Payee's address + * @param value Amount to be transferred + * @param validAfter The time after which this is valid (unix time) + * @param validBefore The time before which this is valid (unix time) + * @param nonce Unique nonce + * @param v v of the signature + * @param r r of the signature + * @param s s of the signature + */ + function transferWithAuthorization( + address from, + address to, + uint256 value, + uint256 validAfter, + uint256 validBefore, + bytes32 nonce, + uint8 v, + bytes32 r, + bytes32 s + ) external whenNotPaused notBlacklisted(from) notBlacklisted(to) { + _transferWithAuthorization( + from, + to, + value, + validAfter, + validBefore, + nonce, + v, + r, + s + ); + } + + /** + * @notice Receive a transfer with a signed authorization from the payer + * @dev This has an additional check to ensure that the payee's address + * matches the caller of this function to prevent front-running attacks. + * @param from Payer's address (Authorizer) + * @param to Payee's address + * @param value Amount to be transferred + * @param validAfter The time after which this is valid (unix time) + * @param validBefore The time before which this is valid (unix time) + * @param nonce Unique nonce + * @param v v of the signature + * @param r r of the signature + * @param s s of the signature + */ + function receiveWithAuthorization( + address from, + address to, + uint256 value, + uint256 validAfter, + uint256 validBefore, + bytes32 nonce, + uint8 v, + bytes32 r, + bytes32 s + ) external whenNotPaused notBlacklisted(from) notBlacklisted(to) { + _receiveWithAuthorization( + from, + to, + value, + validAfter, + validBefore, + nonce, + v, + r, + s + ); + } + + /** + * @notice Attempt to cancel an authorization + * @dev Works only if the authorization is not yet used. + * @param authorizer Authorizer's address + * @param nonce Nonce of the authorization + * @param v v of the signature + * @param r r of the signature + * @param s s of the signature + */ + function cancelAuthorization( + address authorizer, + bytes32 nonce, + uint8 v, + bytes32 r, + bytes32 s + ) external whenNotPaused { + _cancelAuthorization(authorizer, nonce, v, r, s); + } + + /** + * @notice Update allowance with a signed permit + * @param owner Token owner's address (Authorizer) + * @param spender Spender's address + * @param value Amount of allowance + * @param deadline The time at which the signature expires (unix time), or max uint256 value to signal no expiration + * @param v v of the signature + * @param r r of the signature + * @param s s of the signature + */ + function permit( + address owner, + address spender, + uint256 value, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) + external + virtual + whenNotPaused + notBlacklisted(owner) + notBlacklisted(spender) + { + _permit(owner, spender, value, deadline, v, r, s); + } + + /** + * @dev Internal function to increase the allowance by a given increment + * @param owner Token owner's address + * @param spender Spender's address + * @param increment Amount of increase + */ + function _increaseAllowance( + address owner, + address spender, + uint256 increment + ) internal override { + _approve(owner, spender, allowed[owner][spender].add(increment)); + } + + /** + * @dev Internal function to decrease the allowance by a given decrement + * @param owner Token owner's address + * @param spender Spender's address + * @param decrement Amount of decrease + */ + function _decreaseAllowance( + address owner, + address spender, + uint256 decrement + ) internal override { + _approve( + owner, + spender, + allowed[owner][spender].sub( + decrement, + "ERC20: decreased allowance below zero" + ) + ); + } +} + +// contracts/v2/FiatTokenV2_1.sol +/** + * Copyright 2023 Circle Internet Group, Inc. All rights reserved. + * + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// solhint-disable func-name-mixedcase + +/** + * @title FiatToken V2.1 + * @notice ERC20 Token backed by fiat reserves, version 2.1 + */ +contract FiatTokenV2_1 is FiatTokenV2 { + /** + * @notice Initialize v2.1 + * @param lostAndFound The address to which the locked funds are sent + */ + function initializeV2_1(address lostAndFound) external { + // solhint-disable-next-line reason-string + require(_initializedVersion == 1); + + uint256 lockedAmount = _balanceOf(address(this)); + if (lockedAmount > 0) { + _transfer(address(this), lostAndFound, lockedAmount); + } + _blacklist(address(this)); + + _initializedVersion = 2; + } + + /** + * @notice Version string for the EIP712 domain separator + * @return Version string + */ + function version() external pure returns (string memory) { + return "2"; + } +} + +// contracts/v2/FiatTokenV2_2.sol +/** + * Copyright 2023 Circle Internet Group, Inc. All rights reserved. + * + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + // solhint-disable-line no-unused-import + // solhint-disable-line no-unused-import + // solhint-disable-line no-unused-import + // solhint-disable-line no-unused-import + +// solhint-disable func-name-mixedcase + +/** + * @title FiatToken V2.2 + * @notice ERC20 Token backed by fiat reserves, version 2.2 + */ +contract FiatTokenV2_2 is FiatTokenV2_1 { + /** + * @notice Initialize v2.2 + * @param accountsToBlacklist A list of accounts to migrate from the old blacklist + * @param newSymbol New token symbol + * data structure to the new blacklist data structure. + */ + function initializeV2_2( + address[] calldata accountsToBlacklist, + string calldata newSymbol + ) external { + // solhint-disable-next-line reason-string + require(_initializedVersion == 2); + + // Update fiat token symbol + symbol = newSymbol; + + // Add previously blacklisted accounts to the new blacklist data structure + // and remove them from the old blacklist data structure. + for (uint256 i = 0; i < accountsToBlacklist.length; i++) { + require( + _deprecatedBlacklisted[accountsToBlacklist[i]], + "FiatTokenV2_2: Blacklisting previously unblacklisted account!" + ); + _blacklist(accountsToBlacklist[i]); + delete _deprecatedBlacklisted[accountsToBlacklist[i]]; + } + _blacklist(address(this)); + delete _deprecatedBlacklisted[address(this)]; + + _initializedVersion = 3; + } + + /** + * @dev Internal function to get the current chain id. + * @return The current chain id. + */ + function _chainId() internal virtual view returns (uint256) { + uint256 chainId; + assembly { + chainId := chainid() + } + return chainId; + } + + /** + * + */ + function _domainSeparator() internal override view returns (bytes32) { + return EIP712.makeDomainSeparator(name, "2", _chainId()); + } + + /** + * @notice Update allowance with a signed permit + * @dev EOA wallet signatures should be packed in the order of r, s, v. + * @param owner Token owner's address (Authorizer) + * @param spender Spender's address + * @param value Amount of allowance + * @param deadline The time at which the signature expires (unix time), or max uint256 value to signal no expiration + * @param signature Signature bytes signed by an EOA wallet or a contract wallet + */ + function permit( + address owner, + address spender, + uint256 value, + uint256 deadline, + bytes memory signature + ) external whenNotPaused { + _permit(owner, spender, value, deadline, signature); + } + + /** + * @notice Execute a transfer with a signed authorization + * @dev EOA wallet signatures should be packed in the order of r, s, v. + * @param from Payer's address (Authorizer) + * @param to Payee's address + * @param value Amount to be transferred + * @param validAfter The time after which this is valid (unix time) + * @param validBefore The time before which this is valid (unix time) + * @param nonce Unique nonce + * @param signature Signature bytes signed by an EOA wallet or a contract wallet + */ + function transferWithAuthorization( + address from, + address to, + uint256 value, + uint256 validAfter, + uint256 validBefore, + bytes32 nonce, + bytes memory signature + ) external whenNotPaused notBlacklisted(from) notBlacklisted(to) { + _transferWithAuthorization( + from, + to, + value, + validAfter, + validBefore, + nonce, + signature + ); + } + + /** + * @notice Receive a transfer with a signed authorization from the payer + * @dev This has an additional check to ensure that the payee's address + * matches the caller of this function to prevent front-running attacks. + * EOA wallet signatures should be packed in the order of r, s, v. + * @param from Payer's address (Authorizer) + * @param to Payee's address + * @param value Amount to be transferred + * @param validAfter The time after which this is valid (unix time) + * @param validBefore The time before which this is valid (unix time) + * @param nonce Unique nonce + * @param signature Signature bytes signed by an EOA wallet or a contract wallet + */ + function receiveWithAuthorization( + address from, + address to, + uint256 value, + uint256 validAfter, + uint256 validBefore, + bytes32 nonce, + bytes memory signature + ) external whenNotPaused notBlacklisted(from) notBlacklisted(to) { + _receiveWithAuthorization( + from, + to, + value, + validAfter, + validBefore, + nonce, + signature + ); + } + + /** + * @notice Attempt to cancel an authorization + * @dev Works only if the authorization is not yet used. + * EOA wallet signatures should be packed in the order of r, s, v. + * @param authorizer Authorizer's address + * @param nonce Nonce of the authorization + * @param signature Signature bytes signed by an EOA wallet or a contract wallet + */ + function cancelAuthorization( + address authorizer, + bytes32 nonce, + bytes memory signature + ) external whenNotPaused { + _cancelAuthorization(authorizer, nonce, signature); + } + + /** + * @dev Helper method that sets the blacklist state of an account on balanceAndBlacklistStates. + * If _shouldBlacklist is true, we apply a (1 << 255) bitmask with an OR operation on the + * account's balanceAndBlacklistState. This flips the high bit for the account to 1, + * indicating that the account is blacklisted. + * + * If _shouldBlacklist if false, we reset the account's balanceAndBlacklistStates to their + * balances. This clears the high bit for the account, indicating that the account is unblacklisted. + * @param _account The address of the account. + * @param _shouldBlacklist True if the account should be blacklisted, false if the account should be unblacklisted. + */ + function _setBlacklistState(address _account, bool _shouldBlacklist) + internal + override + { + balanceAndBlacklistStates[_account] = _shouldBlacklist + ? balanceAndBlacklistStates[_account] | (1 << 255) + : _balanceOf(_account); + } + + /** + * @dev Helper method that sets the balance of an account on balanceAndBlacklistStates. + * Since balances are stored in the last 255 bits of the balanceAndBlacklistStates value, + * we need to ensure that the updated balance does not exceed (2^255 - 1). + * Since blacklisted accounts' balances cannot be updated, the method will also + * revert if the account is blacklisted + * @param _account The address of the account. + * @param _balance The new fiat token balance of the account (max: (2^255 - 1)). + */ + function _setBalance(address _account, uint256 _balance) internal override { + require( + _balance <= ((1 << 255) - 1), + "FiatTokenV2_2: Balance exceeds (2^255 - 1)" + ); + require( + !_isBlacklisted(_account), + "FiatTokenV2_2: Account is blacklisted" + ); + + balanceAndBlacklistStates[_account] = _balance; + } + + /** + * + */ + function _isBlacklisted(address _account) + internal + override + view + returns (bool) + { + return balanceAndBlacklistStates[_account] >> 255 == 1; + } + + /** + * @dev Helper method to obtain the balance of an account. Since balances + * are stored in the last 255 bits of the balanceAndBlacklistStates value, + * we apply a ((1 << 255) - 1) bit bitmask with an AND operation on the + * balanceAndBlacklistState to obtain the balance. + * @param _account The address of the account. + * @return The fiat token balance of the account. + */ + function _balanceOf(address _account) + internal + override + view + returns (uint256) + { + return balanceAndBlacklistStates[_account] & ((1 << 255) - 1); + } + + /** + * + */ + function approve(address spender, uint256 value) + external + override(FiatTokenV1, IERC20) + whenNotPaused + returns (bool) + { + _approve(msg.sender, spender, value); + return true; + } + + /** + * + */ + function permit( + address owner, + address spender, + uint256 value, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) external override whenNotPaused { + _permit(owner, spender, value, deadline, v, r, s); + } + + /** + * + */ + function increaseAllowance(address spender, uint256 increment) + external + override + whenNotPaused + returns (bool) + { + _increaseAllowance(msg.sender, spender, increment); + return true; + } + + /** + * + */ + function decreaseAllowance(address spender, uint256 decrement) + external + override + whenNotPaused + returns (bool) + { + _decreaseAllowance(msg.sender, spender, decrement); + return true; + } +} \ No newline at end of file diff --git a/contracts/usdc_proxy.sol b/contracts/usdc_proxy.sol new file mode 100644 index 0000000..019fdd0 --- /dev/null +++ b/contracts/usdc_proxy.sol @@ -0,0 +1,578 @@ +//* SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.30; + +// contracts/upgradeability/Proxy.sol +/** + * Copyright 2023 Circle Internet Group, Inc. All rights reserved. + * + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @notice Implements delegation of calls to other contracts, with proper + * forwarding of return values and bubbling of failures. + * It defines a fallback function that delegates all calls to the address + * returned by the abstract _implementation() internal function. + * @dev Forked from https://github.com/zeppelinos/zos-lib/blob/8a16ef3ad17ec7430e3a9d2b5e3f39b8204f8c8d/contracts/upgradeability/Proxy.sol + * Modifications: + * 1. Reformat and conform to Solidity 0.6 syntax (5/13/20) + */ +abstract contract Proxy { + /** + * @dev Fallback function. + * Implemented entirely in `_fallback`. + */ + fallback() external payable { + _fallback(); + } + + receive() external payable { + _fallback(); + } + + /** + * @return The Address of the implementation. + */ + function _implementation() internal virtual view returns (address); + + /** + * @dev Delegates execution to an implementation contract. + * This is a low level function that doesn't return to its internal call site. + * It will return to the external caller whatever the implementation returns. + * @param implementation Address to delegate. + */ + function _delegate(address implementation) internal { + assembly { + // Copy msg.data. We take full control of memory in this inline assembly + // block because it will not return to Solidity code. We overwrite the + // Solidity scratch pad at memory position 0. + calldatacopy(0, 0, calldatasize()) + + // Call the implementation. + // out and outsize are 0 because we don't know the size yet. + let result := delegatecall( + gas(), + implementation, + 0, + calldatasize(), + 0, + 0 + ) + + // Copy the returned data. + returndatacopy(0, 0, returndatasize()) + + switch result + // delegatecall returns 0 on error. + case 0 { + revert(0, returndatasize()) + } + default { + return(0, returndatasize()) + } + } + } + + /** + * @dev Function that is run as the first thing in the fallback function. + * Can be redefined in derived contracts to add functionality. + * Redefinitions must call super._willFallback(). + */ + function _willFallback() internal virtual {} + + /** + * @dev fallback implementation. + * Extracted to enable manual triggering. + */ + function _fallback() internal { + _willFallback(); + _delegate(_implementation()); + } +} + +// node_modules/@openzeppelin/contracts/utils/Address.sol + +/** + * @dev Collection of functions related to the address type + */ +library Address { + /** + * @dev Returns true if `account` is a contract. + * + * [IMPORTANT] + * ==== + * It is unsafe to assume that an address for which this function returns + * false is an externally-owned account (EOA) and not a contract. + * + * Among others, `isContract` will return false for the following + * types of addresses: + * + * - an externally-owned account + * - a contract in construction + * - an address where a contract will be created + * - an address where a contract lived, but was destroyed + * ==== + */ + function isContract(address account) internal view returns (bool) { + // This method relies on extcodesize, which returns 0 for contracts in + // construction, since the code is only stored at the end of the + // constructor execution. + + uint256 size; + // solhint-disable-next-line no-inline-assembly + assembly { size := extcodesize(account) } + return size > 0; + } + + /** + * @dev Replacement for Solidity's `transfer`: sends `amount` wei to + * `recipient`, forwarding all available gas and reverting on errors. + * + * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost + * of certain opcodes, possibly making contracts go over the 2300 gas limit + * imposed by `transfer`, making them unable to receive funds via + * `transfer`. {sendValue} removes this limitation. + * + * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. + * + * IMPORTANT: because control is transferred to `recipient`, care must be + * taken to not create reentrancy vulnerabilities. Consider using + * {ReentrancyGuard} or the + * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. + */ + function sendValue(address payable recipient, uint256 amount) internal { + require(address(this).balance >= amount, "Address: insufficient balance"); + + // solhint-disable-next-line avoid-low-level-calls, avoid-call-value + (bool success, ) = recipient.call{ value: amount }(""); + require(success, "Address: unable to send value, recipient may have reverted"); + } + + /** + * @dev Performs a Solidity function call using a low level `call`. A + * plain`call` is an unsafe replacement for a function call: use this + * function instead. + * + * If `target` reverts with a revert reason, it is bubbled up by this + * function (like regular Solidity function calls). + * + * Returns the raw returned data. To convert to the expected return value, + * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. + * + * Requirements: + * + * - `target` must be a contract. + * - calling `target` with `data` must not revert. + * + * _Available since v3.1._ + */ + function functionCall(address target, bytes memory data) internal returns (bytes memory) { + return functionCall(target, data, "Address: low-level call failed"); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with + * `errorMessage` as a fallback revert reason when `target` reverts. + * + * _Available since v3.1._ + */ + function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { + return functionCallWithValue(target, data, 0, errorMessage); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], + * but also transferring `value` wei to `target`. + * + * Requirements: + * + * - the calling contract must have an ETH balance of at least `value`. + * - the called Solidity function must be `payable`. + * + * _Available since v3.1._ + */ + function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { + return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); + } + + /** + * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but + * with `errorMessage` as a fallback revert reason when `target` reverts. + * + * _Available since v3.1._ + */ + function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) { + require(address(this).balance >= value, "Address: insufficient balance for call"); + require(isContract(target), "Address: call to non-contract"); + + // solhint-disable-next-line avoid-low-level-calls + (bool success, bytes memory returndata) = target.call{ value: value }(data); + return _verifyCallResult(success, returndata, errorMessage); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], + * but performing a static call. + * + * _Available since v3.3._ + */ + function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { + return functionStaticCall(target, data, "Address: low-level static call failed"); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], + * but performing a static call. + * + * _Available since v3.3._ + */ + function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) { + require(isContract(target), "Address: static call to non-contract"); + + // solhint-disable-next-line avoid-low-level-calls + (bool success, bytes memory returndata) = target.staticcall(data); + return _verifyCallResult(success, returndata, errorMessage); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], + * but performing a delegate call. + * + * _Available since v3.4._ + */ + function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { + return functionDelegateCall(target, data, "Address: low-level delegate call failed"); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], + * but performing a delegate call. + * + * _Available since v3.4._ + */ + function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { + require(isContract(target), "Address: delegate call to non-contract"); + + // solhint-disable-next-line avoid-low-level-calls + (bool success, bytes memory returndata) = target.delegatecall(data); + return _verifyCallResult(success, returndata, errorMessage); + } + + function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) { + if (success) { + return returndata; + } else { + // Look for revert reason and bubble it up if present + if (returndata.length > 0) { + // The easiest way to bubble the revert reason is using memory via assembly + + // solhint-disable-next-line no-inline-assembly + assembly { + let returndata_size := mload(returndata) + revert(add(32, returndata), returndata_size) + } + } else { + revert(errorMessage); + } + } + } +} + +// contracts/upgradeability/UpgradeabilityProxy.sol +/** + * Copyright 2023 Circle Internet Group, Inc. All rights reserved. + * + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @notice This contract implements a proxy that allows to change the + * implementation address to which it will delegate. + * Such a change is called an implementation upgrade. + * @dev Forked from https://github.com/zeppelinos/zos-lib/blob/8a16ef3ad17ec7430e3a9d2b5e3f39b8204f8c8d/contracts/upgradeability/UpgradeabilityProxy.sol + * Modifications: + * 1. Reformat, conform to Solidity 0.6 syntax, and add error messages (5/13/20) + * 2. Use Address utility library from the latest OpenZeppelin (5/13/20) + */ +contract UpgradeabilityProxy is Proxy { + /** + * @dev Emitted when the implementation is upgraded. + * @param implementation Address of the new implementation. + */ + event Upgraded(address implementation); + + /** + * @dev Storage slot with the address of the current implementation. + * This is the keccak-256 hash of "org.zeppelinos.proxy.implementation", and is + * validated in the constructor. + */ + bytes32 + private constant IMPLEMENTATION_SLOT = 0x7050c9e0f4ca769c69bd3a8ef740bc37934f8e2c036e5a723fd8ee048ed3f8c3; + + /** + * @dev Contract constructor. + * @param implementationContract Address of the initial implementation. + */ + constructor(address implementationContract) { + assert( + IMPLEMENTATION_SLOT == + keccak256("org.zeppelinos.proxy.implementation") + ); + + _setImplementation(implementationContract); + } + + /** + * @dev Returns the current implementation. + * @return impl Address of the current implementation + */ + function _implementation() internal override view returns (address impl) { + bytes32 slot = IMPLEMENTATION_SLOT; + assembly { + impl := sload(slot) + } + } + + /** + * @dev Upgrades the proxy to a new implementation. + * @param newImplementation Address of the new implementation. + */ + function _upgradeTo(address newImplementation) internal { + _setImplementation(newImplementation); + emit Upgraded(newImplementation); + } + + /** + * @dev Sets the implementation address of the proxy. + * @param newImplementation Address of the new implementation. + */ + function _setImplementation(address newImplementation) private { + require( + Address.isContract(newImplementation), + "Cannot set a proxy implementation to a non-contract address" + ); + + bytes32 slot = IMPLEMENTATION_SLOT; + + assembly { + sstore(slot, newImplementation) + } + } +} + +// contracts/upgradeability/AdminUpgradeabilityProxy.sol +/** + * Copyright 2023 Circle Internet Group, Inc. All rights reserved. + * + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @notice This contract combines an upgradeability proxy with an authorization + * mechanism for administrative tasks. + * @dev Forked from https://github.com/zeppelinos/zos-lib/blob/8a16ef3ad17ec7430e3a9d2b5e3f39b8204f8c8d/contracts/upgradeability/AdminUpgradeabilityProxy.sol + * Modifications: + * 1. Reformat, conform to Solidity 0.6 syntax, and add error messages (5/13/20) + * 2. Remove ifAdmin modifier from admin() and implementation() (5/13/20) + */ +contract AdminUpgradeabilityProxy is UpgradeabilityProxy { + /** + * @dev Emitted when the administration has been transferred. + * @param previousAdmin Address of the previous admin. + * @param newAdmin Address of the new admin. + */ + event AdminChanged(address previousAdmin, address newAdmin); + + /** + * @dev Storage slot with the admin of the contract. + * This is the keccak-256 hash of "org.zeppelinos.proxy.admin", and is + * validated in the constructor. + */ + bytes32 + private constant ADMIN_SLOT = 0x10d6a54a4754c8869d6886b5f5d7fbfa5b4522237ea5c60d11bc4e7a1ff9390b; + + /** + * @dev Modifier to check whether the `msg.sender` is the admin. + * If it is, it will run the function. Otherwise, it will delegate the call + * to the implementation. + */ + modifier ifAdmin() { + if (msg.sender == _admin()) { + _; + } else { + _fallback(); + } + } + + /** + * @dev Contract constructor. + * It sets the `msg.sender` as the proxy administrator. + * @param implementationContract address of the initial implementation. + */ + constructor(address implementationContract) + UpgradeabilityProxy(implementationContract) + { + assert(ADMIN_SLOT == keccak256("org.zeppelinos.proxy.admin")); + + _setAdmin(msg.sender); + } + + /** + * @return The address of the proxy admin. + */ + function admin() external view returns (address) { + return _admin(); + } + + /** + * @return The address of the implementation. + */ + function implementation() external view returns (address) { + return _implementation(); + } + + /** + * @dev Changes the admin of the proxy. + * Only the current admin can call this function. + * @param newAdmin Address to transfer proxy administration to. + */ + function changeAdmin(address newAdmin) external ifAdmin { + require( + newAdmin != address(0), + "Cannot change the admin of a proxy to the zero address" + ); + emit AdminChanged(_admin(), newAdmin); + _setAdmin(newAdmin); + } + + /** + * @dev Upgrade the backing implementation of the proxy. + * Only the admin can call this function. + * @param newImplementation Address of the new implementation. + */ + function upgradeTo(address newImplementation) external ifAdmin { + _upgradeTo(newImplementation); + } + + /** + * @dev Upgrade the backing implementation of the proxy and call a function + * on the new implementation. + * This is useful to initialize the proxied contract. + * @param newImplementation Address of the new implementation. + * @param data Data to send as msg.data in the low level call. + * It should include the signature and the parameters of the function to be + * called, as described in + * https://solidity.readthedocs.io/en/develop/abi-spec.html#function-selector-and-argument-encoding. + */ + function upgradeToAndCall(address newImplementation, bytes calldata data) + external + payable + ifAdmin + { + _upgradeTo(newImplementation); + // prettier-ignore + // solhint-disable-next-line avoid-low-level-calls + (bool success,) = address(this).call{value: msg.value}(data); + // solhint-disable-next-line reason-string + require(success); + } + + /** + * @return adm The admin slot. + */ + function _admin() internal view returns (address adm) { + bytes32 slot = ADMIN_SLOT; + + assembly { + adm := sload(slot) + } + } + + /** + * @dev Sets the address of the proxy admin. + * @param newAdmin Address of the new proxy admin. + */ + function _setAdmin(address newAdmin) internal { + bytes32 slot = ADMIN_SLOT; + + assembly { + sstore(slot, newAdmin) + } + } + + /** + * @dev Only fall back when the sender is not the admin. + */ + function _willFallback() internal override { + require( + msg.sender != _admin(), + "Cannot call fallback function from the proxy admin" + ); + super._willFallback(); + } +} + +// contracts/v1/FiatTokenProxy.sol +/** + * Copyright 2023 Circle Internet Group, Inc. All rights reserved. + * + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @title FiatTokenProxy + * @dev This contract proxies FiatToken calls and enables FiatToken upgrades + */ +contract FiatTokenProxy is AdminUpgradeabilityProxy { + constructor(address implementationContract) + AdminUpgradeabilityProxy(implementationContract) + {} +} \ No newline at end of file diff --git a/contracts/weth.sol b/contracts/weth.sol new file mode 100644 index 0000000..34af626 --- /dev/null +++ b/contracts/weth.sol @@ -0,0 +1,766 @@ +/** + *Submitted for verification at Etherscan.io on 2017-12-12 +*/ + +// Copyright (C) 2015, 2016, 2017 Dapphub + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +pragma solidity ^0.8.30; + +contract WETH9 { + string public name = "Wrapped Ether"; + string public symbol = "WETH"; + uint8 public decimals = 18; + + event Approval(address indexed src, address indexed guy, uint wad); + event Transfer(address indexed src, address indexed dst, uint wad); + event Deposit(address indexed dst, uint wad); + event Withdrawal(address indexed src, uint wad); + + mapping (address => uint) public balanceOf; + mapping (address => mapping (address => uint)) public allowance; + + receive() external payable { + deposit(); + } + + fallback() external payable { + deposit(); + } + function deposit() public payable { + unchecked { balanceOf[msg.sender] += msg.value; } + emit Deposit(msg.sender, msg.value); + } + function withdraw(uint wad) public { + require(balanceOf[msg.sender] >= wad); + unchecked { balanceOf[msg.sender] -= wad; } + payable(msg.sender).transfer(wad); + emit Withdrawal(msg.sender, wad); + } + + function totalSupply() public view returns (uint) { + return address(this).balance; + } + + function approve(address guy, uint wad) public returns (bool) { + allowance[msg.sender][guy] = wad; + emit Approval(msg.sender, guy, wad); + return true; + } + + function transfer(address dst, uint wad) public returns (bool) { + return transferFrom(msg.sender, dst, wad); + } + + function transferFrom(address src, address dst, uint wad) + public + returns (bool) + { + require(balanceOf[src] >= wad); + + if (src != msg.sender && allowance[src][msg.sender] != type(uint).max) { + require(allowance[src][msg.sender] >= wad); + unchecked { allowance[src][msg.sender] -= wad; } + } + + unchecked { + balanceOf[src] -= wad; + balanceOf[dst] += wad; + } + + emit Transfer(src, dst, wad); + + return true; + } +} + + +/* + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. + +*/ \ No newline at end of file diff --git a/contracts/xen_minter.sol b/contracts/xen_minter.sol new file mode 100644 index 0000000..e1d932f --- /dev/null +++ b/contracts/xen_minter.sol @@ -0,0 +1,180 @@ +/** + *Submitted for verification at Etherscan.io on 2022-10-13 +*/ + +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +/* + Refactor goals: + - No hardcoded proxy bytecode literals (no bytes.concat(0x3d60...)) + - No inline assembly for CREATE2 or CALL + - Keep original contract structure/names for easy diff + - Preserve behavior: deploy deterministic proxies and call them with `data` + so that inside delegated execution msg.sender == original (this contract), + enabling d/c/dKill/cKill/k gating to work. +*/ + +interface IERC20 { + function balanceOf(address account) external view returns (uint256); + function transfer(address to, uint256 amount) external returns (bool); +} + +/** + * @notice Source-code-only proxy deployed with CREATE2 via `new {salt: ...}`. + * @dev Fallback delegates all calls to `implementation` and **does not bubble + * return data** (matches the original contract's behavior of ignoring `succeeded`). + * No assembly is used. + */ +contract CoinTool_Proxy { + address public immutable implementation; + + constructor(address _implementation) payable { + implementation = _implementation; + } + + fallback() external payable { + // Delegatecall into the implementation; ignore returndata and success + // to mimic the original "fire-and-forget" calls. + implementation.delegatecall(msg.data); + } + + receive() external payable {} +} + +contract CoinTool_App { + address owner; + address private immutable original; + mapping(address => mapping(bytes => uint256)) public map; + + constructor() payable { + original = address(this); + owner = tx.origin; + } + + receive() external payable {} + fallback() external payable {} + + // --- internal helpers (new) --- + + function _salt(bytes calldata _saltBytes, uint256 i, address user) internal pure returns (bytes32) { + return keccak256(abi.encodePacked(_saltBytes, i, user)); + } + + function _deployProxy(bytes32 salt) internal returns (address proxy) { + // CREATE2 deployment without assembly (compiler handles it) + proxy = address(new CoinTool_Proxy{salt: salt}(address(this))); + } + + function _predictProxy(bytes32 salt) internal view returns (address predicted) { + // Standard CREATE2 formula using init_code hash: + // address = keccak256(0xff ++ deployer ++ salt ++ keccak256(init_code))[12:] + bytes memory initCode = abi.encodePacked( + type(CoinTool_Proxy).creationCode, + abi.encode(address(this)) + ); + + bytes32 initCodeHash = keccak256(initCode); + bytes32 h = keccak256(abi.encodePacked(bytes1(0xff), address(this), salt, initCodeHash)); + predicted = address(uint160(uint256(h))); + } + + // --- original functions, refactored to avoid bytecode literals + assembly --- + + function t(uint256 total, bytes memory data, bytes calldata _saltBytes) external payable { + require(msg.sender == tx.origin); + + uint256 i = map[msg.sender][_saltBytes] + 1; + uint256 end = total + i; + + for (; i < end; ++i) { + bytes32 salt = _salt(_saltBytes, i, msg.sender); + address proxy = _deployProxy(salt); + + // Call proxy with the provided calldata (no assembly) + // (ignore success to match original behavior) + proxy.call(data); + } + + map[msg.sender][_saltBytes] += total; + } + + function t_(uint256[] calldata a, bytes memory data, bytes calldata _saltBytes) external payable { + require(msg.sender == tx.origin); + + for (uint256 i = 0; i < a.length; ++i) { + bytes32 salt = keccak256(abi.encodePacked(_saltBytes, a[i], msg.sender)); + address proxy = _deployProxy(salt); + + proxy.call(data); + } + + uint256 e = a[a.length - 1]; + if (e > map[msg.sender][_saltBytes]) { + map[msg.sender][_saltBytes] = e; + } + } + + function f(uint256[] calldata a, bytes memory data, bytes memory _saltBytes) external payable { + require(msg.sender == tx.origin); + + for (uint256 i = 0; i < a.length; ++i) { + bytes32 salt = keccak256(abi.encodePacked(_saltBytes, a[i], msg.sender)); + address proxy = _predictProxy(salt); + + // Mimic original behavior: calling an address with no code should not revert. + // Using low-level .call(data) preserves that: + // - If proxy not deployed: call succeeds (does nothing) in most cases. + // - If deployed: proxy fallback delegatecalls into this contract. + proxy.call(data); + } + } + + // --- keep remaining functions & structure (no assembly) --- + + function d(address a, bytes memory data) external payable { + require(msg.sender == original); + a.delegatecall(data); + } + + function c(address a, bytes calldata data) external payable { + require(msg.sender == original); + external_call(a, data); + } + + function dKill(address a, bytes memory data) external payable { + require(msg.sender == original); + a.delegatecall(data); + (bool ok,) = payable(msg.sender).call{value: address(this).balance}(""); + require(ok, "SWEEP_FAILED"); + } + + function cKill(address a, bytes calldata data) external payable { + require(msg.sender == original); + external_call(a, data); + (bool ok,) = payable(msg.sender).call{value: address(this).balance}(""); + require(ok, "SWEEP_FAILED"); + } + + function k() external { + require(msg.sender == original); + (bool ok,) = payable(msg.sender).call{value: address(this).balance}(""); + require(ok, "SWEEP_FAILED"); + } + + function external_call(address destination, bytes memory data) internal { + destination.call(data); + } + + function claimTokens(address _token) external { + require(owner == msg.sender); + + if (_token == address(0)) { + payable(owner).transfer(address(this).balance); + return; + } + IERC20 erc20token = IERC20(_token); + uint256 balance = erc20token.balanceOf(address(this)); + erc20token.transfer(owner, balance); + } +} \ No newline at end of file diff --git a/contracts/xen_token.sol b/contracts/xen_token.sol new file mode 100644 index 0000000..4a6a7c0 --- /dev/null +++ b/contracts/xen_token.sol @@ -0,0 +1,3220 @@ + + +// Sources flattened with hardhat v2.21.0 https://hardhat.org + +// SPDX-License-Identifier: BSD-4-Clause AND MIT + +// File @openzeppelin/contracts/utils/introspection/IERC165.sol@v4.9.6 + +// Original license: SPDX_License_Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Interface of the ERC165 standard, as defined in the + * https://eips.ethereum.org/EIPS/eip-165[EIP]. + * + * Implementers can declare support of contract interfaces, which can then be + * queried by others ({ERC165Checker}). + * + * For an implementation, see {ERC165}. + */ +interface IERC165 { + /** + * @dev Returns true if this contract implements the interface defined by + * `interfaceId`. See the corresponding + * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] + * to learn more about how these ids are created. + * + * This function call must use less than 30 000 gas. + */ + function supportsInterface(bytes4 interfaceId) external view returns (bool); +} + + +// File @openzeppelin/contracts/interfaces/IERC165.sol@v4.9.6 + +// Original license: SPDX_License_Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (interfaces/IERC165.sol) + +pragma solidity ^0.8.0; + + +// File @openzeppelin/contracts/token/ERC20/IERC20.sol@v4.9.6 + +// Original license: SPDX_License_Identifier: MIT +// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Interface of the ERC20 standard as defined in the EIP. + */ +interface IERC20 { + /** + * @dev Emitted when `value` tokens are moved from one account (`from`) to + * another (`to`). + * + * Note that `value` may be zero. + */ + event Transfer(address indexed from, address indexed to, uint256 value); + + /** + * @dev Emitted when the allowance of a `spender` for an `owner` is set by + * a call to {approve}. `value` is the new allowance. + */ + event Approval(address indexed owner, address indexed spender, uint256 value); + + /** + * @dev Returns the amount of tokens in existence. + */ + function totalSupply() external view returns (uint256); + + /** + * @dev Returns the amount of tokens owned by `account`. + */ + function balanceOf(address account) external view returns (uint256); + + /** + * @dev Moves `amount` tokens from the caller's account to `to`. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * Emits a {Transfer} event. + */ + function transfer(address to, uint256 amount) external returns (bool); + + /** + * @dev Returns the remaining number of tokens that `spender` will be + * allowed to spend on behalf of `owner` through {transferFrom}. This is + * zero by default. + * + * This value changes when {approve} or {transferFrom} are called. + */ + function allowance(address owner, address spender) external view returns (uint256); + + /** + * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * IMPORTANT: Beware that changing an allowance with this method brings the risk + * that someone may use both the old and the new allowance by unfortunate + * transaction ordering. One possible solution to mitigate this race + * condition is to first reduce the spender's allowance to 0 and set the + * desired value afterwards: + * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 + * + * Emits an {Approval} event. + */ + function approve(address spender, uint256 amount) external returns (bool); + + /** + * @dev Moves `amount` tokens from `from` to `to` using the + * allowance mechanism. `amount` is then deducted from the caller's + * allowance. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * Emits a {Transfer} event. + */ + function transferFrom(address from, address to, uint256 amount) external returns (bool); +} + + +// File @openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol@v4.9.6 + +// Original license: SPDX_License_Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Interface for the optional metadata functions from the ERC20 standard. + * + * _Available since v4.1._ + */ +interface IERC20Metadata is IERC20 { + /** + * @dev Returns the name of the token. + */ + function name() external view returns (string memory); + + /** + * @dev Returns the symbol of the token. + */ + function symbol() external view returns (string memory); + + /** + * @dev Returns the decimals places of the token. + */ + function decimals() external view returns (uint8); +} + + +// File @openzeppelin/contracts/utils/Context.sol@v4.9.6 + +// Original license: SPDX_License_Identifier: MIT +// OpenZeppelin Contracts (last updated v4.9.4) (utils/Context.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Provides information about the current execution context, including the + * sender of the transaction and its data. While these are generally available + * via msg.sender and msg.data, they should not be accessed in such a direct + * manner, since when dealing with meta-transactions the account sending and + * paying for execution may not be the actual sender (as far as an application + * is concerned). + * + * This contract is only required for intermediate, library-like contracts. + */ +abstract contract Context { + function _msgSender() internal view virtual returns (address) { + return msg.sender; + } + + function _msgData() internal view virtual returns (bytes calldata) { + return msg.data; + } + + function _contextSuffixLength() internal view virtual returns (uint256) { + return 0; + } +} + + +// File @openzeppelin/contracts/token/ERC20/ERC20.sol@v4.9.6 + +// Original license: SPDX_License_Identifier: MIT +// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/ERC20.sol) + +pragma solidity ^0.8.0; + + + +/** + * @dev Implementation of the {IERC20} interface. + * + * This implementation is agnostic to the way tokens are created. This means + * that a supply mechanism has to be added in a derived contract using {_mint}. + * For a generic mechanism see {ERC20PresetMinterPauser}. + * + * TIP: For a detailed writeup see our guide + * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How + * to implement supply mechanisms]. + * + * The default value of {decimals} is 18. To change this, you should override + * this function so it returns a different value. + * + * We have followed general OpenZeppelin Contracts guidelines: functions revert + * instead returning `false` on failure. This behavior is nonetheless + * conventional and does not conflict with the expectations of ERC20 + * applications. + * + * Additionally, an {Approval} event is emitted on calls to {transferFrom}. + * This allows applications to reconstruct the allowance for all accounts just + * by listening to said events. Other implementations of the EIP may not emit + * these events, as it isn't required by the specification. + * + * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} + * functions have been added to mitigate the well-known issues around setting + * allowances. See {IERC20-approve}. + */ +contract ERC20 is Context, IERC20, IERC20Metadata { + mapping(address => uint256) private _balances; + + mapping(address => mapping(address => uint256)) private _allowances; + + uint256 private _totalSupply; + + string private _name; + string private _symbol; + + /** + * @dev Sets the values for {name} and {symbol}. + * + * All two of these values are immutable: they can only be set once during + * construction. + */ + constructor(string memory name_, string memory symbol_) { + _name = name_; + _symbol = symbol_; + } + + /** + * @dev Returns the name of the token. + */ + function name() public view virtual override returns (string memory) { + return _name; + } + + /** + * @dev Returns the symbol of the token, usually a shorter version of the + * name. + */ + function symbol() public view virtual override returns (string memory) { + return _symbol; + } + + /** + * @dev Returns the number of decimals used to get its user representation. + * For example, if `decimals` equals `2`, a balance of `505` tokens should + * be displayed to a user as `5.05` (`505 / 10 ** 2`). + * + * Tokens usually opt for a value of 18, imitating the relationship between + * Ether and Wei. This is the default value returned by this function, unless + * it's overridden. + * + * NOTE: This information is only used for _display_ purposes: it in + * no way affects any of the arithmetic of the contract, including + * {IERC20-balanceOf} and {IERC20-transfer}. + */ + function decimals() public view virtual override returns (uint8) { + return 18; + } + + /** + * @dev See {IERC20-totalSupply}. + */ + function totalSupply() public view virtual override returns (uint256) { + return _totalSupply; + } + + /** + * @dev See {IERC20-balanceOf}. + */ + function balanceOf(address account) public view virtual override returns (uint256) { + return _balances[account]; + } + + /** + * @dev See {IERC20-transfer}. + * + * Requirements: + * + * - `to` cannot be the zero address. + * - the caller must have a balance of at least `amount`. + */ + function transfer(address to, uint256 amount) public virtual override returns (bool) { + address owner = _msgSender(); + _transfer(owner, to, amount); + return true; + } + + /** + * @dev See {IERC20-allowance}. + */ + function allowance(address owner, address spender) public view virtual override returns (uint256) { + return _allowances[owner][spender]; + } + + /** + * @dev See {IERC20-approve}. + * + * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on + * `transferFrom`. This is semantically equivalent to an infinite approval. + * + * Requirements: + * + * - `spender` cannot be the zero address. + */ + function approve(address spender, uint256 amount) public virtual override returns (bool) { + address owner = _msgSender(); + _approve(owner, spender, amount); + return true; + } + + /** + * @dev See {IERC20-transferFrom}. + * + * Emits an {Approval} event indicating the updated allowance. This is not + * required by the EIP. See the note at the beginning of {ERC20}. + * + * NOTE: Does not update the allowance if the current allowance + * is the maximum `uint256`. + * + * Requirements: + * + * - `from` and `to` cannot be the zero address. + * - `from` must have a balance of at least `amount`. + * - the caller must have allowance for ``from``'s tokens of at least + * `amount`. + */ + function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) { + address spender = _msgSender(); + _spendAllowance(from, spender, amount); + _transfer(from, to, amount); + return true; + } + + /** + * @dev Atomically increases the allowance granted to `spender` by the caller. + * + * This is an alternative to {approve} that can be used as a mitigation for + * problems described in {IERC20-approve}. + * + * Emits an {Approval} event indicating the updated allowance. + * + * Requirements: + * + * - `spender` cannot be the zero address. + */ + function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { + address owner = _msgSender(); + _approve(owner, spender, allowance(owner, spender) + addedValue); + return true; + } + + /** + * @dev Atomically decreases the allowance granted to `spender` by the caller. + * + * This is an alternative to {approve} that can be used as a mitigation for + * problems described in {IERC20-approve}. + * + * Emits an {Approval} event indicating the updated allowance. + * + * Requirements: + * + * - `spender` cannot be the zero address. + * - `spender` must have allowance for the caller of at least + * `subtractedValue`. + */ + function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { + address owner = _msgSender(); + uint256 currentAllowance = allowance(owner, spender); + require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero"); + unchecked { + _approve(owner, spender, currentAllowance - subtractedValue); + } + + return true; + } + + /** + * @dev Moves `amount` of tokens from `from` to `to`. + * + * This internal function is equivalent to {transfer}, and can be used to + * e.g. implement automatic token fees, slashing mechanisms, etc. + * + * Emits a {Transfer} event. + * + * Requirements: + * + * - `from` cannot be the zero address. + * - `to` cannot be the zero address. + * - `from` must have a balance of at least `amount`. + */ + function _transfer(address from, address to, uint256 amount) internal virtual { + require(from != address(0), "ERC20: transfer from the zero address"); + require(to != address(0), "ERC20: transfer to the zero address"); + + _beforeTokenTransfer(from, to, amount); + + uint256 fromBalance = _balances[from]; + require(fromBalance >= amount, "ERC20: transfer amount exceeds balance"); + unchecked { + _balances[from] = fromBalance - amount; + // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by + // decrementing then incrementing. + _balances[to] += amount; + } + + emit Transfer(from, to, amount); + + _afterTokenTransfer(from, to, amount); + } + + /** @dev Creates `amount` tokens and assigns them to `account`, increasing + * the total supply. + * + * Emits a {Transfer} event with `from` set to the zero address. + * + * Requirements: + * + * - `account` cannot be the zero address. + */ + function _mint(address account, uint256 amount) internal virtual { + require(account != address(0), "ERC20: mint to the zero address"); + + _beforeTokenTransfer(address(0), account, amount); + + _totalSupply += amount; + unchecked { + // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above. + _balances[account] += amount; + } + emit Transfer(address(0), account, amount); + + _afterTokenTransfer(address(0), account, amount); + } + + /** + * @dev Destroys `amount` tokens from `account`, reducing the + * total supply. + * + * Emits a {Transfer} event with `to` set to the zero address. + * + * Requirements: + * + * - `account` cannot be the zero address. + * - `account` must have at least `amount` tokens. + */ + function _burn(address account, uint256 amount) internal virtual { + require(account != address(0), "ERC20: burn from the zero address"); + + _beforeTokenTransfer(account, address(0), amount); + + uint256 accountBalance = _balances[account]; + require(accountBalance >= amount, "ERC20: burn amount exceeds balance"); + unchecked { + _balances[account] = accountBalance - amount; + // Overflow not possible: amount <= accountBalance <= totalSupply. + _totalSupply -= amount; + } + + emit Transfer(account, address(0), amount); + + _afterTokenTransfer(account, address(0), amount); + } + + /** + * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens. + * + * This internal function is equivalent to `approve`, and can be used to + * e.g. set automatic allowances for certain subsystems, etc. + * + * Emits an {Approval} event. + * + * Requirements: + * + * - `owner` cannot be the zero address. + * - `spender` cannot be the zero address. + */ + function _approve(address owner, address spender, uint256 amount) internal virtual { + require(owner != address(0), "ERC20: approve from the zero address"); + require(spender != address(0), "ERC20: approve to the zero address"); + + _allowances[owner][spender] = amount; + emit Approval(owner, spender, amount); + } + + /** + * @dev Updates `owner` s allowance for `spender` based on spent `amount`. + * + * Does not update the allowance amount in case of infinite allowance. + * Revert if not enough allowance is available. + * + * Might emit an {Approval} event. + */ + function _spendAllowance(address owner, address spender, uint256 amount) internal virtual { + uint256 currentAllowance = allowance(owner, spender); + if (currentAllowance != type(uint256).max) { + require(currentAllowance >= amount, "ERC20: insufficient allowance"); + unchecked { + _approve(owner, spender, currentAllowance - amount); + } + } + } + + /** + * @dev Hook that is called before any transfer of tokens. This includes + * minting and burning. + * + * Calling conditions: + * + * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens + * will be transferred to `to`. + * - when `from` is zero, `amount` tokens will be minted for `to`. + * - when `to` is zero, `amount` of ``from``'s tokens will be burned. + * - `from` and `to` are never both zero. + * + * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. + */ + function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {} + + /** + * @dev Hook that is called after any transfer of tokens. This includes + * minting and burning. + * + * Calling conditions: + * + * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens + * has been transferred to `to`. + * - when `from` is zero, `amount` tokens have been minted for `to`. + * - when `to` is zero, `amount` of ``from``'s tokens have been burned. + * - `from` and `to` are never both zero. + * + * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. + */ + function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {} +} + + +// File contracts/interfaces/IBurnableToken.sol + +// Original license: SPDX_License_Identifier: MIT +pragma solidity ^0.8.10; + +interface IBurnableToken { + function burn(address user, uint256 amount) external; +} + + +// File contracts/interfaces/IBurnRedeemable.sol + +// Original license: SPDX_License_Identifier: MIT +pragma solidity ^0.8.10; + +interface IBurnRedeemable { + event Redeemed( + address indexed user, + address indexed xenContract, + address indexed tokenContract, + uint256 xenAmount, + uint256 tokenAmount + ); + + function onTokenBurned(address user, uint256 amount) external; +} + + +// File contracts/test/BadBurner.sol + +// Original license: SPDX_License_Identifier: MIT +pragma solidity ^0.8.10; + + + + +/** + This contract implements IBurnRedeemable but tries to make reentrant burn call to XENCrypto + */ +contract BadBurner is Context, IBurnRedeemable, IERC165, ERC20("Bad Burner", "BAD") { + IBurnableToken public xenContract; + + constructor(address _xenContractAddress) { + xenContract = IBurnableToken(_xenContractAddress); + } + + function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { + return interfaceId == type(IBurnRedeemable).interfaceId; + } + + function exchangeTokens(uint256 amount) external { + xenContract.burn(_msgSender(), amount); + } + + function onTokenBurned(address, uint256 amount) public { + xenContract.burn(_msgSender(), amount); + } +} + + +// File contracts/test/Burner.sol + +// Original license: SPDX_License_Identifier: MIT +pragma solidity ^0.8.10; + + + + +contract Burner is Context, IBurnRedeemable, IERC165, ERC20("Burner", "BURN") { + IBurnableToken public xenContract; + + constructor(address _xenContractAddress) { + xenContract = IBurnableToken(_xenContractAddress); + } + + function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { + return interfaceId == type(IBurnRedeemable).interfaceId; + } + + function exchangeTokens(uint256 amount) external { + xenContract.burn(_msgSender(), amount); + } + + function onTokenBurned(address user, uint256 amount) public { + require(_msgSender() == address(xenContract), "Burner: wrong caller"); + require(user != address(0), "Burner: zero user address"); + require(amount != 0, "Burner: zero amount"); + + _mint(user, amount); + } +} + + +// File contracts/test/RevertingBurner.sol + +// Original license: SPDX_License_Identifier: MIT +pragma solidity ^0.8.10; + + + + +/** + This contract implements IBurnRedeemable but reverts in the callback hook + */ +contract RevertingBurner is Context, IBurnRedeemable, IERC165, ERC20("Reverting Burner", "REV") { + IBurnableToken public xenContract; + + constructor(address _xenContractAddress) { + xenContract = IBurnableToken(_xenContractAddress); + } + + function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { + return interfaceId == type(IBurnRedeemable).interfaceId; + } + + function exchangeTokens(uint256 amount) external { + xenContract.burn(_msgSender(), amount); + } + + function onTokenBurned(address, uint256) public pure { + revert(); + } +} + + +// File abdk-libraries-solidity/ABDKMath64x64.sol@v3.0.0 + +// Original license: SPDX_License_Identifier: BSD-4-Clause +/* + * ABDK Math 64.64 Smart Contract Library. Copyright © 2019 by ABDK Consulting. + * Author: Mikhail Vladimirov + */ +pragma solidity ^0.8.0; + +/** + * Smart contract library of mathematical functions operating with signed + * 64.64-bit fixed point numbers. Signed 64.64-bit fixed point number is + * basically a simple fraction whose numerator is signed 128-bit integer and + * denominator is 2^64. As long as denominator is always the same, there is no + * need to store it, thus in Solidity signed 64.64-bit fixed point numbers are + * represented by int128 type holding only the numerator. + */ +library ABDKMath64x64 { + /* + * Minimum value signed 64.64-bit fixed point number may have. + */ + int128 private constant MIN_64x64 = -0x80000000000000000000000000000000; + + /* + * Maximum value signed 64.64-bit fixed point number may have. + */ + int128 private constant MAX_64x64 = 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; + + /** + * Convert signed 256-bit integer number into signed 64.64-bit fixed point + * number. Revert on overflow. + * + * @param x signed 256-bit integer number + * @return signed 64.64-bit fixed point number + */ + function fromInt (int256 x) internal pure returns (int128) { + unchecked { + require (x >= -0x8000000000000000 && x <= 0x7FFFFFFFFFFFFFFF); + return int128 (x << 64); + } + } + + /** + * Convert signed 64.64 fixed point number into signed 64-bit integer number + * rounding down. + * + * @param x signed 64.64-bit fixed point number + * @return signed 64-bit integer number + */ + function toInt (int128 x) internal pure returns (int64) { + unchecked { + return int64 (x >> 64); + } + } + + /** + * Convert unsigned 256-bit integer number into signed 64.64-bit fixed point + * number. Revert on overflow. + * + * @param x unsigned 256-bit integer number + * @return signed 64.64-bit fixed point number + */ + function fromUInt (uint256 x) internal pure returns (int128) { + unchecked { + require (x <= 0x7FFFFFFFFFFFFFFF); + return int128 (int256 (x << 64)); + } + } + + /** + * Convert signed 64.64 fixed point number into unsigned 64-bit integer + * number rounding down. Revert on underflow. + * + * @param x signed 64.64-bit fixed point number + * @return unsigned 64-bit integer number + */ + function toUInt (int128 x) internal pure returns (uint64) { + unchecked { + require (x >= 0); + return uint64 (uint128 (x >> 64)); + } + } + + /** + * Convert signed 128.128 fixed point number into signed 64.64-bit fixed point + * number rounding down. Revert on overflow. + * + * @param x signed 128.128-bin fixed point number + * @return signed 64.64-bit fixed point number + */ + function from128x128 (int256 x) internal pure returns (int128) { + unchecked { + int256 result = x >> 64; + require (result >= MIN_64x64 && result <= MAX_64x64); + return int128 (result); + } + } + + /** + * Convert signed 64.64 fixed point number into signed 128.128 fixed point + * number. + * + * @param x signed 64.64-bit fixed point number + * @return signed 128.128 fixed point number + */ + function to128x128 (int128 x) internal pure returns (int256) { + unchecked { + return int256 (x) << 64; + } + } + + /** + * Calculate x + y. Revert on overflow. + * + * @param x signed 64.64-bit fixed point number + * @param y signed 64.64-bit fixed point number + * @return signed 64.64-bit fixed point number + */ + function add (int128 x, int128 y) internal pure returns (int128) { + unchecked { + int256 result = int256(x) + y; + require (result >= MIN_64x64 && result <= MAX_64x64); + return int128 (result); + } + } + + /** + * Calculate x - y. Revert on overflow. + * + * @param x signed 64.64-bit fixed point number + * @param y signed 64.64-bit fixed point number + * @return signed 64.64-bit fixed point number + */ + function sub (int128 x, int128 y) internal pure returns (int128) { + unchecked { + int256 result = int256(x) - y; + require (result >= MIN_64x64 && result <= MAX_64x64); + return int128 (result); + } + } + + /** + * Calculate x * y rounding down. Revert on overflow. + * + * @param x signed 64.64-bit fixed point number + * @param y signed 64.64-bit fixed point number + * @return signed 64.64-bit fixed point number + */ + function mul (int128 x, int128 y) internal pure returns (int128) { + unchecked { + int256 result = int256(x) * y >> 64; + require (result >= MIN_64x64 && result <= MAX_64x64); + return int128 (result); + } + } + + /** + * Calculate x * y rounding towards zero, where x is signed 64.64 fixed point + * number and y is signed 256-bit integer number. Revert on overflow. + * + * @param x signed 64.64 fixed point number + * @param y signed 256-bit integer number + * @return signed 256-bit integer number + */ + function muli (int128 x, int256 y) internal pure returns (int256) { + unchecked { + if (x == MIN_64x64) { + require (y >= -0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF && + y <= 0x1000000000000000000000000000000000000000000000000); + return -y << 63; + } else { + bool negativeResult = false; + if (x < 0) { + x = -x; + negativeResult = true; + } + if (y < 0) { + y = -y; // We rely on overflow behavior here + negativeResult = !negativeResult; + } + uint256 absoluteResult = mulu (x, uint256 (y)); + if (negativeResult) { + require (absoluteResult <= + 0x8000000000000000000000000000000000000000000000000000000000000000); + return -int256 (absoluteResult); // We rely on overflow behavior here + } else { + require (absoluteResult <= + 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); + return int256 (absoluteResult); + } + } + } + } + + /** + * Calculate x * y rounding down, where x is signed 64.64 fixed point number + * and y is unsigned 256-bit integer number. Revert on overflow. + * + * @param x signed 64.64 fixed point number + * @param y unsigned 256-bit integer number + * @return unsigned 256-bit integer number + */ + function mulu (int128 x, uint256 y) internal pure returns (uint256) { + unchecked { + if (y == 0) return 0; + + require (x >= 0); + + uint256 lo = (uint256 (int256 (x)) * (y & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)) >> 64; + uint256 hi = uint256 (int256 (x)) * (y >> 128); + + require (hi <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); + hi <<= 64; + + require (hi <= + 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - lo); + return hi + lo; + } + } + + /** + * Calculate x / y rounding towards zero. Revert on overflow or when y is + * zero. + * + * @param x signed 64.64-bit fixed point number + * @param y signed 64.64-bit fixed point number + * @return signed 64.64-bit fixed point number + */ + function div (int128 x, int128 y) internal pure returns (int128) { + unchecked { + require (y != 0); + int256 result = (int256 (x) << 64) / y; + require (result >= MIN_64x64 && result <= MAX_64x64); + return int128 (result); + } + } + + /** + * Calculate x / y rounding towards zero, where x and y are signed 256-bit + * integer numbers. Revert on overflow or when y is zero. + * + * @param x signed 256-bit integer number + * @param y signed 256-bit integer number + * @return signed 64.64-bit fixed point number + */ + function divi (int256 x, int256 y) internal pure returns (int128) { + unchecked { + require (y != 0); + + bool negativeResult = false; + if (x < 0) { + x = -x; // We rely on overflow behavior here + negativeResult = true; + } + if (y < 0) { + y = -y; // We rely on overflow behavior here + negativeResult = !negativeResult; + } + uint128 absoluteResult = divuu (uint256 (x), uint256 (y)); + if (negativeResult) { + require (absoluteResult <= 0x80000000000000000000000000000000); + return -int128 (absoluteResult); // We rely on overflow behavior here + } else { + require (absoluteResult <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); + return int128 (absoluteResult); // We rely on overflow behavior here + } + } + } + + /** + * Calculate x / y rounding towards zero, where x and y are unsigned 256-bit + * integer numbers. Revert on overflow or when y is zero. + * + * @param x unsigned 256-bit integer number + * @param y unsigned 256-bit integer number + * @return signed 64.64-bit fixed point number + */ + function divu (uint256 x, uint256 y) internal pure returns (int128) { + unchecked { + require (y != 0); + uint128 result = divuu (x, y); + require (result <= uint128 (MAX_64x64)); + return int128 (result); + } + } + + /** + * Calculate -x. Revert on overflow. + * + * @param x signed 64.64-bit fixed point number + * @return signed 64.64-bit fixed point number + */ + function neg (int128 x) internal pure returns (int128) { + unchecked { + require (x != MIN_64x64); + return -x; + } + } + + /** + * Calculate |x|. Revert on overflow. + * + * @param x signed 64.64-bit fixed point number + * @return signed 64.64-bit fixed point number + */ + function abs (int128 x) internal pure returns (int128) { + unchecked { + require (x != MIN_64x64); + return x < 0 ? -x : x; + } + } + + /** + * Calculate 1 / x rounding towards zero. Revert on overflow or when x is + * zero. + * + * @param x signed 64.64-bit fixed point number + * @return signed 64.64-bit fixed point number + */ + function inv (int128 x) internal pure returns (int128) { + unchecked { + require (x != 0); + int256 result = int256 (0x100000000000000000000000000000000) / x; + require (result >= MIN_64x64 && result <= MAX_64x64); + return int128 (result); + } + } + + /** + * Calculate arithmetics average of x and y, i.e. (x + y) / 2 rounding down. + * + * @param x signed 64.64-bit fixed point number + * @param y signed 64.64-bit fixed point number + * @return signed 64.64-bit fixed point number + */ + function avg (int128 x, int128 y) internal pure returns (int128) { + unchecked { + return int128 ((int256 (x) + int256 (y)) >> 1); + } + } + + /** + * Calculate geometric average of x and y, i.e. sqrt (x * y) rounding down. + * Revert on overflow or in case x * y is negative. + * + * @param x signed 64.64-bit fixed point number + * @param y signed 64.64-bit fixed point number + * @return signed 64.64-bit fixed point number + */ + function gavg (int128 x, int128 y) internal pure returns (int128) { + unchecked { + int256 m = int256 (x) * int256 (y); + require (m >= 0); + require (m < + 0x4000000000000000000000000000000000000000000000000000000000000000); + return int128 (sqrtu (uint256 (m))); + } + } + + /** + * Calculate x^y assuming 0^0 is 1, where x is signed 64.64 fixed point number + * and y is unsigned 256-bit integer number. Revert on overflow. + * + * @param x signed 64.64-bit fixed point number + * @param y uint256 value + * @return signed 64.64-bit fixed point number + */ + function pow (int128 x, uint256 y) internal pure returns (int128) { + unchecked { + bool negative = x < 0 && y & 1 == 1; + + uint256 absX = uint128 (x < 0 ? -x : x); + uint256 absResult; + absResult = 0x100000000000000000000000000000000; + + if (absX <= 0x10000000000000000) { + absX <<= 63; + while (y != 0) { + if (y & 0x1 != 0) { + absResult = absResult * absX >> 127; + } + absX = absX * absX >> 127; + + if (y & 0x2 != 0) { + absResult = absResult * absX >> 127; + } + absX = absX * absX >> 127; + + if (y & 0x4 != 0) { + absResult = absResult * absX >> 127; + } + absX = absX * absX >> 127; + + if (y & 0x8 != 0) { + absResult = absResult * absX >> 127; + } + absX = absX * absX >> 127; + + y >>= 4; + } + + absResult >>= 64; + } else { + uint256 absXShift = 63; + if (absX < 0x1000000000000000000000000) { absX <<= 32; absXShift -= 32; } + if (absX < 0x10000000000000000000000000000) { absX <<= 16; absXShift -= 16; } + if (absX < 0x1000000000000000000000000000000) { absX <<= 8; absXShift -= 8; } + if (absX < 0x10000000000000000000000000000000) { absX <<= 4; absXShift -= 4; } + if (absX < 0x40000000000000000000000000000000) { absX <<= 2; absXShift -= 2; } + if (absX < 0x80000000000000000000000000000000) { absX <<= 1; absXShift -= 1; } + + uint256 resultShift = 0; + while (y != 0) { + require (absXShift < 64); + + if (y & 0x1 != 0) { + absResult = absResult * absX >> 127; + resultShift += absXShift; + if (absResult > 0x100000000000000000000000000000000) { + absResult >>= 1; + resultShift += 1; + } + } + absX = absX * absX >> 127; + absXShift <<= 1; + if (absX >= 0x100000000000000000000000000000000) { + absX >>= 1; + absXShift += 1; + } + + y >>= 1; + } + + require (resultShift < 64); + absResult >>= 64 - resultShift; + } + int256 result = negative ? -int256 (absResult) : int256 (absResult); + require (result >= MIN_64x64 && result <= MAX_64x64); + return int128 (result); + } + } + + /** + * Calculate sqrt (x) rounding down. Revert if x < 0. + * + * @param x signed 64.64-bit fixed point number + * @return signed 64.64-bit fixed point number + */ + function sqrt (int128 x) internal pure returns (int128) { + unchecked { + require (x >= 0); + return int128 (sqrtu (uint256 (int256 (x)) << 64)); + } + } + + /** + * Calculate binary logarithm of x. Revert if x <= 0. + * + * @param x signed 64.64-bit fixed point number + * @return signed 64.64-bit fixed point number + */ + function log_2 (int128 x) internal pure returns (int128) { + unchecked { + require (x > 0); + + int256 msb = 0; + int256 xc = x; + if (xc >= 0x10000000000000000) { xc >>= 64; msb += 64; } + if (xc >= 0x100000000) { xc >>= 32; msb += 32; } + if (xc >= 0x10000) { xc >>= 16; msb += 16; } + if (xc >= 0x100) { xc >>= 8; msb += 8; } + if (xc >= 0x10) { xc >>= 4; msb += 4; } + if (xc >= 0x4) { xc >>= 2; msb += 2; } + if (xc >= 0x2) msb += 1; // No need to shift xc anymore + + int256 result = msb - 64 << 64; + uint256 ux = uint256 (int256 (x)) << uint256 (127 - msb); + for (int256 bit = 0x8000000000000000; bit > 0; bit >>= 1) { + ux *= ux; + uint256 b = ux >> 255; + ux >>= 127 + b; + result += bit * int256 (b); + } + + return int128 (result); + } + } + + /** + * Calculate natural logarithm of x. Revert if x <= 0. + * + * @param x signed 64.64-bit fixed point number + * @return signed 64.64-bit fixed point number + */ + function ln (int128 x) internal pure returns (int128) { + unchecked { + require (x > 0); + + return int128 (int256 ( + uint256 (int256 (log_2 (x))) * 0xB17217F7D1CF79ABC9E3B39803F2F6AF >> 128)); + } + } + + /** + * Calculate binary exponent of x. Revert on overflow. + * + * @param x signed 64.64-bit fixed point number + * @return signed 64.64-bit fixed point number + */ + function exp_2 (int128 x) internal pure returns (int128) { + unchecked { + require (x < 0x400000000000000000); // Overflow + + if (x < -0x400000000000000000) return 0; // Underflow + + uint256 result = 0x80000000000000000000000000000000; + + if (x & 0x8000000000000000 > 0) + result = result * 0x16A09E667F3BCC908B2FB1366EA957D3E >> 128; + if (x & 0x4000000000000000 > 0) + result = result * 0x1306FE0A31B7152DE8D5A46305C85EDEC >> 128; + if (x & 0x2000000000000000 > 0) + result = result * 0x1172B83C7D517ADCDF7C8C50EB14A791F >> 128; + if (x & 0x1000000000000000 > 0) + result = result * 0x10B5586CF9890F6298B92B71842A98363 >> 128; + if (x & 0x800000000000000 > 0) + result = result * 0x1059B0D31585743AE7C548EB68CA417FD >> 128; + if (x & 0x400000000000000 > 0) + result = result * 0x102C9A3E778060EE6F7CACA4F7A29BDE8 >> 128; + if (x & 0x200000000000000 > 0) + result = result * 0x10163DA9FB33356D84A66AE336DCDFA3F >> 128; + if (x & 0x100000000000000 > 0) + result = result * 0x100B1AFA5ABCBED6129AB13EC11DC9543 >> 128; + if (x & 0x80000000000000 > 0) + result = result * 0x10058C86DA1C09EA1FF19D294CF2F679B >> 128; + if (x & 0x40000000000000 > 0) + result = result * 0x1002C605E2E8CEC506D21BFC89A23A00F >> 128; + if (x & 0x20000000000000 > 0) + result = result * 0x100162F3904051FA128BCA9C55C31E5DF >> 128; + if (x & 0x10000000000000 > 0) + result = result * 0x1000B175EFFDC76BA38E31671CA939725 >> 128; + if (x & 0x8000000000000 > 0) + result = result * 0x100058BA01FB9F96D6CACD4B180917C3D >> 128; + if (x & 0x4000000000000 > 0) + result = result * 0x10002C5CC37DA9491D0985C348C68E7B3 >> 128; + if (x & 0x2000000000000 > 0) + result = result * 0x1000162E525EE054754457D5995292026 >> 128; + if (x & 0x1000000000000 > 0) + result = result * 0x10000B17255775C040618BF4A4ADE83FC >> 128; + if (x & 0x800000000000 > 0) + result = result * 0x1000058B91B5BC9AE2EED81E9B7D4CFAB >> 128; + if (x & 0x400000000000 > 0) + result = result * 0x100002C5C89D5EC6CA4D7C8ACC017B7C9 >> 128; + if (x & 0x200000000000 > 0) + result = result * 0x10000162E43F4F831060E02D839A9D16D >> 128; + if (x & 0x100000000000 > 0) + result = result * 0x100000B1721BCFC99D9F890EA06911763 >> 128; + if (x & 0x80000000000 > 0) + result = result * 0x10000058B90CF1E6D97F9CA14DBCC1628 >> 128; + if (x & 0x40000000000 > 0) + result = result * 0x1000002C5C863B73F016468F6BAC5CA2B >> 128; + if (x & 0x20000000000 > 0) + result = result * 0x100000162E430E5A18F6119E3C02282A5 >> 128; + if (x & 0x10000000000 > 0) + result = result * 0x1000000B1721835514B86E6D96EFD1BFE >> 128; + if (x & 0x8000000000 > 0) + result = result * 0x100000058B90C0B48C6BE5DF846C5B2EF >> 128; + if (x & 0x4000000000 > 0) + result = result * 0x10000002C5C8601CC6B9E94213C72737A >> 128; + if (x & 0x2000000000 > 0) + result = result * 0x1000000162E42FFF037DF38AA2B219F06 >> 128; + if (x & 0x1000000000 > 0) + result = result * 0x10000000B17217FBA9C739AA5819F44F9 >> 128; + if (x & 0x800000000 > 0) + result = result * 0x1000000058B90BFCDEE5ACD3C1CEDC823 >> 128; + if (x & 0x400000000 > 0) + result = result * 0x100000002C5C85FE31F35A6A30DA1BE50 >> 128; + if (x & 0x200000000 > 0) + result = result * 0x10000000162E42FF0999CE3541B9FFFCF >> 128; + if (x & 0x100000000 > 0) + result = result * 0x100000000B17217F80F4EF5AADDA45554 >> 128; + if (x & 0x80000000 > 0) + result = result * 0x10000000058B90BFBF8479BD5A81B51AD >> 128; + if (x & 0x40000000 > 0) + result = result * 0x1000000002C5C85FDF84BD62AE30A74CC >> 128; + if (x & 0x20000000 > 0) + result = result * 0x100000000162E42FEFB2FED257559BDAA >> 128; + if (x & 0x10000000 > 0) + result = result * 0x1000000000B17217F7D5A7716BBA4A9AE >> 128; + if (x & 0x8000000 > 0) + result = result * 0x100000000058B90BFBE9DDBAC5E109CCE >> 128; + if (x & 0x4000000 > 0) + result = result * 0x10000000002C5C85FDF4B15DE6F17EB0D >> 128; + if (x & 0x2000000 > 0) + result = result * 0x1000000000162E42FEFA494F1478FDE05 >> 128; + if (x & 0x1000000 > 0) + result = result * 0x10000000000B17217F7D20CF927C8E94C >> 128; + if (x & 0x800000 > 0) + result = result * 0x1000000000058B90BFBE8F71CB4E4B33D >> 128; + if (x & 0x400000 > 0) + result = result * 0x100000000002C5C85FDF477B662B26945 >> 128; + if (x & 0x200000 > 0) + result = result * 0x10000000000162E42FEFA3AE53369388C >> 128; + if (x & 0x100000 > 0) + result = result * 0x100000000000B17217F7D1D351A389D40 >> 128; + if (x & 0x80000 > 0) + result = result * 0x10000000000058B90BFBE8E8B2D3D4EDE >> 128; + if (x & 0x40000 > 0) + result = result * 0x1000000000002C5C85FDF4741BEA6E77E >> 128; + if (x & 0x20000 > 0) + result = result * 0x100000000000162E42FEFA39FE95583C2 >> 128; + if (x & 0x10000 > 0) + result = result * 0x1000000000000B17217F7D1CFB72B45E1 >> 128; + if (x & 0x8000 > 0) + result = result * 0x100000000000058B90BFBE8E7CC35C3F0 >> 128; + if (x & 0x4000 > 0) + result = result * 0x10000000000002C5C85FDF473E242EA38 >> 128; + if (x & 0x2000 > 0) + result = result * 0x1000000000000162E42FEFA39F02B772C >> 128; + if (x & 0x1000 > 0) + result = result * 0x10000000000000B17217F7D1CF7D83C1A >> 128; + if (x & 0x800 > 0) + result = result * 0x1000000000000058B90BFBE8E7BDCBE2E >> 128; + if (x & 0x400 > 0) + result = result * 0x100000000000002C5C85FDF473DEA871F >> 128; + if (x & 0x200 > 0) + result = result * 0x10000000000000162E42FEFA39EF44D91 >> 128; + if (x & 0x100 > 0) + result = result * 0x100000000000000B17217F7D1CF79E949 >> 128; + if (x & 0x80 > 0) + result = result * 0x10000000000000058B90BFBE8E7BCE544 >> 128; + if (x & 0x40 > 0) + result = result * 0x1000000000000002C5C85FDF473DE6ECA >> 128; + if (x & 0x20 > 0) + result = result * 0x100000000000000162E42FEFA39EF366F >> 128; + if (x & 0x10 > 0) + result = result * 0x1000000000000000B17217F7D1CF79AFA >> 128; + if (x & 0x8 > 0) + result = result * 0x100000000000000058B90BFBE8E7BCD6D >> 128; + if (x & 0x4 > 0) + result = result * 0x10000000000000002C5C85FDF473DE6B2 >> 128; + if (x & 0x2 > 0) + result = result * 0x1000000000000000162E42FEFA39EF358 >> 128; + if (x & 0x1 > 0) + result = result * 0x10000000000000000B17217F7D1CF79AB >> 128; + + result >>= uint256 (int256 (63 - (x >> 64))); + require (result <= uint256 (int256 (MAX_64x64))); + + return int128 (int256 (result)); + } + } + + /** + * Calculate natural exponent of x. Revert on overflow. + * + * @param x signed 64.64-bit fixed point number + * @return signed 64.64-bit fixed point number + */ + function exp (int128 x) internal pure returns (int128) { + unchecked { + require (x < 0x400000000000000000); // Overflow + + if (x < -0x400000000000000000) return 0; // Underflow + + return exp_2 ( + int128 (int256 (x) * 0x171547652B82FE1777D0FFDA0D23A7D12 >> 128)); + } + } + + /** + * Calculate x / y rounding towards zero, where x and y are unsigned 256-bit + * integer numbers. Revert on overflow or when y is zero. + * + * @param x unsigned 256-bit integer number + * @param y unsigned 256-bit integer number + * @return unsigned 64.64-bit fixed point number + */ + function divuu (uint256 x, uint256 y) private pure returns (uint128) { + unchecked { + require (y != 0); + + uint256 result; + + if (x <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) + result = (x << 64) / y; + else { + uint256 msb = 192; + uint256 xc = x >> 192; + if (xc >= 0x100000000) { xc >>= 32; msb += 32; } + if (xc >= 0x10000) { xc >>= 16; msb += 16; } + if (xc >= 0x100) { xc >>= 8; msb += 8; } + if (xc >= 0x10) { xc >>= 4; msb += 4; } + if (xc >= 0x4) { xc >>= 2; msb += 2; } + if (xc >= 0x2) msb += 1; // No need to shift xc anymore + + result = (x << 255 - msb) / ((y - 1 >> msb - 191) + 1); + require (result <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); + + uint256 hi = result * (y >> 128); + uint256 lo = result * (y & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); + + uint256 xh = x >> 192; + uint256 xl = x << 64; + + if (xl < lo) xh -= 1; + xl -= lo; // We rely on overflow behavior here + lo = hi << 128; + if (xl < lo) xh -= 1; + xl -= lo; // We rely on overflow behavior here + + assert (xh == hi >> 128); + + result += xl / y; + } + + require (result <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); + return uint128 (result); + } + } + + /** + * Calculate sqrt (x) rounding down, where x is unsigned 256-bit integer + * number. + * + * @param x unsigned 256-bit integer number + * @return unsigned 128-bit integer number + */ + function sqrtu (uint256 x) private pure returns (uint128) { + unchecked { + if (x == 0) return 0; + else { + uint256 xx = x; + uint256 r = 1; + if (xx >= 0x100000000000000000000000000000000) { xx >>= 128; r <<= 64; } + if (xx >= 0x10000000000000000) { xx >>= 64; r <<= 32; } + if (xx >= 0x100000000) { xx >>= 32; r <<= 16; } + if (xx >= 0x10000) { xx >>= 16; r <<= 8; } + if (xx >= 0x100) { xx >>= 8; r <<= 4; } + if (xx >= 0x10) { xx >>= 4; r <<= 2; } + if (xx >= 0x8) { r <<= 1; } + r = (r + x / r) >> 1; + r = (r + x / r) >> 1; + r = (r + x / r) >> 1; + r = (r + x / r) >> 1; + r = (r + x / r) >> 1; + r = (r + x / r) >> 1; + r = (r + x / r) >> 1; // Seven iterations should be enough + uint256 r1 = x / r; + return uint128 (r < r1 ? r : r1); + } + } + } +} + + +// File contracts/interfaces/IRankedMintingToken.sol + +// Original license: SPDX_License_Identifier: MIT +pragma solidity ^0.8.10; + +interface IRankedMintingToken { + event RankClaimed(address indexed user, uint256 term, uint256 rank); + + event MintClaimed(address indexed user, uint256 rewardAmount); + + function claimRank(uint256 term) external; + + function claimMintReward() external; +} + + +// File contracts/interfaces/IStakingToken.sol + +// Original license: SPDX_License_Identifier: MIT +pragma solidity ^0.8.10; + +interface IStakingToken { + event Staked(address indexed user, uint256 amount, uint256 term); + + event Withdrawn(address indexed user, uint256 amount, uint256 reward); + + function stake(uint256 amount, uint256 term) external; + + function withdraw() external; +} + + +// File contracts/test/XENCryptoRank100001.sol + +// Original license: SPDX_License_Identifier: MIT +pragma solidity ^0.8.10; + + + + + + + +contract XENCrypto100001 is + Context, + IRankedMintingToken, + IStakingToken, + IBurnableToken, + ERC20("XEN Crypto 100k", "XEN100K") +{ + using ABDKMath64x64 for int128; + using ABDKMath64x64 for uint256; + + // INTERNAL TYPE TO DESCRIBE A RANK STAKE + struct MintInfo { + address user; + uint256 term; + uint256 maturityTs; + uint256 rank; + uint256 amplifier; + uint256 eaaRate; + } + + // INTERNAL TYPE TO DESCRIBE A XEN STAKE + struct StakeInfo { + uint256 term; + uint256 maturityTs; + uint256 amount; + uint256 apy; + } + + // PUBLIC CONSTANTS + + uint256 public constant SECONDS_IN_DAY = 3_600 * 24; + uint256 public constant DAYS_IN_YEAR = 365; + + // N.B. modified original contract to test MaxTerm after Global Rank > 100_001 + uint256 public constant GENESIS_RANK = 100_001; + + uint256 public constant MIN_TERM = 1 * SECONDS_IN_DAY - 1; + uint256 public constant MAX_TERM_START = 100 * SECONDS_IN_DAY; + uint256 public constant MAX_TERM_END = 1_000 * SECONDS_IN_DAY; + uint256 public constant TERM_AMPLIFIER = 15; + uint256 public constant TERM_AMPLIFIER_THRESHOLD = 5_000; + uint256 public constant REWARD_AMPLIFIER_START = 3_000; + uint256 public constant REWARD_AMPLIFIER_END = 1; + uint256 public constant EAA_PM_START = 100; + uint256 public constant EAA_PM_STEP = 1; + uint256 public constant EAA_RANK_STEP = 100_000; + uint256 public constant WITHDRAWAL_WINDOW_DAYS = 7; + + uint256 public constant XEN_MIN_STAKE = 0; + + uint256 public constant XEN_MIN_BURN = 0; + + uint256 public constant XEN_APY_START = 20; + uint256 public constant XEN_APY_DAYS_STEP = 90; + uint256 public constant XEN_APY_END = 2; + + string public constant AUTHORS = "@MrJackLevin @lbelyaev faircrypto.org"; + + // PUBLIC STATE, READABLE VIA NAMESAKE GETTERS + + uint256 public immutable genesisTs; + uint256 public globalRank = GENESIS_RANK; + uint256 public activeMinters; + uint256 public activeStakes; + uint256 public totalXenStaked; + // user address => XEN mint info + mapping(address => MintInfo) public userMints; + // user address => XEN stake info + mapping(address => StakeInfo) public userStakes; + // user address => XEN burn amount + mapping(address => uint256) public userBurns; + + // CONSTRUCTOR + constructor() { + genesisTs = block.timestamp; + } + + function _min(uint256 a, uint256 b) private pure returns (uint256) { + if (a > b) return b; + return a; + } + + function _max(uint256 a, uint256 b) private pure returns (uint256) { + if (a > b) return a; + return b; + } + + // PRIVATE METHODS + + /** + * @dev calculates current MaxTerm based on GlobalTank + * (if GlobalTank crosses over TERM_AMPLIFIER_THRESHOLD) + */ + function _calculateMaxTerm() private view returns (uint256) { + if (globalRank > TERM_AMPLIFIER_THRESHOLD) { + uint256 delta = globalRank.fromUInt().log_2().mul(TERM_AMPLIFIER.fromUInt()).toUInt(); + uint256 newMax = MAX_TERM_START + delta * SECONDS_IN_DAY; + return _min(newMax, MAX_TERM_END); + } + return MAX_TERM_START; + } + + /** + * @dev calculates Withdrawal Penalty depending on lateness + */ + function _penalty(uint256 secsLate) private pure returns (uint256) { + // =MIN(2^(daysLate+3)/window-1,100) + uint256 daysLate = secsLate / SECONDS_IN_DAY; + if (daysLate > WITHDRAWAL_WINDOW_DAYS - 1) return 100; + uint256 penalty = (uint256(1) << (daysLate + 3)) / WITHDRAWAL_WINDOW_DAYS - 1; + return _min(penalty, 100); + } + + /** + * @dev calculates net Mint Reward (adjusted for Penalty) + */ + function _calculateMintReward( + uint256 cRank, + uint256 term, + uint256 maturityTs, + uint256 amplifier, + uint256 eeaRate + ) private view returns (uint256) { + uint256 secsLate = block.timestamp - maturityTs; + uint256 penalty = _penalty(secsLate); + uint256 rankDelta = _max(globalRank - cRank, 2); + uint256 EAA = (1_000 + eeaRate); + uint256 reward = getGrossReward(rankDelta, amplifier, term, EAA); + return (reward * (100 - penalty)) / 100; + } + + /** + * @dev cleans up Stake storage (gets some Gas credit;)) + */ + function _cleanUpStake() private { + delete userMints[_msgSender()]; + activeMinters--; + } + + /** + * @dev calculates XEN Stake Reward + */ + function _calculateStakeReward( + uint256 amount, + uint256 term, + uint256 maturityTs, + uint256 apy + ) private view returns (uint256) { + if (block.timestamp > maturityTs) { + uint256 rate = (apy * term * 1_000_000) / DAYS_IN_YEAR; + return (amount * rate) / 100_000_000; + } + return 0; + } + + /** + * @dev calculates Reward Amplifier + */ + function _calculateRewardAmplifier() private view returns (uint256) { + uint256 amplifierDecrease = (block.timestamp - genesisTs) / SECONDS_IN_DAY; + if (amplifierDecrease < REWARD_AMPLIFIER_START) { + return _max(REWARD_AMPLIFIER_START - amplifierDecrease, REWARD_AMPLIFIER_END); + } else { + return REWARD_AMPLIFIER_END; + } + } + + /** + * @dev calculates Early Adopter Amplifier Rate (in 1/000ths) + * actual EAA is (1_000 + EAAR) / 1_000 + */ + function _calculateEAARate() private view returns (uint256) { + uint256 decrease = (EAA_PM_STEP * globalRank) / EAA_RANK_STEP; + if (decrease > EAA_PM_START) return 0; + return EAA_PM_START - decrease; + } + + /** + * @dev calculates APY (in %) + */ + function _calculateAPY() private view returns (uint256) { + uint256 decrease = (block.timestamp - genesisTs) / (SECONDS_IN_DAY * XEN_APY_DAYS_STEP); + if (XEN_APY_START - XEN_APY_END < decrease) return XEN_APY_END; + return XEN_APY_START - decrease; + } + + /** + * @dev calculates Reward Amplifier + */ + function _createStake(uint256 amount, uint256 term) private { + userStakes[_msgSender()] = StakeInfo({ + term: term, + maturityTs: block.timestamp + term * SECONDS_IN_DAY, + amount: amount, + apy: _calculateAPY() + }); + activeStakes++; + totalXenStaked += amount; + } + + // PUBLIC CONVENIENCE GETTERS + + /** + * @dev calculates gross Mint Reward + */ + function getGrossReward( + uint256 rankDelta, + uint256 amplifier, + uint256 term, + uint256 eaa + ) public pure returns (uint256) { + int128 log128 = rankDelta.fromUInt().log_2(); + int128 reward128 = log128.mul(amplifier.fromUInt()).mul(term.fromUInt()).mul(eaa.fromUInt()); + return reward128.div(uint256(1_000).fromUInt()).toUInt(); + } + + /** + * @dev returns Rank Stake object associated with User account address + */ + function getUserMint() external view returns (MintInfo memory) { + return userMints[_msgSender()]; + } + + /** + * @dev returns XEN Stake object associated with User account address + */ + function getUserStake() external view returns (StakeInfo memory) { + return userStakes[_msgSender()]; + } + + /** + * @dev returns current AMP + */ + function getCurrentAMP() external view returns (uint256) { + return _calculateRewardAmplifier(); + } + + /** + * @dev returns current EAA Rate + */ + function getCurrentEAAR() external view returns (uint256) { + return _calculateEAARate(); + } + + /** + * @dev returns current APY + */ + function getCurrentAPY() external view returns (uint256) { + return _calculateAPY(); + } + + // PUBLIC STATE-CHANGING METHODS + + /** + * @dev accepts User Rank Stake provided all checks pass (incl. no current Stake) + */ + function claimRank(uint256 term) external { + uint256 termSec = term * SECONDS_IN_DAY; + require(termSec > MIN_TERM, "CRank: Term less than min"); + require(termSec < _calculateMaxTerm() + 1, "CRank: Term more than current max term"); + require(userMints[_msgSender()].rank == 0, "CRank: Mint already in progress"); + + // create and store new MintInfo + MintInfo memory mintInfo = MintInfo({ + user: _msgSender(), + term: term, + maturityTs: block.timestamp + termSec, + rank: globalRank, + amplifier: _calculateRewardAmplifier(), + eaaRate: _calculateEAARate() + }); + userMints[_msgSender()] = mintInfo; + activeMinters++; + emit RankClaimed(_msgSender(), term, globalRank++); + } + + /** + * @dev ends minting upon maturity (and within permitted Withdrawal Time Window), gets minted XEN + */ + function claimMintReward() external { + MintInfo memory mintInfo = userMints[_msgSender()]; + require(mintInfo.rank > 0, "CRank: No mint exists"); + require(block.timestamp > mintInfo.maturityTs, "CRank: Mint maturity not reached"); + + // calculate reward and mint tokens + uint256 rewardAmount = _calculateMintReward( + mintInfo.rank, + mintInfo.term, + mintInfo.maturityTs, + mintInfo.amplifier, + mintInfo.eaaRate + ) * 1 ether; + _mint(_msgSender(), rewardAmount); + + _cleanUpStake(); + emit MintClaimed(_msgSender(), rewardAmount); + } + + /** + * @dev ends Rank Stake upon maturity (and within permitted Withdrawal time Window) + * mints XEN coins and splits them between User and designated other address + */ + function claimMintRewardAndShare(address other, uint256 pct) external { + MintInfo memory mintInfo = userMints[_msgSender()]; + require(other != address(0), "CRank: Cannot share with zero address"); + require(pct > 0, "CRank: Cannot share zero percent"); + require(pct < 101, "CRank: Cannot share 100+ percent"); + require(mintInfo.rank > 0, "CRank: No mint exists"); + require(block.timestamp > mintInfo.maturityTs, "CRank: Mint maturity not reached"); + + // calculate reward + uint256 rewardAmount = _calculateMintReward( + mintInfo.rank, + mintInfo.term, + mintInfo.maturityTs, + mintInfo.amplifier, + mintInfo.eaaRate + ) * 1 ether; + uint256 sharedReward = (rewardAmount * pct) / 100; + uint256 ownReward = rewardAmount - sharedReward; + + // mint reward tokens + _mint(_msgSender(), ownReward); + _mint(other, sharedReward); + + _cleanUpStake(); + emit MintClaimed(_msgSender(), rewardAmount); + } + + /** + * @dev ends Rank Stake upon maturity (and within permitted Withdrawal time Window) + * mints XEN coins and stakes 'pct' of it for 'term' + */ + function claimMintRewardAndStake(uint256 pct, uint256 term) external { + MintInfo memory mintInfo = userMints[_msgSender()]; + // require(pct > 0, "CRank: Cannot share zero percent"); + require(pct < 101, "CRank: Cannot share >100 percent"); + require(mintInfo.rank > 0, "CRank: No mint exists"); + require(block.timestamp > mintInfo.maturityTs, "CRank: Mint maturity not reached"); + + // calculate reward + uint256 rewardAmount = _calculateMintReward( + mintInfo.rank, + mintInfo.term, + mintInfo.maturityTs, + mintInfo.amplifier, + mintInfo.eaaRate + ) * 1 ether; + uint256 stakedReward = (rewardAmount * pct) / 100; + uint256 ownReward = rewardAmount - stakedReward; + + // mint reward tokens part + _mint(_msgSender(), ownReward); + _cleanUpStake(); + emit MintClaimed(_msgSender(), rewardAmount); + + // nothing to burn since we haven't minted this part yet + // stake extra tokens part + require(stakedReward > XEN_MIN_STAKE, "XEN: Below min stake"); + require(term * SECONDS_IN_DAY > MIN_TERM, "XEN: Below min stake term"); + require(term * SECONDS_IN_DAY < MAX_TERM_END + 1, "XEN: Above max stake term"); + require(userStakes[_msgSender()].amount == 0, "XEN: stake exists"); + + _createStake(stakedReward, term); + emit Staked(_msgSender(), stakedReward, term); + } + + /** + * @dev initiates XEN Stake in amount for a term (days) + */ + function stake(uint256 amount, uint256 term) external { + require(balanceOf(_msgSender()) >= amount, "XEN: not enough balance"); + require(amount > XEN_MIN_STAKE, "XEN: Below min stake"); + require(term * SECONDS_IN_DAY > MIN_TERM, "XEN: Below min stake term"); + require(term * SECONDS_IN_DAY < MAX_TERM_END + 1, "XEN: Above max stake term"); + require(userStakes[_msgSender()].amount == 0, "XEN: stake exists"); + + // burn staked XEN + _burn(_msgSender(), amount); + // create XEN Stake + _createStake(amount, term); + emit Staked(_msgSender(), amount, term); + } + + /** + * @dev ends XEN Stake and gets reward if the Stake is mature + */ + function withdraw() external { + StakeInfo memory userStake = userStakes[_msgSender()]; + require(userStake.amount > 0, "XEN: no stake exists"); + + uint256 xenReward = _calculateStakeReward( + userStake.amount, + userStake.term, + userStake.maturityTs, + userStake.apy + ); + activeStakes--; + totalXenStaked -= userStake.amount; + + // mint staked XEN (+ reward) + _mint(_msgSender(), userStake.amount + xenReward); + emit Withdrawn(_msgSender(), userStake.amount, xenReward); + delete userStakes[_msgSender()]; + } + + /** + * @dev burns XEN tokens and creates Proof-Of-Burn record to be used by connected DeFi services + */ + function burn(address user, uint256 amount) public { + require(amount > XEN_MIN_BURN, "Burn: Below min limit"); + require( + IERC165(_msgSender()).supportsInterface(type(IBurnRedeemable).interfaceId), + "Burn: not a supported contract" + ); + + _spendAllowance(user, _msgSender(), amount); + _burn(user, amount); + userBurns[user] += amount; + IBurnRedeemable(_msgSender()).onTokenBurned(user, amount); + } +} + + +// File contracts/test/XENCryptoRank25mm1.sol + +// Original license: SPDX_License_Identifier: MIT +pragma solidity ^0.8.10; + + + + + + + +contract XENCrypto25mm1 is + Context, + IRankedMintingToken, + IStakingToken, + IBurnableToken, + ERC20("XEN Crypto 25mm", "XEN25MM") +{ + using ABDKMath64x64 for int128; + using ABDKMath64x64 for uint256; + + // INTERNAL TYPE TO DESCRIBE A RANK STAKE + struct MintInfo { + address user; + uint256 term; + uint256 maturityTs; + uint256 rank; + uint256 amplifier; + uint256 eaaRate; + } + + // INTERNAL TYPE TO DESCRIBE A XEN STAKE + struct StakeInfo { + uint256 term; + uint256 maturityTs; + uint256 amount; + uint256 apy; + } + + // PUBLIC CONSTANTS + + uint256 public constant SECONDS_IN_DAY = 3_600 * 24; + uint256 public constant DAYS_IN_YEAR = 365; + + // N.B. modified original contract to test MaxTerm after Global Rank > 100_001 + uint256 public constant GENESIS_RANK = 25_000_001; + + uint256 public constant MIN_TERM = 1 * SECONDS_IN_DAY - 1; + uint256 public constant MAX_TERM_START = 100 * SECONDS_IN_DAY; + uint256 public constant MAX_TERM_END = 1_000 * SECONDS_IN_DAY; + uint256 public constant TERM_AMPLIFIER = 15; + uint256 public constant TERM_AMPLIFIER_THRESHOLD = 5_000; + uint256 public constant REWARD_AMPLIFIER_START = 3_000; + uint256 public constant REWARD_AMPLIFIER_END = 1; + uint256 public constant EAA_PM_START = 100; + uint256 public constant EAA_PM_STEP = 1; + uint256 public constant EAA_RANK_STEP = 100_000; + uint256 public constant WITHDRAWAL_WINDOW_DAYS = 7; + + uint256 public constant XEN_MIN_STAKE = 0; + + uint256 public constant XEN_MIN_BURN = 0; + + uint256 public constant XEN_APY_START = 20; + uint256 public constant XEN_APY_DAYS_STEP = 90; + uint256 public constant XEN_APY_END = 2; + + string public constant AUTHORS = "@MrJackLevin @lbelyaev faircrypto.org"; + + // PUBLIC STATE, READABLE VIA NAMESAKE GETTERS + + uint256 public immutable genesisTs; + uint256 public globalRank = GENESIS_RANK; + uint256 public activeMinters; + uint256 public activeStakes; + uint256 public totalXenStaked; + // user address => XEN mint info + mapping(address => MintInfo) public userMints; + // user address => XEN stake info + mapping(address => StakeInfo) public userStakes; + // user address => XEN burn amount + mapping(address => uint256) public userBurns; + + // CONSTRUCTOR + constructor() { + genesisTs = block.timestamp; + } + + function _min(uint256 a, uint256 b) private pure returns (uint256) { + if (a > b) return b; + return a; + } + + function _max(uint256 a, uint256 b) private pure returns (uint256) { + if (a > b) return a; + return b; + } + + // PRIVATE METHODS + + /** + * @dev calculates current MaxTerm based on GlobalTank + * (if GlobalTank crosses over TERM_AMPLIFIER_THRESHOLD) + */ + function _calculateMaxTerm() private view returns (uint256) { + if (globalRank > TERM_AMPLIFIER_THRESHOLD) { + uint256 delta = globalRank.fromUInt().log_2().mul(TERM_AMPLIFIER.fromUInt()).toUInt(); + uint256 newMax = MAX_TERM_START + delta * SECONDS_IN_DAY; + return _min(newMax, MAX_TERM_END); + } + return MAX_TERM_START; + } + + /** + * @dev calculates Withdrawal Penalty depending on lateness + */ + function _penalty(uint256 secsLate) private pure returns (uint256) { + // =MIN(2^(daysLate+3)/window-1,100) + uint256 daysLate = secsLate / SECONDS_IN_DAY; + if (daysLate > WITHDRAWAL_WINDOW_DAYS - 1) return 100; + uint256 penalty = (uint256(1) << (daysLate + 3)) / WITHDRAWAL_WINDOW_DAYS - 1; + return _min(penalty, 100); + } + + /** + * @dev calculates net Mint Reward (adjusted for Penalty) + */ + function _calculateMintReward( + uint256 cRank, + uint256 term, + uint256 maturityTs, + uint256 amplifier, + uint256 eeaRate + ) private view returns (uint256) { + uint256 secsLate = block.timestamp - maturityTs; + uint256 penalty = _penalty(secsLate); + uint256 rankDelta = _max(globalRank - cRank, 2); + uint256 EAA = (1_000 + eeaRate); + uint256 reward = getGrossReward(rankDelta, amplifier, term, EAA); + return (reward * (100 - penalty)) / 100; + } + + /** + * @dev cleans up Stake storage (gets some Gas credit;)) + */ + function _cleanUpStake() private { + delete userMints[_msgSender()]; + activeMinters--; + } + + /** + * @dev calculates XEN Stake Reward + */ + function _calculateStakeReward( + uint256 amount, + uint256 term, + uint256 maturityTs, + uint256 apy + ) private view returns (uint256) { + if (block.timestamp > maturityTs) { + uint256 rate = (apy * term * 1_000_000) / DAYS_IN_YEAR; + return (amount * rate) / 100_000_000; + } + return 0; + } + + /** + * @dev calculates Reward Amplifier + */ + function _calculateRewardAmplifier() private view returns (uint256) { + uint256 amplifierDecrease = (block.timestamp - genesisTs) / SECONDS_IN_DAY; + if (amplifierDecrease < REWARD_AMPLIFIER_START) { + return _max(REWARD_AMPLIFIER_START - amplifierDecrease, REWARD_AMPLIFIER_END); + } else { + return REWARD_AMPLIFIER_END; + } + } + + /** + * @dev calculates Early Adopter Amplifier Rate (in 1/000ths) + * actual EAA is (1_000 + EAAR) / 1_000 + */ + function _calculateEAARate() private view returns (uint256) { + uint256 decrease = (EAA_PM_STEP * globalRank) / EAA_RANK_STEP; + if (decrease > EAA_PM_START) return 0; + return EAA_PM_START - decrease; + } + + /** + * @dev calculates APY (in %) + */ + function _calculateAPY() private view returns (uint256) { + uint256 decrease = (block.timestamp - genesisTs) / (SECONDS_IN_DAY * XEN_APY_DAYS_STEP); + if (XEN_APY_START - XEN_APY_END < decrease) return XEN_APY_END; + return XEN_APY_START - decrease; + } + + /** + * @dev calculates Reward Amplifier + */ + function _createStake(uint256 amount, uint256 term) private { + userStakes[_msgSender()] = StakeInfo({ + term: term, + maturityTs: block.timestamp + term * SECONDS_IN_DAY, + amount: amount, + apy: _calculateAPY() + }); + activeStakes++; + totalXenStaked += amount; + } + + // PUBLIC CONVENIENCE GETTERS + + /** + * @dev calculates gross Mint Reward + */ + function getGrossReward( + uint256 rankDelta, + uint256 amplifier, + uint256 term, + uint256 eaa + ) public pure returns (uint256) { + int128 log128 = rankDelta.fromUInt().log_2(); + int128 reward128 = log128.mul(amplifier.fromUInt()).mul(term.fromUInt()).mul(eaa.fromUInt()); + return reward128.div(uint256(1_000).fromUInt()).toUInt(); + } + + /** + * @dev returns Rank Stake object associated with User account address + */ + function getUserMint() external view returns (MintInfo memory) { + return userMints[_msgSender()]; + } + + /** + * @dev returns XEN Stake object associated with User account address + */ + function getUserStake() external view returns (StakeInfo memory) { + return userStakes[_msgSender()]; + } + + /** + * @dev returns current AMP + */ + function getCurrentAMP() external view returns (uint256) { + return _calculateRewardAmplifier(); + } + + /** + * @dev returns current EAA Rate + */ + function getCurrentEAAR() external view returns (uint256) { + return _calculateEAARate(); + } + + /** + * @dev returns current APY + */ + function getCurrentAPY() external view returns (uint256) { + return _calculateAPY(); + } + + // PUBLIC STATE-CHANGING METHODS + + /** + * @dev accepts User Rank Stake provided all checks pass (incl. no current Stake) + */ + function claimRank(uint256 term) external { + uint256 termSec = term * SECONDS_IN_DAY; + require(termSec > MIN_TERM, "CRank: Term less than min"); + require(termSec < _calculateMaxTerm() + 1, "CRank: Term more than current max term"); + require(userMints[_msgSender()].rank == 0, "CRank: Mint already in progress"); + + // create and store new MintInfo + MintInfo memory mintInfo = MintInfo({ + user: _msgSender(), + term: term, + maturityTs: block.timestamp + termSec, + rank: globalRank, + amplifier: _calculateRewardAmplifier(), + eaaRate: _calculateEAARate() + }); + userMints[_msgSender()] = mintInfo; + activeMinters++; + emit RankClaimed(_msgSender(), term, globalRank++); + } + + /** + * @dev ends minting upon maturity (and within permitted Withdrawal Time Window), gets minted XEN + */ + function claimMintReward() external { + MintInfo memory mintInfo = userMints[_msgSender()]; + require(mintInfo.rank > 0, "CRank: No mint exists"); + require(block.timestamp > mintInfo.maturityTs, "CRank: Mint maturity not reached"); + + // calculate reward and mint tokens + uint256 rewardAmount = _calculateMintReward( + mintInfo.rank, + mintInfo.term, + mintInfo.maturityTs, + mintInfo.amplifier, + mintInfo.eaaRate + ) * 1 ether; + _mint(_msgSender(), rewardAmount); + + _cleanUpStake(); + emit MintClaimed(_msgSender(), rewardAmount); + } + + /** + * @dev ends Rank Stake upon maturity (and within permitted Withdrawal time Window) + * mints XEN coins and splits them between User and designated other address + */ + function claimMintRewardAndShare(address other, uint256 pct) external { + MintInfo memory mintInfo = userMints[_msgSender()]; + require(other != address(0), "CRank: Cannot share with zero address"); + require(pct > 0, "CRank: Cannot share zero percent"); + require(pct < 101, "CRank: Cannot share 100+ percent"); + require(mintInfo.rank > 0, "CRank: No mint exists"); + require(block.timestamp > mintInfo.maturityTs, "CRank: Mint maturity not reached"); + + // calculate reward + uint256 rewardAmount = _calculateMintReward( + mintInfo.rank, + mintInfo.term, + mintInfo.maturityTs, + mintInfo.amplifier, + mintInfo.eaaRate + ) * 1 ether; + uint256 sharedReward = (rewardAmount * pct) / 100; + uint256 ownReward = rewardAmount - sharedReward; + + // mint reward tokens + _mint(_msgSender(), ownReward); + _mint(other, sharedReward); + + _cleanUpStake(); + emit MintClaimed(_msgSender(), rewardAmount); + } + + /** + * @dev ends Rank Stake upon maturity (and within permitted Withdrawal time Window) + * mints XEN coins and stakes 'pct' of it for 'term' + */ + function claimMintRewardAndStake(uint256 pct, uint256 term) external { + MintInfo memory mintInfo = userMints[_msgSender()]; + // require(pct > 0, "CRank: Cannot share zero percent"); + require(pct < 101, "CRank: Cannot share >100 percent"); + require(mintInfo.rank > 0, "CRank: No mint exists"); + require(block.timestamp > mintInfo.maturityTs, "CRank: Mint maturity not reached"); + + // calculate reward + uint256 rewardAmount = _calculateMintReward( + mintInfo.rank, + mintInfo.term, + mintInfo.maturityTs, + mintInfo.amplifier, + mintInfo.eaaRate + ) * 1 ether; + uint256 stakedReward = (rewardAmount * pct) / 100; + uint256 ownReward = rewardAmount - stakedReward; + + // mint reward tokens part + _mint(_msgSender(), ownReward); + _cleanUpStake(); + emit MintClaimed(_msgSender(), rewardAmount); + + // nothing to burn since we haven't minted this part yet + // stake extra tokens part + require(stakedReward > XEN_MIN_STAKE, "XEN: Below min stake"); + require(term * SECONDS_IN_DAY > MIN_TERM, "XEN: Below min stake term"); + require(term * SECONDS_IN_DAY < MAX_TERM_END + 1, "XEN: Above max stake term"); + require(userStakes[_msgSender()].amount == 0, "XEN: stake exists"); + + _createStake(stakedReward, term); + emit Staked(_msgSender(), stakedReward, term); + } + + /** + * @dev initiates XEN Stake in amount for a term (days) + */ + function stake(uint256 amount, uint256 term) external { + require(balanceOf(_msgSender()) >= amount, "XEN: not enough balance"); + require(amount > XEN_MIN_STAKE, "XEN: Below min stake"); + require(term * SECONDS_IN_DAY > MIN_TERM, "XEN: Below min stake term"); + require(term * SECONDS_IN_DAY < MAX_TERM_END + 1, "XEN: Above max stake term"); + require(userStakes[_msgSender()].amount == 0, "XEN: stake exists"); + + // burn staked XEN + _burn(_msgSender(), amount); + // create XEN Stake + _createStake(amount, term); + emit Staked(_msgSender(), amount, term); + } + + /** + * @dev ends XEN Stake and gets reward if the Stake is mature + */ + function withdraw() external { + StakeInfo memory userStake = userStakes[_msgSender()]; + require(userStake.amount > 0, "XEN: no stake exists"); + + uint256 xenReward = _calculateStakeReward( + userStake.amount, + userStake.term, + userStake.maturityTs, + userStake.apy + ); + activeStakes--; + totalXenStaked -= userStake.amount; + + // mint staked XEN (+ reward) + _mint(_msgSender(), userStake.amount + xenReward); + emit Withdrawn(_msgSender(), userStake.amount, xenReward); + delete userStakes[_msgSender()]; + } + + /** + * @dev burns XEN tokens and creates Proof-Of-Burn record to be used by connected DeFi services + */ + function burn(address user, uint256 amount) public { + require(amount > XEN_MIN_BURN, "Burn: Below min limit"); + require( + IERC165(_msgSender()).supportsInterface(type(IBurnRedeemable).interfaceId), + "Burn: not a supported contract" + ); + + _spendAllowance(user, _msgSender(), amount); + _burn(user, amount); + userBurns[user] += amount; + IBurnRedeemable(_msgSender()).onTokenBurned(user, amount); + } +} + + +// File contracts/test/XENCryptoRank5001.sol + +// Original license: SPDX_License_Identifier: MIT +pragma solidity ^0.8.10; + + + + + + + +contract XENCrypto5001 is + Context, + IRankedMintingToken, + IStakingToken, + IBurnableToken, + ERC20("XEN Crypto 5001", "XEN5001") +{ + using ABDKMath64x64 for int128; + using ABDKMath64x64 for uint256; + + // INTERNAL TYPE TO DESCRIBE A RANK STAKE + struct MintInfo { + address user; + uint256 term; + uint256 maturityTs; + uint256 rank; + uint256 amplifier; + uint256 eaaRate; + } + + // INTERNAL TYPE TO DESCRIBE A XEN STAKE + struct StakeInfo { + uint256 term; + uint256 maturityTs; + uint256 amount; + uint256 apy; + } + + // PUBLIC CONSTANTS + + uint256 public constant SECONDS_IN_DAY = 3_600 * 24; + uint256 public constant DAYS_IN_YEAR = 365; + + // N.B. modified original contract to test MaxTerm after Global Rank > 5000 + uint256 public constant GENESIS_RANK = 5_001; + + uint256 public constant MIN_TERM = 1 * SECONDS_IN_DAY - 1; + uint256 public constant MAX_TERM_START = 100 * SECONDS_IN_DAY; + uint256 public constant MAX_TERM_END = 1_000 * SECONDS_IN_DAY; + uint256 public constant TERM_AMPLIFIER = 15; + uint256 public constant TERM_AMPLIFIER_THRESHOLD = 5_000; + uint256 public constant REWARD_AMPLIFIER_START = 3_000; + uint256 public constant REWARD_AMPLIFIER_END = 1; + uint256 public constant EAA_PM_START = 100; + uint256 public constant EAA_PM_STEP = 1; + uint256 public constant EAA_RANK_STEP = 100_000; + uint256 public constant WITHDRAWAL_WINDOW_DAYS = 7; + + uint256 public constant XEN_MIN_STAKE = 0; + + uint256 public constant XEN_MIN_BURN = 0; + + uint256 public constant XEN_APY_START = 20; + uint256 public constant XEN_APY_DAYS_STEP = 90; + uint256 public constant XEN_APY_END = 2; + + string public constant AUTHORS = "@MrJackLevin @lbelyaev faircrypto.org"; + + // PUBLIC STATE, READABLE VIA NAMESAKE GETTERS + + uint256 public immutable genesisTs; + uint256 public globalRank = GENESIS_RANK; + uint256 public activeMinters; + uint256 public activeStakes; + uint256 public totalXenStaked; + // user address => XEN mint info + mapping(address => MintInfo) public userMints; + // user address => XEN stake info + mapping(address => StakeInfo) public userStakes; + // user address => XEN burn amount + mapping(address => uint256) public userBurns; + + // CONSTRUCTOR + constructor() { + genesisTs = block.timestamp; + } + + function _min(uint256 a, uint256 b) private pure returns (uint256) { + if (a > b) return b; + return a; + } + + function _max(uint256 a, uint256 b) private pure returns (uint256) { + if (a > b) return a; + return b; + } + + // PRIVATE METHODS + + /** + * @dev calculates current MaxTerm based on GlobalTank + * (if GlobalTank crosses over TERM_AMPLIFIER_THRESHOLD) + */ + function _calculateMaxTerm() private view returns (uint256) { + if (globalRank > TERM_AMPLIFIER_THRESHOLD) { + uint256 delta = globalRank.fromUInt().log_2().mul(TERM_AMPLIFIER.fromUInt()).toUInt(); + uint256 newMax = MAX_TERM_START + delta * SECONDS_IN_DAY; + return _min(newMax, MAX_TERM_END); + } + return MAX_TERM_START; + } + + /** + * @dev calculates Withdrawal Penalty depending on lateness + */ + function _penalty(uint256 secsLate) private pure returns (uint256) { + // =MIN(2^(daysLate+3)/window-1,100) + uint256 daysLate = secsLate / SECONDS_IN_DAY; + if (daysLate > WITHDRAWAL_WINDOW_DAYS - 1) return 100; + uint256 penalty = (uint256(1) << (daysLate + 3)) / WITHDRAWAL_WINDOW_DAYS - 1; + return _min(penalty, 100); + } + + /** + * @dev calculates net Mint Reward (adjusted for Penalty) + */ + function _calculateMintReward( + uint256 cRank, + uint256 term, + uint256 maturityTs, + uint256 amplifier, + uint256 eeaRate + ) private view returns (uint256) { + uint256 secsLate = block.timestamp - maturityTs; + uint256 penalty = _penalty(secsLate); + uint256 rankDelta = _max(globalRank - cRank, 2); + uint256 EAA = (1_000 + eeaRate); + uint256 reward = getGrossReward(rankDelta, amplifier, term, EAA); + return (reward * (100 - penalty)) / 100; + } + + /** + * @dev cleans up Stake storage (gets some Gas credit;)) + */ + function _cleanUpStake() private { + delete userMints[_msgSender()]; + activeMinters--; + } + + /** + * @dev calculates XEN Stake Reward + */ + function _calculateStakeReward( + uint256 amount, + uint256 term, + uint256 maturityTs, + uint256 apy + ) private view returns (uint256) { + if (block.timestamp > maturityTs) { + uint256 rate = (apy * term * 1_000_000) / DAYS_IN_YEAR; + return (amount * rate) / 100_000_000; + } + return 0; + } + + /** + * @dev calculates Reward Amplifier + */ + function _calculateRewardAmplifier() private view returns (uint256) { + uint256 amplifierDecrease = (block.timestamp - genesisTs) / SECONDS_IN_DAY; + if (amplifierDecrease < REWARD_AMPLIFIER_START) { + return _max(REWARD_AMPLIFIER_START - amplifierDecrease, REWARD_AMPLIFIER_END); + } else { + return REWARD_AMPLIFIER_END; + } + } + + /** + * @dev calculates Early Adopter Amplifier Rate (in 1/000ths) + * actual EAA is (1_000 + EAAR) / 1_000 + */ + function _calculateEAARate() private view returns (uint256) { + uint256 decrease = (EAA_PM_STEP * globalRank) / EAA_RANK_STEP; + if (decrease > EAA_PM_START) return 0; + return EAA_PM_START - decrease; + } + + /** + * @dev calculates APY (in %) + */ + function _calculateAPY() private view returns (uint256) { + uint256 decrease = (block.timestamp - genesisTs) / (SECONDS_IN_DAY * XEN_APY_DAYS_STEP); + if (XEN_APY_START - XEN_APY_END < decrease) return XEN_APY_END; + return XEN_APY_START - decrease; + } + + /** + * @dev calculates Reward Amplifier + */ + function _createStake(uint256 amount, uint256 term) private { + userStakes[_msgSender()] = StakeInfo({ + term: term, + maturityTs: block.timestamp + term * SECONDS_IN_DAY, + amount: amount, + apy: _calculateAPY() + }); + activeStakes++; + totalXenStaked += amount; + } + + // PUBLIC CONVENIENCE GETTERS + + /** + * @dev calculates gross Mint Reward + */ + function getGrossReward( + uint256 rankDelta, + uint256 amplifier, + uint256 term, + uint256 eaa + ) public pure returns (uint256) { + int128 log128 = rankDelta.fromUInt().log_2(); + int128 reward128 = log128.mul(amplifier.fromUInt()).mul(term.fromUInt()).mul(eaa.fromUInt()); + return reward128.div(uint256(1_000).fromUInt()).toUInt(); + } + + /** + * @dev returns Rank Stake object associated with User account address + */ + function getUserMint() external view returns (MintInfo memory) { + return userMints[_msgSender()]; + } + + /** + * @dev returns XEN Stake object associated with User account address + */ + function getUserStake() external view returns (StakeInfo memory) { + return userStakes[_msgSender()]; + } + + /** + * @dev returns current AMP + */ + function getCurrentAMP() external view returns (uint256) { + return _calculateRewardAmplifier(); + } + + /** + * @dev returns current EAA Rate + */ + function getCurrentEAAR() external view returns (uint256) { + return _calculateEAARate(); + } + + /** + * @dev returns current APY + */ + function getCurrentAPY() external view returns (uint256) { + return _calculateAPY(); + } + + // PUBLIC STATE-CHANGING METHODS + + /** + * @dev accepts User Rank Stake provided all checks pass (incl. no current Stake) + */ + function claimRank(uint256 term) external { + uint256 termSec = term * SECONDS_IN_DAY; + require(termSec > MIN_TERM, "CRank: Term less than min"); + require(termSec < _calculateMaxTerm() + 1, "CRank: Term more than current max term"); + require(userMints[_msgSender()].rank == 0, "CRank: Mint already in progress"); + + // create and store new MintInfo + MintInfo memory mintInfo = MintInfo({ + user: _msgSender(), + term: term, + maturityTs: block.timestamp + termSec, + rank: globalRank, + amplifier: _calculateRewardAmplifier(), + eaaRate: _calculateEAARate() + }); + userMints[_msgSender()] = mintInfo; + activeMinters++; + emit RankClaimed(_msgSender(), term, globalRank++); + } + + /** + * @dev ends minting upon maturity (and within permitted Withdrawal Time Window), gets minted XEN + */ + function claimMintReward() external { + MintInfo memory mintInfo = userMints[_msgSender()]; + require(mintInfo.rank > 0, "CRank: No mint exists"); + require(block.timestamp > mintInfo.maturityTs, "CRank: Mint maturity not reached"); + + // calculate reward and mint tokens + uint256 rewardAmount = _calculateMintReward( + mintInfo.rank, + mintInfo.term, + mintInfo.maturityTs, + mintInfo.amplifier, + mintInfo.eaaRate + ) * 1 ether; + _mint(_msgSender(), rewardAmount); + + _cleanUpStake(); + emit MintClaimed(_msgSender(), rewardAmount); + } + + /** + * @dev ends Rank Stake upon maturity (and within permitted Withdrawal time Window) + * mints XEN coins and splits them between User and designated other address + */ + function claimMintRewardAndShare(address other, uint256 pct) external { + MintInfo memory mintInfo = userMints[_msgSender()]; + require(other != address(0), "CRank: Cannot share with zero address"); + require(pct > 0, "CRank: Cannot share zero percent"); + require(pct < 101, "CRank: Cannot share 100+ percent"); + require(mintInfo.rank > 0, "CRank: No mint exists"); + require(block.timestamp > mintInfo.maturityTs, "CRank: Mint maturity not reached"); + + // calculate reward + uint256 rewardAmount = _calculateMintReward( + mintInfo.rank, + mintInfo.term, + mintInfo.maturityTs, + mintInfo.amplifier, + mintInfo.eaaRate + ) * 1 ether; + uint256 sharedReward = (rewardAmount * pct) / 100; + uint256 ownReward = rewardAmount - sharedReward; + + // mint reward tokens + _mint(_msgSender(), ownReward); + _mint(other, sharedReward); + + _cleanUpStake(); + emit MintClaimed(_msgSender(), rewardAmount); + } + + /** + * @dev ends Rank Stake upon maturity (and within permitted Withdrawal time Window) + * mints XEN coins and stakes 'pct' of it for 'term' + */ + function claimMintRewardAndStake(uint256 pct, uint256 term) external { + MintInfo memory mintInfo = userMints[_msgSender()]; + // require(pct > 0, "CRank: Cannot share zero percent"); + require(pct < 101, "CRank: Cannot share >100 percent"); + require(mintInfo.rank > 0, "CRank: No mint exists"); + require(block.timestamp > mintInfo.maturityTs, "CRank: Mint maturity not reached"); + + // calculate reward + uint256 rewardAmount = _calculateMintReward( + mintInfo.rank, + mintInfo.term, + mintInfo.maturityTs, + mintInfo.amplifier, + mintInfo.eaaRate + ) * 1 ether; + uint256 stakedReward = (rewardAmount * pct) / 100; + uint256 ownReward = rewardAmount - stakedReward; + + // mint reward tokens part + _mint(_msgSender(), ownReward); + _cleanUpStake(); + emit MintClaimed(_msgSender(), rewardAmount); + + // nothing to burn since we haven't minted this part yet + // stake extra tokens part + require(stakedReward > XEN_MIN_STAKE, "XEN: Below min stake"); + require(term * SECONDS_IN_DAY > MIN_TERM, "XEN: Below min stake term"); + require(term * SECONDS_IN_DAY < MAX_TERM_END + 1, "XEN: Above max stake term"); + require(userStakes[_msgSender()].amount == 0, "XEN: stake exists"); + + _createStake(stakedReward, term); + emit Staked(_msgSender(), stakedReward, term); + } + + /** + * @dev initiates XEN Stake in amount for a term (days) + */ + function stake(uint256 amount, uint256 term) external { + require(balanceOf(_msgSender()) >= amount, "XEN: not enough balance"); + require(amount > XEN_MIN_STAKE, "XEN: Below min stake"); + require(term * SECONDS_IN_DAY > MIN_TERM, "XEN: Below min stake term"); + require(term * SECONDS_IN_DAY < MAX_TERM_END + 1, "XEN: Above max stake term"); + require(userStakes[_msgSender()].amount == 0, "XEN: stake exists"); + + // burn staked XEN + _burn(_msgSender(), amount); + // create XEN Stake + _createStake(amount, term); + emit Staked(_msgSender(), amount, term); + } + + /** + * @dev ends XEN Stake and gets reward if the Stake is mature + */ + function withdraw() external { + StakeInfo memory userStake = userStakes[_msgSender()]; + require(userStake.amount > 0, "XEN: no stake exists"); + + uint256 xenReward = _calculateStakeReward( + userStake.amount, + userStake.term, + userStake.maturityTs, + userStake.apy + ); + activeStakes--; + totalXenStaked -= userStake.amount; + + // mint staked XEN (+ reward) + _mint(_msgSender(), userStake.amount + xenReward); + emit Withdrawn(_msgSender(), userStake.amount, xenReward); + delete userStakes[_msgSender()]; + } + + /** + * @dev burns XEN tokens and creates Proof-Of-Burn record to be used by connected DeFi services + */ + function burn(address user, uint256 amount) public { + require(amount > XEN_MIN_BURN, "Burn: Below min limit"); + require( + IERC165(_msgSender()).supportsInterface(type(IBurnRedeemable).interfaceId), + "Burn: not a supported contract" + ); + + _spendAllowance(user, _msgSender(), amount); + _burn(user, amount); + userBurns[user] += amount; + IBurnRedeemable(_msgSender()).onTokenBurned(user, amount); + } +} + + +// File contracts/XENMath.sol + +// Original license: SPDX_License_Identifier: MIT +pragma solidity ^0.8.10; + +library XENMath { + function min(uint256 a, uint256 b) external pure returns (uint256) { + if (a > b) return b; + return a; + } + + function max(uint256 a, uint256 b) external pure returns (uint256) { + if (a > b) return a; + return b; + } + + function logX64(uint256 x) external pure returns (int128) { + return ABDKMath64x64.log_2(ABDKMath64x64.fromUInt(x)); + } +} + + +// File contracts/XENCrypto.sol + +// Original license: SPDX_License_Identifier: MIT +pragma solidity ^0.8.10; + + + + + + + + +contract XENCrypto is Context, IRankedMintingToken, IStakingToken, IBurnableToken, ERC20("XEN Crypto", "XEN") { + using XENMath for uint256; + using ABDKMath64x64 for int128; + using ABDKMath64x64 for uint256; + + // INTERNAL TYPE TO DESCRIBE A XEN MINT INFO + struct MintInfo { + address user; + uint256 term; + uint256 maturityTs; + uint256 rank; + uint256 amplifier; + uint256 eaaRate; + } + + // INTERNAL TYPE TO DESCRIBE A XEN STAKE + struct StakeInfo { + uint256 term; + uint256 maturityTs; + uint256 amount; + uint256 apy; + } + + // PUBLIC CONSTANTS + + uint256 public constant SECONDS_IN_DAY = 3_600 * 24; + uint256 public constant DAYS_IN_YEAR = 365; + + uint256 public constant GENESIS_RANK = 1; + + uint256 public constant MIN_TERM = 1 * SECONDS_IN_DAY - 1; + uint256 public constant MAX_TERM_START = 100 * SECONDS_IN_DAY; + uint256 public constant MAX_TERM_END = 1_000 * SECONDS_IN_DAY; + uint256 public constant TERM_AMPLIFIER = 15; + uint256 public constant TERM_AMPLIFIER_THRESHOLD = 5_000; + uint256 public constant REWARD_AMPLIFIER_START = 3_000; + uint256 public constant REWARD_AMPLIFIER_END = 1; + uint256 public constant EAA_PM_START = 100; + uint256 public constant EAA_PM_STEP = 1; + uint256 public constant EAA_RANK_STEP = 100_000; + uint256 public constant WITHDRAWAL_WINDOW_DAYS = 7; + uint256 public constant MAX_PENALTY_PCT = 99; + + uint256 public constant XEN_MIN_STAKE = 0; + + uint256 public constant XEN_MIN_BURN = 0; + + uint256 public constant XEN_APY_START = 20; + uint256 public constant XEN_APY_DAYS_STEP = 90; + uint256 public constant XEN_APY_END = 2; + + string public constant AUTHORS = "@MrJackLevin @lbelyaev faircrypto.org"; + + // PUBLIC STATE, READABLE VIA NAMESAKE GETTERS + + uint256 public immutable genesisTs; + uint256 public globalRank = GENESIS_RANK; + uint256 public activeMinters; + uint256 public activeStakes; + uint256 public totalXenStaked; + // user address => XEN mint info + mapping(address => MintInfo) public userMints; + // user address => XEN stake info + mapping(address => StakeInfo) public userStakes; + // user address => XEN burn amount + mapping(address => uint256) public userBurns; + + // CONSTRUCTOR + constructor() { + genesisTs = block.timestamp; + } + + // PRIVATE METHODS + + /** + * @dev calculates current MaxTerm based on Global Rank + * (if Global Rank crosses over TERM_AMPLIFIER_THRESHOLD) + */ + function _calculateMaxTerm() private view returns (uint256) { + if (globalRank > TERM_AMPLIFIER_THRESHOLD) { + uint256 delta = globalRank.fromUInt().log_2().mul(TERM_AMPLIFIER.fromUInt()).toUInt(); + uint256 newMax = MAX_TERM_START + delta * SECONDS_IN_DAY; + return XENMath.min(newMax, MAX_TERM_END); + } + return MAX_TERM_START; + } + + /** + * @dev calculates Withdrawal Penalty depending on lateness + */ + function _penalty(uint256 secsLate) private pure returns (uint256) { + // =MIN(2^(daysLate+3)/window-1,99) + uint256 daysLate = secsLate / SECONDS_IN_DAY; + if (daysLate > WITHDRAWAL_WINDOW_DAYS - 1) return MAX_PENALTY_PCT; + uint256 penalty = (uint256(1) << (daysLate + 3)) / WITHDRAWAL_WINDOW_DAYS - 1; + return XENMath.min(penalty, MAX_PENALTY_PCT); + } + + /** + * @dev calculates net Mint Reward (adjusted for Penalty) + */ + function _calculateMintReward( + uint256 cRank, + uint256 term, + uint256 maturityTs, + uint256 amplifier, + uint256 eeaRate + ) private view returns (uint256) { + uint256 secsLate = block.timestamp - maturityTs; + uint256 penalty = _penalty(secsLate); + uint256 rankDelta = XENMath.max(globalRank - cRank, 2); + uint256 EAA = (1_000 + eeaRate); + uint256 reward = getGrossReward(rankDelta, amplifier, term, EAA); + return (reward * (100 - penalty)) / 100; + } + + /** + * @dev cleans up User Mint storage (gets some Gas credit;)) + */ + function _cleanUpUserMint() private { + delete userMints[_msgSender()]; + activeMinters--; + } + + /** + * @dev calculates XEN Stake Reward + */ + function _calculateStakeReward( + uint256 amount, + uint256 term, + uint256 maturityTs, + uint256 apy + ) private view returns (uint256) { + if (block.timestamp > maturityTs) { + uint256 rate = (apy * term * 1_000_000) / DAYS_IN_YEAR; + return (amount * rate) / 100_000_000; + } + return 0; + } + + /** + * @dev calculates Reward Amplifier + */ + function _calculateRewardAmplifier() private view returns (uint256) { + uint256 amplifierDecrease = (block.timestamp - genesisTs) / SECONDS_IN_DAY; + if (amplifierDecrease < REWARD_AMPLIFIER_START) { + return XENMath.max(REWARD_AMPLIFIER_START - amplifierDecrease, REWARD_AMPLIFIER_END); + } else { + return REWARD_AMPLIFIER_END; + } + } + + /** + * @dev calculates Early Adopter Amplifier Rate (in 1/000ths) + * actual EAA is (1_000 + EAAR) / 1_000 + */ + function _calculateEAARate() private view returns (uint256) { + uint256 decrease = (EAA_PM_STEP * globalRank) / EAA_RANK_STEP; + if (decrease > EAA_PM_START) return 0; + return EAA_PM_START - decrease; + } + + /** + * @dev calculates APY (in %) + */ + function _calculateAPY() private view returns (uint256) { + uint256 decrease = (block.timestamp - genesisTs) / (SECONDS_IN_DAY * XEN_APY_DAYS_STEP); + if (XEN_APY_START - XEN_APY_END < decrease) return XEN_APY_END; + return XEN_APY_START - decrease; + } + + /** + * @dev creates User Stake + */ + function _createStake(uint256 amount, uint256 term) private { + userStakes[_msgSender()] = StakeInfo({ + term: term, + maturityTs: block.timestamp + term * SECONDS_IN_DAY, + amount: amount, + apy: _calculateAPY() + }); + activeStakes++; + totalXenStaked += amount; + } + + // PUBLIC CONVENIENCE GETTERS + + /** + * @dev calculates gross Mint Reward + */ + function getGrossReward( + uint256 rankDelta, + uint256 amplifier, + uint256 term, + uint256 eaa + ) public pure returns (uint256) { + int128 log128 = rankDelta.fromUInt().log_2(); + int128 reward128 = log128.mul(amplifier.fromUInt()).mul(term.fromUInt()).mul(eaa.fromUInt()); + return reward128.div(uint256(1_000).fromUInt()).toUInt(); + } + + /** + * @dev returns User Mint object associated with User account address + */ + function getUserMint() external view returns (MintInfo memory) { + return userMints[_msgSender()]; + } + + /** + * @dev returns XEN Stake object associated with User account address + */ + function getUserStake() external view returns (StakeInfo memory) { + return userStakes[_msgSender()]; + } + + /** + * @dev returns current AMP + */ + function getCurrentAMP() external view returns (uint256) { + return _calculateRewardAmplifier(); + } + + /** + * @dev returns current EAA Rate + */ + function getCurrentEAAR() external view returns (uint256) { + return _calculateEAARate(); + } + + /** + * @dev returns current APY + */ + function getCurrentAPY() external view returns (uint256) { + return _calculateAPY(); + } + + /** + * @dev returns current MaxTerm + */ + function getCurrentMaxTerm() external view returns (uint256) { + return _calculateMaxTerm(); + } + + // PUBLIC STATE-CHANGING METHODS + + /** + * @dev accepts User cRank claim provided all checks pass (incl. no current claim exists) + */ + function claimRank(uint256 term) external { + uint256 termSec = term * SECONDS_IN_DAY; + require(termSec > MIN_TERM, "CRank: Term less than min"); + require(termSec < _calculateMaxTerm() + 1, "CRank: Term more than current max term"); + require(userMints[_msgSender()].rank == 0, "CRank: Mint already in progress"); + + // create and store new MintInfo + MintInfo memory mintInfo = MintInfo({ + user: _msgSender(), + term: term, + maturityTs: block.timestamp + termSec, + rank: globalRank, + amplifier: _calculateRewardAmplifier(), + eaaRate: _calculateEAARate() + }); + userMints[_msgSender()] = mintInfo; + activeMinters++; + emit RankClaimed(_msgSender(), term, globalRank++); + } + + /** + * @dev ends minting upon maturity (and within permitted Withdrawal Time Window), gets minted XEN + */ + function claimMintReward() external { + MintInfo memory mintInfo = userMints[_msgSender()]; + require(mintInfo.rank > 0, "CRank: No mint exists"); + require(block.timestamp > mintInfo.maturityTs, "CRank: Mint maturity not reached"); + + // calculate reward and mint tokens + uint256 rewardAmount = _calculateMintReward( + mintInfo.rank, + mintInfo.term, + mintInfo.maturityTs, + mintInfo.amplifier, + mintInfo.eaaRate + ) * 1 ether; + _mint(_msgSender(), rewardAmount); + + _cleanUpUserMint(); + emit MintClaimed(_msgSender(), rewardAmount); + } + + /** + * @dev ends minting upon maturity (and within permitted Withdrawal time Window) + * mints XEN coins and splits them between User and designated other address + */ + function claimMintRewardAndShare(address other, uint256 pct) external { + MintInfo memory mintInfo = userMints[_msgSender()]; + require(other != address(0), "CRank: Cannot share with zero address"); + require(pct > 0, "CRank: Cannot share zero percent"); + require(pct < 101, "CRank: Cannot share 100+ percent"); + require(mintInfo.rank > 0, "CRank: No mint exists"); + require(block.timestamp > mintInfo.maturityTs, "CRank: Mint maturity not reached"); + + // calculate reward + uint256 rewardAmount = _calculateMintReward( + mintInfo.rank, + mintInfo.term, + mintInfo.maturityTs, + mintInfo.amplifier, + mintInfo.eaaRate + ) * 1 ether; + uint256 sharedReward = (rewardAmount * pct) / 100; + uint256 ownReward = rewardAmount - sharedReward; + + // mint reward tokens + _mint(_msgSender(), ownReward); + _mint(other, sharedReward); + + _cleanUpUserMint(); + emit MintClaimed(_msgSender(), rewardAmount); + } + + /** + * @dev ends minting upon maturity (and within permitted Withdrawal time Window) + * mints XEN coins and stakes 'pct' of it for 'term' + */ + function claimMintRewardAndStake(uint256 pct, uint256 term) external { + MintInfo memory mintInfo = userMints[_msgSender()]; + // require(pct > 0, "CRank: Cannot share zero percent"); + require(pct < 101, "CRank: Cannot share >100 percent"); + require(mintInfo.rank > 0, "CRank: No mint exists"); + require(block.timestamp > mintInfo.maturityTs, "CRank: Mint maturity not reached"); + + // calculate reward + uint256 rewardAmount = _calculateMintReward( + mintInfo.rank, + mintInfo.term, + mintInfo.maturityTs, + mintInfo.amplifier, + mintInfo.eaaRate + ) * 1 ether; + uint256 stakedReward = (rewardAmount * pct) / 100; + uint256 ownReward = rewardAmount - stakedReward; + + // mint reward tokens part + _mint(_msgSender(), ownReward); + _cleanUpUserMint(); + emit MintClaimed(_msgSender(), rewardAmount); + + // nothing to burn since we haven't minted this part yet + // stake extra tokens part + require(stakedReward > XEN_MIN_STAKE, "XEN: Below min stake"); + require(term * SECONDS_IN_DAY > MIN_TERM, "XEN: Below min stake term"); + require(term * SECONDS_IN_DAY < MAX_TERM_END + 1, "XEN: Above max stake term"); + require(userStakes[_msgSender()].amount == 0, "XEN: stake exists"); + + _createStake(stakedReward, term); + emit Staked(_msgSender(), stakedReward, term); + } + + /** + * @dev initiates XEN Stake in amount for a term (days) + */ + function stake(uint256 amount, uint256 term) external { + require(balanceOf(_msgSender()) >= amount, "XEN: not enough balance"); + require(amount > XEN_MIN_STAKE, "XEN: Below min stake"); + require(term * SECONDS_IN_DAY > MIN_TERM, "XEN: Below min stake term"); + require(term * SECONDS_IN_DAY < MAX_TERM_END + 1, "XEN: Above max stake term"); + require(userStakes[_msgSender()].amount == 0, "XEN: stake exists"); + + // burn staked XEN + _burn(_msgSender(), amount); + // create XEN Stake + _createStake(amount, term); + emit Staked(_msgSender(), amount, term); + } + + /** + * @dev ends XEN Stake and gets reward if the Stake is mature + */ + function withdraw() external { + StakeInfo memory userStake = userStakes[_msgSender()]; + require(userStake.amount > 0, "XEN: no stake exists"); + + uint256 xenReward = _calculateStakeReward( + userStake.amount, + userStake.term, + userStake.maturityTs, + userStake.apy + ); + activeStakes--; + totalXenStaked -= userStake.amount; + + // mint staked XEN (+ reward) + _mint(_msgSender(), userStake.amount + xenReward); + emit Withdrawn(_msgSender(), userStake.amount, xenReward); + delete userStakes[_msgSender()]; + } + + /** + * @dev burns XEN tokens and creates Proof-Of-Burn record to be used by connected DeFi services + */ + function burn(address user, uint256 amount) public { + require(amount > XEN_MIN_BURN, "Burn: Below min limit"); + require( + IERC165(_msgSender()).supportsInterface(type(IBurnRedeemable).interfaceId), + "Burn: not a supported contract" + ); + + _spendAllowance(user, _msgSender(), amount); + _burn(user, amount); + userBurns[user] += amount; + IBurnRedeemable(_msgSender()).onTokenBurned(user, amount); + } +} \ No newline at end of file diff --git a/deno.json b/deno.json index 98ffd41..9f70f16 100644 --- a/deno.json +++ b/deno.json @@ -18,6 +18,7 @@ "imports": { "@openzeppelin/contracts": "npm:@openzeppelin/contracts@^5.1.0", "@openzeppelin/contracts-upgradeable": "npm:@openzeppelin/contracts-upgradeable@^5.1.0", + "@polkadot/api": "npm:@polkadot/api@^16.5.4", "@std/cli": "jsr:@std/cli@^1.0.24", "@std/collections": "jsr:@std/collections@^1.1.3", "@std/fs": "jsr:@std/fs@^1.0.20", diff --git a/deno.lock b/deno.lock index ccaf307..03a1b0e 100644 --- a/deno.lock +++ b/deno.lock @@ -1,56 +1,44 @@ { "version": "5", "specifiers": { - "jsr:@std/cli@^1.0.24": "1.0.24", + "jsr:@std/cli@^1.0.24": "1.0.26", "jsr:@std/collections@^1.1.3": "1.1.3", - "jsr:@std/fmt@^1.0.5": "1.0.8", - "jsr:@std/fs@*": "1.0.19", - "jsr:@std/fs@^1.0.11": "1.0.20", - "jsr:@std/fs@^1.0.20": "1.0.20", + "jsr:@std/fmt@^1.0.5": "1.0.9", + "jsr:@std/fs@^1.0.11": "1.0.22", + "jsr:@std/fs@^1.0.20": "1.0.22", "jsr:@std/internal@^1.0.12": "1.0.12", - "jsr:@std/internal@^1.0.9": "1.0.12", "jsr:@std/io@~0.225.2": "0.225.2", "jsr:@std/log@~0.224.14": "0.224.14", - "jsr:@std/path@*": "1.1.3", - "jsr:@std/path@^1.1.1": "1.1.3", - "jsr:@std/path@^1.1.3": "1.1.3", + "jsr:@std/path@^1.1.3": "1.1.4", + "jsr:@std/path@^1.1.4": "1.1.4", "npm:@openzeppelin/contracts-upgradeable@^5.1.0": "5.4.0_@openzeppelin+contracts@5.4.0", "npm:@openzeppelin/contracts@^5.1.0": "5.4.0", - "npm:cli-table3@*": "0.6.5", + "npm:@polkadot/api@^16.5.4": "16.5.4_@polkadot+util@14.0.1_@polkadot+util-crypto@14.0.1__@polkadot+util@14.0.1__@polkadot+x-randomvalues@14.0.1___@polkadot+util@14.0.1___@polkadot+wasm-util@7.5.4____@polkadot+util@14.0.1__@polkadot+wasm-util@7.5.4___@polkadot+util@14.0.1", "npm:cli-table3@~0.6.5": "0.6.5", - "npm:ink@^6.5.1": "6.5.1_react@19.2.3", - "npm:react@^19.2.3": "19.2.3", - "npm:solc@~0.8.31": "0.8.31", - "npm:tablemark@*": "4.1.0", + "npm:ink@^6.5.1": "6.6.0_react@19.2.4", + "npm:react@^19.2.3": "19.2.4", + "npm:solc@~0.8.31": "0.8.33", "npm:tablemark@^4.1.0": "4.1.0", - "npm:viem@*": "2.43.1_ws@8.18.3", - "npm:viem@^2.43.1": "2.43.1_ws@8.18.3" + "npm:viem@^2.43.1": "2.45.0_ws@8.18.3" }, "jsr": { - "@std/cli@1.0.24": { - "integrity": "b655a5beb26aa94f98add6bc8889f5fb9bc3ee2cc3fc954e151201f4c4200a5e", + "@std/cli@1.0.26": { + "integrity": "53679a2b041406f15c3ff193986852595f3d11ba7b9d4b0c71a42589662e8fb5", "dependencies": [ - "jsr:@std/internal@^1.0.12" + "jsr:@std/internal" ] }, "@std/collections@1.1.3": { "integrity": "bf8b0818886df6a32b64c7d3b037a425111f28278d69fd0995aeb62777c986b0" }, - "@std/fmt@1.0.8": { - "integrity": "71e1fc498787e4434d213647a6e43e794af4fd393ef8f52062246e06f7e372b7" + "@std/fmt@1.0.9": { + "integrity": "2487343e8899fb2be5d0e3d35013e54477ada198854e52dd05ed0422eddcabe0" }, - "@std/fs@1.0.19": { - "integrity": "051968c2b1eae4d2ea9f79a08a3845740ef6af10356aff43d3e2ef11ed09fb06", + "@std/fs@1.0.22": { + "integrity": "de0f277a58a867147a8a01bc1b181d0dfa80bfddba8c9cf2bacd6747bcec9308", "dependencies": [ - "jsr:@std/internal@^1.0.9", - "jsr:@std/path@^1.1.1" - ] - }, - "@std/fs@1.0.20": { - "integrity": "e953206aae48d46ee65e8783ded459f23bec7dd1f3879512911c35e5484ea187", - "dependencies": [ - "jsr:@std/internal@^1.0.12", - "jsr:@std/path@^1.1.3" + "jsr:@std/internal", + "jsr:@std/path@^1.1.4" ] }, "@std/internal@1.0.12": { @@ -67,10 +55,10 @@ "jsr:@std/io" ] }, - "@std/path@1.1.3": { - "integrity": "b015962d82a5e6daea980c32b82d2c40142149639968549c649031a230b1afb3", + "@std/path@1.1.4": { + "integrity": "1d2d43f39efb1b42f0b1882a25486647cb851481862dc7313390b2bb044314b5", "dependencies": [ - "jsr:@std/internal@^1.0.12" + "jsr:@std/internal" ] } }, @@ -78,8 +66,8 @@ "@adraffy/ens-normalize@1.11.1": { "integrity": "sha512-nhCBV3quEgesuf7c7KYfperqSS14T8bYuvJ8PcLJp6znkZpFc0AuW4qBtr8eKVyPPe/8RSr7sglCWPU5eaxwKQ==" }, - "@alcalzone/ansi-tokenize@0.2.2": { - "integrity": "sha512-mkOh+Wwawzuf5wa30bvc4nA+Qb6DIrGWgBhRR/Pw4T9nsgYait8izvXkNyU78D6Wcu3Z+KUdwCmLCxlWjEotYA==", + "@alcalzone/ansi-tokenize@0.2.3": { + "integrity": "sha512-jsElTJ0sQ4wHRz+C45tfect76BwbTbgkgKByOzpCN9xG61N5V6u/glvg1CsNJhq2xJIFpKHSwG3D2wPPuEYOrQ==", "dependencies": [ "ansi-styles", "is-fullwidth-code-point@5.1.0" @@ -97,6 +85,12 @@ "@noble/hashes" ] }, + "@noble/curves@1.9.7": { + "integrity": "sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==", + "dependencies": [ + "@noble/hashes" + ] + }, "@noble/hashes@1.8.0": { "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==" }, @@ -109,13 +103,396 @@ "@openzeppelin/contracts@5.4.0": { "integrity": "sha512-eCYgWnLg6WO+X52I16TZt8uEjbtdkgLC0SUX/xnAksjjrQI4Xfn4iBRoI5j55dmlOhDv1Y7BoR3cU7e3WWhC6A==" }, + "@polkadot-api/json-rpc-provider-proxy@0.1.0": { + "integrity": "sha512-8GSFE5+EF73MCuLQm8tjrbCqlgclcHBSRaswvXziJ0ZW7iw3UEMsKkkKvELayWyBuOPa2T5i1nj6gFOeIsqvrg==" + }, + "@polkadot-api/json-rpc-provider@0.0.1": { + "integrity": "sha512-/SMC/l7foRjpykLTUTacIH05H3mr9ip8b5xxfwXlVezXrNVLp3Cv0GX6uItkKd+ZjzVPf3PFrDF2B2/HLSNESA==" + }, + "@polkadot-api/metadata-builders@0.3.2": { + "integrity": "sha512-TKpfoT6vTb+513KDzMBTfCb/ORdgRnsS3TDFpOhAhZ08ikvK+hjHMt5plPiAX/OWkm1Wc9I3+K6W0hX5Ab7MVg==", + "dependencies": [ + "@polkadot-api/substrate-bindings", + "@polkadot-api/utils" + ] + }, + "@polkadot-api/observable-client@0.3.2_@polkadot-api+substrate-client@0.1.4_rxjs@7.8.2": { + "integrity": "sha512-HGgqWgEutVyOBXoGOPp4+IAq6CNdK/3MfQJmhCJb8YaJiaK4W6aRGrdQuQSTPHfERHCARt9BrOmEvTXAT257Ug==", + "dependencies": [ + "@polkadot-api/metadata-builders", + "@polkadot-api/substrate-bindings", + "@polkadot-api/substrate-client", + "@polkadot-api/utils", + "rxjs" + ] + }, + "@polkadot-api/substrate-bindings@0.6.0": { + "integrity": "sha512-lGuhE74NA1/PqdN7fKFdE5C1gNYX357j1tWzdlPXI0kQ7h3kN0zfxNOpPUN7dIrPcOFZ6C0tRRVrBylXkI6xPw==", + "dependencies": [ + "@noble/hashes", + "@polkadot-api/utils", + "@scure/base", + "scale-ts" + ] + }, + "@polkadot-api/substrate-client@0.1.4": { + "integrity": "sha512-MljrPobN0ZWTpn++da9vOvt+Ex+NlqTlr/XT7zi9sqPtDJiQcYl+d29hFAgpaeTqbeQKZwz3WDE9xcEfLE8c5A==", + "dependencies": [ + "@polkadot-api/json-rpc-provider", + "@polkadot-api/utils" + ] + }, + "@polkadot-api/utils@0.1.0": { + "integrity": "sha512-MXzWZeuGxKizPx2Xf/47wx9sr/uxKw39bVJUptTJdsaQn/TGq+z310mHzf1RCGvC1diHM8f593KrnDgc9oNbJA==" + }, + "@polkadot/api-augment@16.5.4": { + "integrity": "sha512-9FTohz13ih458V2JBFjRACKHPqfM6j4bmmTbcSaE7hXcIOYzm4ABFo7xq5osLyvItganjsICErL2vRn2zULycw==", + "dependencies": [ + "@polkadot/api-base", + "@polkadot/rpc-augment", + "@polkadot/types@16.5.4_@polkadot+util@14.0.1_@polkadot+util-crypto@14.0.1__@polkadot+util@14.0.1__@polkadot+x-randomvalues@14.0.1___@polkadot+util@14.0.1___@polkadot+wasm-util@7.5.4____@polkadot+util@14.0.1__@polkadot+wasm-util@7.5.4___@polkadot+util@14.0.1", + "@polkadot/types-augment@16.5.4", + "@polkadot/types-codec", + "@polkadot/util", + "tslib" + ] + }, + "@polkadot/api-base@16.5.4": { + "integrity": "sha512-V69v3ieg5+91yRUCG1vFRSLr7V7MvHPvo/QrzleIUu8tPXWldJ0kyXbWKHVNZEpVBA9LpjGvII+MHUW7EaKMNg==", + "dependencies": [ + "@polkadot/rpc-core", + "@polkadot/types@16.5.4_@polkadot+util@14.0.1_@polkadot+util-crypto@14.0.1__@polkadot+util@14.0.1__@polkadot+x-randomvalues@14.0.1___@polkadot+util@14.0.1___@polkadot+wasm-util@7.5.4____@polkadot+util@14.0.1__@polkadot+wasm-util@7.5.4___@polkadot+util@14.0.1", + "@polkadot/util", + "rxjs", + "tslib" + ] + }, + "@polkadot/api-derive@16.5.4_@polkadot+util@14.0.1_@polkadot+util-crypto@14.0.1__@polkadot+util@14.0.1__@polkadot+x-randomvalues@14.0.1___@polkadot+util@14.0.1___@polkadot+wasm-util@7.5.4____@polkadot+util@14.0.1__@polkadot+wasm-util@7.5.4___@polkadot+util@14.0.1": { + "integrity": "sha512-0JP2a6CaqTviacHsmnUKF4VLRsKdYOzQCqdL9JpwY/QBz/ZLqIKKPiSRg285EVLf8n/hWdTfxbWqQCsRa5NL+Q==", + "dependencies": [ + "@polkadot/api", + "@polkadot/api-augment", + "@polkadot/api-base", + "@polkadot/rpc-core", + "@polkadot/types@16.5.4_@polkadot+util@14.0.1_@polkadot+util-crypto@14.0.1__@polkadot+util@14.0.1__@polkadot+x-randomvalues@14.0.1___@polkadot+util@14.0.1___@polkadot+wasm-util@7.5.4____@polkadot+util@14.0.1__@polkadot+wasm-util@7.5.4___@polkadot+util@14.0.1", + "@polkadot/types-codec", + "@polkadot/util", + "@polkadot/util-crypto@14.0.1_@polkadot+util@14.0.1_@polkadot+x-randomvalues@14.0.1__@polkadot+util@14.0.1__@polkadot+wasm-util@7.5.4___@polkadot+util@14.0.1_@polkadot+wasm-util@7.5.4__@polkadot+util@14.0.1", + "rxjs", + "tslib" + ] + }, + "@polkadot/api@16.5.4_@polkadot+util@14.0.1_@polkadot+util-crypto@14.0.1__@polkadot+util@14.0.1__@polkadot+x-randomvalues@14.0.1___@polkadot+util@14.0.1___@polkadot+wasm-util@7.5.4____@polkadot+util@14.0.1__@polkadot+wasm-util@7.5.4___@polkadot+util@14.0.1": { + "integrity": "sha512-mX1fwtXCBAHXEyZLSnSrMDGP+jfU2rr7GfDVQBz0cBY1nmY8N34RqPWGrZWj8o4DxVu1DQ91sGncOmlBwEl0Qg==", + "dependencies": [ + "@polkadot/api-augment", + "@polkadot/api-base", + "@polkadot/api-derive", + "@polkadot/keyring@14.0.1_@polkadot+util@14.0.1_@polkadot+util-crypto@14.0.1__@polkadot+util@14.0.1__@polkadot+x-randomvalues@14.0.1___@polkadot+util@14.0.1___@polkadot+wasm-util@7.5.4____@polkadot+util@14.0.1__@polkadot+wasm-util@7.5.4___@polkadot+util@14.0.1", + "@polkadot/rpc-augment", + "@polkadot/rpc-core", + "@polkadot/rpc-provider", + "@polkadot/types@16.5.4_@polkadot+util@14.0.1_@polkadot+util-crypto@14.0.1__@polkadot+util@14.0.1__@polkadot+x-randomvalues@14.0.1___@polkadot+util@14.0.1___@polkadot+wasm-util@7.5.4____@polkadot+util@14.0.1__@polkadot+wasm-util@7.5.4___@polkadot+util@14.0.1", + "@polkadot/types-augment@16.5.4", + "@polkadot/types-codec", + "@polkadot/types-create", + "@polkadot/types-known", + "@polkadot/util", + "@polkadot/util-crypto@14.0.1_@polkadot+util@14.0.1_@polkadot+x-randomvalues@14.0.1__@polkadot+util@14.0.1__@polkadot+wasm-util@7.5.4___@polkadot+util@14.0.1_@polkadot+wasm-util@7.5.4__@polkadot+util@14.0.1", + "eventemitter3@5.0.4", + "rxjs", + "tslib" + ] + }, + "@polkadot/keyring@14.0.1": { + "integrity": "sha512-kHydQPCeTvJrMC9VQO8LPhAhTUxzxfNF1HEknhZDBPPsxP/XpkYsEy/Ln1QzJmQqD5VsgwzLDE6cExbJ2CT9CA==" + }, + "@polkadot/keyring@14.0.1_@polkadot+util@14.0.1_@polkadot+util-crypto@14.0.1__@polkadot+util@14.0.1__@polkadot+x-randomvalues@14.0.1___@polkadot+util@14.0.1___@polkadot+wasm-util@7.5.4____@polkadot+util@14.0.1__@polkadot+wasm-util@7.5.4___@polkadot+util@14.0.1": { + "integrity": "sha512-kHydQPCeTvJrMC9VQO8LPhAhTUxzxfNF1HEknhZDBPPsxP/XpkYsEy/Ln1QzJmQqD5VsgwzLDE6cExbJ2CT9CA==", + "dependencies": [ + "@polkadot/util", + "@polkadot/util-crypto@14.0.1_@polkadot+util@14.0.1_@polkadot+x-randomvalues@14.0.1__@polkadot+util@14.0.1__@polkadot+wasm-util@7.5.4___@polkadot+util@14.0.1_@polkadot+wasm-util@7.5.4__@polkadot+util@14.0.1", + "tslib" + ] + }, + "@polkadot/networks@14.0.1": { + "integrity": "sha512-wGlBtXDkusRAj4P7uxfPz80gLO1+j99MLBaQi3bEym2xrFrFhgIWVHOZlBit/1PfaBjhX2Z8XjRxaM2w1p7w2w==", + "dependencies": [ + "@polkadot/util", + "@substrate/ss58-registry", + "tslib" + ] + }, + "@polkadot/rpc-augment@16.5.4": { + "integrity": "sha512-j9v3Ttqv/EYGezHtVksGJAFZhE/4F7LUWooOazh/53ATowMby3lZUdwInrK6bpYmG2whmYMw/Fo283fwDroBtQ==", + "dependencies": [ + "@polkadot/rpc-core", + "@polkadot/types@16.5.4_@polkadot+util@14.0.1_@polkadot+util-crypto@14.0.1__@polkadot+util@14.0.1__@polkadot+x-randomvalues@14.0.1___@polkadot+util@14.0.1___@polkadot+wasm-util@7.5.4____@polkadot+util@14.0.1__@polkadot+wasm-util@7.5.4___@polkadot+util@14.0.1", + "@polkadot/types-codec", + "@polkadot/util", + "tslib" + ] + }, + "@polkadot/rpc-core@16.5.4": { + "integrity": "sha512-92LOSTWujPjtmKOPvfCPs8rAaPFU+18wTtkIzwPwKxvxkN/SWsYSGIxmsoags9ramyHB6jp7Lr59TEuGMxIZzQ==", + "dependencies": [ + "@polkadot/rpc-augment", + "@polkadot/rpc-provider", + "@polkadot/types@16.5.4_@polkadot+util@14.0.1_@polkadot+util-crypto@14.0.1__@polkadot+util@14.0.1__@polkadot+x-randomvalues@14.0.1___@polkadot+util@14.0.1___@polkadot+wasm-util@7.5.4____@polkadot+util@14.0.1__@polkadot+wasm-util@7.5.4___@polkadot+util@14.0.1", + "@polkadot/util", + "rxjs", + "tslib" + ] + }, + "@polkadot/rpc-provider@16.5.4_@polkadot+util@14.0.1_@polkadot+util-crypto@14.0.1__@polkadot+util@14.0.1__@polkadot+x-randomvalues@14.0.1___@polkadot+util@14.0.1___@polkadot+wasm-util@7.5.4____@polkadot+util@14.0.1__@polkadot+wasm-util@7.5.4___@polkadot+util@14.0.1": { + "integrity": "sha512-mNAIBRA3jMvpnHsuqAX4InHSIqBdgxFD6ayVUFFAzOX8Fh6Xpd4RdI1dqr6a1pCzjnPSby4nbg+VuadWwauVtg==", + "dependencies": [ + "@polkadot/keyring@14.0.1_@polkadot+util@14.0.1_@polkadot+util-crypto@14.0.1__@polkadot+util@14.0.1__@polkadot+x-randomvalues@14.0.1___@polkadot+util@14.0.1___@polkadot+wasm-util@7.5.4____@polkadot+util@14.0.1__@polkadot+wasm-util@7.5.4___@polkadot+util@14.0.1", + "@polkadot/types@16.5.4_@polkadot+util@14.0.1_@polkadot+util-crypto@14.0.1__@polkadot+util@14.0.1__@polkadot+x-randomvalues@14.0.1___@polkadot+util@14.0.1___@polkadot+wasm-util@7.5.4____@polkadot+util@14.0.1__@polkadot+wasm-util@7.5.4___@polkadot+util@14.0.1", + "@polkadot/types-support", + "@polkadot/util", + "@polkadot/util-crypto@14.0.1_@polkadot+util@14.0.1_@polkadot+x-randomvalues@14.0.1__@polkadot+util@14.0.1__@polkadot+wasm-util@7.5.4___@polkadot+util@14.0.1_@polkadot+wasm-util@7.5.4__@polkadot+util@14.0.1", + "@polkadot/x-fetch", + "@polkadot/x-global", + "@polkadot/x-ws", + "eventemitter3@5.0.4", + "mock-socket", + "nock", + "tslib" + ], + "optionalDependencies": [ + "@substrate/connect" + ] + }, + "@polkadot/types-augment@16.5.4": { + "integrity": "sha512-AGjXR+Q9O9UtVkGw/HuOXlbRqVpvG6H8nr+taXP71wuC6RD9gznFBFBqoNkfWHD2w89esNVQLTvXHVxlLpTXqA==", + "dependencies": [ + "@polkadot/types@16.5.4_@polkadot+util@14.0.1_@polkadot+util-crypto@14.0.1__@polkadot+util@14.0.1__@polkadot+x-randomvalues@14.0.1___@polkadot+util@14.0.1___@polkadot+wasm-util@7.5.4____@polkadot+util@14.0.1__@polkadot+wasm-util@7.5.4___@polkadot+util@14.0.1", + "@polkadot/types-codec", + "@polkadot/util", + "tslib" + ] + }, + "@polkadot/types-augment@16.5.4_@polkadot+util@14.0.1_@polkadot+util-crypto@14.0.1__@polkadot+util@14.0.1__@polkadot+x-randomvalues@14.0.1___@polkadot+util@14.0.1___@polkadot+wasm-util@7.5.4____@polkadot+util@14.0.1__@polkadot+wasm-util@7.5.4___@polkadot+util@14.0.1": { + "integrity": "sha512-AGjXR+Q9O9UtVkGw/HuOXlbRqVpvG6H8nr+taXP71wuC6RD9gznFBFBqoNkfWHD2w89esNVQLTvXHVxlLpTXqA==", + "dependencies": [ + "@polkadot/types@16.5.4", + "@polkadot/types-codec", + "@polkadot/util", + "tslib" + ] + }, + "@polkadot/types-codec@16.5.4": { + "integrity": "sha512-OQtT1pmJu2F3/+Vh1OiXifKoeRy+CU1+Lu7dgTcdO705dnxU4447Zup5JVCJDnxBmMITts/38vbFN2pD225AnA==", + "dependencies": [ + "@polkadot/util", + "@polkadot/x-bigint", + "tslib" + ] + }, + "@polkadot/types-create@16.5.4": { + "integrity": "sha512-URQnvr/sgvgIRSxIW3lmml6HMSTRRj2hTZIm6nhMTlYSVT4rLWx0ZbYUAjoPBbaJ+BmoqZ6Bbs+tA+5cQViv5Q==", + "dependencies": [ + "@polkadot/types-codec", + "@polkadot/util", + "tslib" + ] + }, + "@polkadot/types-known@16.5.4": { + "integrity": "sha512-Dd59y4e3AFCrH9xiqMU4xlG5+Zy0OTy7GQvqJVYXZFyAH+4HYDlxXjJGcSidGAmJcclSYfS3wyEkfw+j1EOVEw==", + "dependencies": [ + "@polkadot/networks", + "@polkadot/types@16.5.4_@polkadot+util@14.0.1_@polkadot+util-crypto@14.0.1__@polkadot+util@14.0.1__@polkadot+x-randomvalues@14.0.1___@polkadot+util@14.0.1___@polkadot+wasm-util@7.5.4____@polkadot+util@14.0.1__@polkadot+wasm-util@7.5.4___@polkadot+util@14.0.1", + "@polkadot/types-codec", + "@polkadot/types-create", + "@polkadot/util", + "tslib" + ] + }, + "@polkadot/types-support@16.5.4": { + "integrity": "sha512-Ra6keCaO73ibxN6MzA56jFq9EReje7jjE4JQfzV5IpyDZdXcmPyJiEfa2Yps/YSP13Gc2e38t9FFyVau0V+SFQ==", + "dependencies": [ + "@polkadot/util", + "tslib" + ] + }, + "@polkadot/types@16.5.4": { + "integrity": "sha512-8Oo1QWaL0DkIc/n2wKBIozPWug/0b2dPVhL+XrXHxJX7rIqS0x8sXDRbM9r166sI0nTqJiUho7pRIkt2PR/DMQ==", + "dependencies": [ + "@polkadot/keyring@14.0.1", + "@polkadot/types-augment@16.5.4", + "@polkadot/types-codec", + "@polkadot/types-create", + "@polkadot/util", + "@polkadot/util-crypto@14.0.1", + "rxjs", + "tslib" + ] + }, + "@polkadot/types@16.5.4_@polkadot+util@14.0.1_@polkadot+util-crypto@14.0.1__@polkadot+util@14.0.1__@polkadot+x-randomvalues@14.0.1___@polkadot+util@14.0.1___@polkadot+wasm-util@7.5.4____@polkadot+util@14.0.1__@polkadot+wasm-util@7.5.4___@polkadot+util@14.0.1": { + "integrity": "sha512-8Oo1QWaL0DkIc/n2wKBIozPWug/0b2dPVhL+XrXHxJX7rIqS0x8sXDRbM9r166sI0nTqJiUho7pRIkt2PR/DMQ==", + "dependencies": [ + "@polkadot/keyring@14.0.1_@polkadot+util@14.0.1_@polkadot+util-crypto@14.0.1__@polkadot+util@14.0.1__@polkadot+x-randomvalues@14.0.1___@polkadot+util@14.0.1___@polkadot+wasm-util@7.5.4____@polkadot+util@14.0.1__@polkadot+wasm-util@7.5.4___@polkadot+util@14.0.1", + "@polkadot/types-augment@16.5.4_@polkadot+util@14.0.1_@polkadot+util-crypto@14.0.1__@polkadot+util@14.0.1__@polkadot+x-randomvalues@14.0.1___@polkadot+util@14.0.1___@polkadot+wasm-util@7.5.4____@polkadot+util@14.0.1__@polkadot+wasm-util@7.5.4___@polkadot+util@14.0.1", + "@polkadot/types-codec", + "@polkadot/types-create", + "@polkadot/util", + "@polkadot/util-crypto@14.0.1_@polkadot+util@14.0.1_@polkadot+x-randomvalues@14.0.1__@polkadot+util@14.0.1__@polkadot+wasm-util@7.5.4___@polkadot+util@14.0.1_@polkadot+wasm-util@7.5.4__@polkadot+util@14.0.1", + "rxjs", + "tslib" + ] + }, + "@polkadot/util-crypto@14.0.1": { + "integrity": "sha512-Cu7AKUzBTsUkbOtyuNzXcTpDjR9QW0fVR56o3gBmzfUCmvO1vlsuGzmmPzqpHymQQ3rrfqV78CPs62EGhw0R+A==", + "dependencies": [ + "@noble/curves@1.9.1", + "@noble/hashes", + "@polkadot/networks" + ] + }, + "@polkadot/util-crypto@14.0.1_@polkadot+util@14.0.1_@polkadot+x-randomvalues@14.0.1__@polkadot+util@14.0.1__@polkadot+wasm-util@7.5.4___@polkadot+util@14.0.1_@polkadot+wasm-util@7.5.4__@polkadot+util@14.0.1": { + "integrity": "sha512-Cu7AKUzBTsUkbOtyuNzXcTpDjR9QW0fVR56o3gBmzfUCmvO1vlsuGzmmPzqpHymQQ3rrfqV78CPs62EGhw0R+A==", + "dependencies": [ + "@noble/curves@1.9.1", + "@noble/hashes", + "@polkadot/networks", + "@polkadot/util", + "@polkadot/wasm-crypto", + "@polkadot/wasm-util", + "@polkadot/x-bigint", + "@polkadot/x-randomvalues", + "@scure/base", + "@scure/sr25519", + "tslib" + ] + }, + "@polkadot/util@14.0.1": { + "integrity": "sha512-764HhxkPV3x5rM0/p6QdynC2dw26n+SaE+jisjx556ViCd4E28Ke4xSPef6C0Spy4aoXf2gt0PuLEcBvd6fVZg==", + "dependencies": [ + "@polkadot/x-bigint", + "@polkadot/x-global", + "@polkadot/x-textdecoder", + "@polkadot/x-textencoder", + "@types/bn.js", + "bn.js", + "tslib" + ] + }, + "@polkadot/wasm-bridge@7.5.4_@polkadot+util@14.0.1_@polkadot+x-randomvalues@14.0.1__@polkadot+util@14.0.1__@polkadot+wasm-util@7.5.4___@polkadot+util@14.0.1_@polkadot+wasm-util@7.5.4__@polkadot+util@14.0.1": { + "integrity": "sha512-6xaJVvoZbnbgpQYXNw9OHVNWjXmtcoPcWh7hlwx3NpfiLkkjljj99YS+XGZQlq7ks2fVCg7FbfknkNb8PldDaA==", + "dependencies": [ + "@polkadot/util", + "@polkadot/wasm-util", + "@polkadot/x-randomvalues", + "tslib" + ] + }, + "@polkadot/wasm-crypto-asmjs@7.5.4_@polkadot+util@14.0.1": { + "integrity": "sha512-ZYwxQHAJ8pPt6kYk9XFmyuFuSS+yirJLonvP+DYbxOrARRUHfN4nzp4zcZNXUuaFhpbDobDSFn6gYzye6BUotA==", + "dependencies": [ + "@polkadot/util", + "tslib" + ] + }, + "@polkadot/wasm-crypto-init@7.5.4_@polkadot+util@14.0.1_@polkadot+x-randomvalues@14.0.1__@polkadot+util@14.0.1__@polkadot+wasm-util@7.5.4___@polkadot+util@14.0.1_@polkadot+wasm-util@7.5.4__@polkadot+util@14.0.1": { + "integrity": "sha512-U6s4Eo2rHs2n1iR01vTz/sOQ7eOnRPjaCsGWhPV+ZC/20hkVzwPAhiizu/IqMEol4tO2yiSheD4D6bn0KxUJhg==", + "dependencies": [ + "@polkadot/util", + "@polkadot/wasm-bridge", + "@polkadot/wasm-crypto-asmjs", + "@polkadot/wasm-crypto-wasm", + "@polkadot/wasm-util", + "@polkadot/x-randomvalues", + "tslib" + ] + }, + "@polkadot/wasm-crypto-wasm@7.5.4_@polkadot+util@14.0.1": { + "integrity": "sha512-PsHgLsVTu43eprwSvUGnxybtOEuHPES6AbApcs7y5ZbM2PiDMzYbAjNul098xJK/CPtrxZ0ePDFnaQBmIJyTFw==", + "dependencies": [ + "@polkadot/util", + "@polkadot/wasm-util", + "tslib" + ] + }, + "@polkadot/wasm-crypto@7.5.4_@polkadot+util@14.0.1_@polkadot+x-randomvalues@14.0.1__@polkadot+util@14.0.1__@polkadot+wasm-util@7.5.4___@polkadot+util@14.0.1_@polkadot+wasm-util@7.5.4__@polkadot+util@14.0.1": { + "integrity": "sha512-1seyClxa7Jd7kQjfnCzTTTfYhTa/KUTDUaD3DMHBk5Q4ZUN1D1unJgX+v1aUeXSPxmzocdZETPJJRZjhVOqg9g==", + "dependencies": [ + "@polkadot/util", + "@polkadot/wasm-bridge", + "@polkadot/wasm-crypto-asmjs", + "@polkadot/wasm-crypto-init", + "@polkadot/wasm-crypto-wasm", + "@polkadot/wasm-util", + "@polkadot/x-randomvalues", + "tslib" + ] + }, + "@polkadot/wasm-util@7.5.4_@polkadot+util@14.0.1": { + "integrity": "sha512-hqPpfhCpRAqCIn/CYbBluhh0TXmwkJnDRjxrU9Bnqtw9nMNa97D8JuOjdd2pi0rxm+eeLQ/f1rQMp71RMM9t4w==", + "dependencies": [ + "@polkadot/util", + "tslib" + ] + }, + "@polkadot/x-bigint@14.0.1": { + "integrity": "sha512-gfozjGnebr2rqURs31KtaWumbW4rRZpbiluhlmai6luCNrf5u8pB+oLA35kPEntrsLk9PnIG9OsC/n4hEtx4OQ==", + "dependencies": [ + "@polkadot/x-global", + "tslib" + ] + }, + "@polkadot/x-fetch@14.0.1": { + "integrity": "sha512-yFsnO0xfkp3bIcvH70ZvmeUINYH1YnjOIS1B430f3w6axkqKhAOWCgzzKGMSRgn4dtm3YgwMBKPQ4nyfIsGOJQ==", + "dependencies": [ + "@polkadot/x-global", + "node-fetch", + "tslib" + ] + }, + "@polkadot/x-global@14.0.1": { + "integrity": "sha512-aCI44DJU4fU0XXqrrSGIpi7JrZXK2kpe0jaQ2p6oDVXOOYEnZYXnMhTTmBE1lF/xtxzX50MnZrrU87jziU0qbA==", + "dependencies": [ + "tslib" + ] + }, + "@polkadot/x-randomvalues@14.0.1_@polkadot+util@14.0.1_@polkadot+wasm-util@7.5.4__@polkadot+util@14.0.1": { + "integrity": "sha512-/XkQcvshzJLHITuPrN3zmQKuFIPdKWoaiHhhVLD6rQWV60lTXA3ajw3ocju8ZN7xRxnweMS9Ce0kMPYa0NhRMg==", + "dependencies": [ + "@polkadot/util", + "@polkadot/wasm-util", + "@polkadot/x-global", + "tslib" + ] + }, + "@polkadot/x-textdecoder@14.0.1": { + "integrity": "sha512-CcWiPCuPVJsNk4Vq43lgFHqLRBQHb4r9RD7ZIYgmwoebES8TNm4g2ew9ToCzakFKSpzKu6I07Ne9wv/dt5zLuw==", + "dependencies": [ + "@polkadot/x-global", + "tslib" + ] + }, + "@polkadot/x-textencoder@14.0.1": { + "integrity": "sha512-VY51SpQmF1ccmAGLfxhYnAe95Spfz049WZ/+kK4NfsGF9WejxVdU53Im5C80l45r8qHuYQsCWU3+t0FNunh2Kg==", + "dependencies": [ + "@polkadot/x-global", + "tslib" + ] + }, + "@polkadot/x-ws@14.0.1": { + "integrity": "sha512-Q18hoSuOl7F4aENNGNt9XYxkrjwZlC6xye9OQrPDeHam1SrvflGv9mSZHyo+mwJs0z1PCz2STpPEN9PKfZvHng==", + "dependencies": [ + "@polkadot/x-global", + "tslib", + "ws@8.19.0" + ] + }, "@scure/base@1.2.6": { "integrity": "sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg==" }, "@scure/bip32@1.7.0": { "integrity": "sha512-E4FFX/N3f4B80AKWp5dP6ow+flD1LQZo/w8UnLGYZO674jS6YnYeepycOOksv+vLPSpgN35wgKgy+ybfTb2SMw==", "dependencies": [ - "@noble/curves", + "@noble/curves@1.9.1", "@noble/hashes", "@scure/base" ] @@ -127,6 +504,57 @@ "@scure/base" ] }, + "@scure/sr25519@0.2.0": { + "integrity": "sha512-uUuLP7Z126XdSizKtrCGqYyR3b3hYtJ6Fg/XFUXmc2//k2aXHDLqZwFeXxL97gg4XydPROPVnuaHGF2+xriSKg==", + "dependencies": [ + "@noble/curves@1.9.7", + "@noble/hashes" + ] + }, + "@substrate/connect-extension-protocol@2.2.2": { + "integrity": "sha512-t66jwrXA0s5Goq82ZtjagLNd7DPGCNjHeehRlE/gcJmJ+G56C0W+2plqOMRicJ8XGR1/YFnUSEqUFiSNbjGrAA==" + }, + "@substrate/connect-known-chains@1.10.3": { + "integrity": "sha512-OJEZO1Pagtb6bNE3wCikc2wrmvEU5x7GxFFLqqbz1AJYYxSlrPCGu4N2og5YTExo4IcloNMQYFRkBGue0BKZ4w==" + }, + "@substrate/connect@0.8.11_smoldot@2.0.26": { + "integrity": "sha512-ofLs1PAO9AtDdPbdyTYj217Pe+lBfTLltdHDs3ds8no0BseoLeAGxpz1mHfi7zB4IxI3YyAiLjH6U8cw4pj4Nw==", + "dependencies": [ + "@substrate/connect-extension-protocol", + "@substrate/connect-known-chains", + "@substrate/light-client-extension-helpers", + "smoldot" + ], + "deprecated": true + }, + "@substrate/light-client-extension-helpers@1.0.0_smoldot@2.0.26_@polkadot-api+substrate-client@0.1.4_rxjs@7.8.2": { + "integrity": "sha512-TdKlni1mBBZptOaeVrKnusMg/UBpWUORNDv5fdCaJklP4RJiFOzBCrzC+CyVI5kQzsXBisZ+2pXm+rIjS38kHg==", + "dependencies": [ + "@polkadot-api/json-rpc-provider", + "@polkadot-api/json-rpc-provider-proxy", + "@polkadot-api/observable-client", + "@polkadot-api/substrate-client", + "@substrate/connect-extension-protocol", + "@substrate/connect-known-chains", + "rxjs", + "smoldot" + ] + }, + "@substrate/ss58-registry@1.51.0": { + "integrity": "sha512-TWDurLiPxndFgKjVavCniytBIw+t4ViOi7TYp9h/D0NMmkEc9klFTo+827eyEJ0lELpqO207Ey7uGxUa+BS1jQ==" + }, + "@types/bn.js@5.2.0": { + "integrity": "sha512-DLbJ1BPqxvQhIGbeu8VbUC1DiAiahHtAYvA0ZEAa4P31F7IaArc8z3C3BRQdWX4mtLQuABG4yzp76ZrS02Ui1Q==", + "dependencies": [ + "@types/node" + ] + }, + "@types/node@24.2.0": { + "integrity": "sha512-3xyG3pMCq3oYCNg7/ZP+E1ooTaGB4cG8JWRsqqOYQdbWNY4zbaV0Ennrd7stjiJEFZCaybcIgpTjJWHRfBSIDw==", + "dependencies": [ + "undici-types" + ] + }, "abitype@1.2.3": { "integrity": "sha512-Ofer5QUnuUdTFsBRwARMoWKOH1ND5ehwYhJ3OJ/BQO+StkwQjHw0XyVh4vDttzHB7QOFhPHa/o413PJ82gU/Tg==" }, @@ -148,6 +576,9 @@ "auto-bind@5.0.1": { "integrity": "sha512-ooviqdwwgfIfNmDwo94wlshcdzfO64XV0Cg6oDsDYBJfITDz1EngD2z7DkbvCWn+XIMsIqW27sEVF6qcpJrRcg==" }, + "bn.js@5.2.2": { + "integrity": "sha512-v2YAxEmKaBLahNwE1mjp4WON6huMNeuDvagFZW+ASCuA/ku0bXR9hSMw0XpiqMoA3+rmnyck/tPRSFQkoC9Cuw==" + }, "chalk@5.6.2": { "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==" }, @@ -194,6 +625,15 @@ "convert-to-spaces@2.0.1": { "integrity": "sha512-rcQ1bsQO9799wq24uE5AM2tAILy4gXGIK/njFWcVQkGNZ96edlpY+A7bjwvzjYvLDyzmG1MmMLZhpcsb+klNMQ==" }, + "data-uri-to-buffer@4.0.1": { + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==" + }, + "debug@4.4.3": { + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dependencies": [ + "ms" + ] + }, "emoji-regex@10.6.0": { "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==" }, @@ -203,8 +643,8 @@ "environment@1.1.0": { "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==" }, - "es-toolkit@1.42.0": { - "integrity": "sha512-SLHIyY7VfDJBM8clz4+T2oquwTQxEzu263AyhVK4jREOAwJ+8eebaa4wM3nlvnAqhDrMm2EsA6hWHaQsMPQ1nA==" + "es-toolkit@1.44.0": { + "integrity": "sha512-6penXeZalaV88MM3cGkFZZfOoLGWshWWfdy0tWw/RlVVyhvMaWSBTOvXNeiW3e5FwdS5ePW0LGEu17zT139ktg==" }, "escape-string-regexp@2.0.0": { "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==" @@ -212,17 +652,33 @@ "eventemitter3@5.0.1": { "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==" }, + "eventemitter3@5.0.4": { + "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==" + }, + "fetch-blob@3.2.0": { + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "dependencies": [ + "node-domexception", + "web-streams-polyfill" + ] + }, "follow-redirects@1.15.11": { "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==" }, + "formdata-polyfill@4.0.10": { + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "dependencies": [ + "fetch-blob" + ] + }, "get-east-asian-width@1.4.0": { "integrity": "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==" }, "indent-string@5.0.0": { "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==" }, - "ink@6.5.1_react@19.2.3": { - "integrity": "sha512-wF3j/DmkM8q5E+OtfdQhCRw8/0ahkc8CUTgEddxZzpEWPslu7YPL3t64MWRoI9m6upVGpfAg4ms2BBvxCdKRLQ==", + "ink@6.6.0_react@19.2.4": { + "integrity": "sha512-QDt6FgJxgmSxAelcOvOHUvFxbIUjVpCH5bx+Slvc5m7IEcpGt3dYwbz/L+oRnqEGeRvwy1tineKK4ect3nW1vQ==", "dependencies": [ "@alcalzone/ansi-tokenize", "ansi-escapes", @@ -246,7 +702,7 @@ "type-fest", "widest-line", "wrap-ansi", - "ws", + "ws@8.19.0", "yoga-layout" ] }, @@ -266,18 +722,47 @@ "isows@1.0.7_ws@8.18.3": { "integrity": "sha512-I1fSfDCZL5P0v33sVqeTDSpcstAg/N+wF5HS033mogOVIp4B+oHC7oOCsA3axAbBSGTJ8QubbNmnIRN/h8U7hg==", "dependencies": [ - "ws" + "ws@8.18.3" ] }, "js-sha3@0.8.0": { "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==" }, + "json-stringify-safe@5.0.1": { + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==" + }, "memorystream@0.3.1": { "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==" }, "mimic-fn@2.1.0": { "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" }, + "mock-socket@9.3.1": { + "integrity": "sha512-qxBgB7Qa2sEQgHFjj0dSigq7fX4k6Saisd5Nelwp2q8mlbAFh5dHV9JTTlF8viYJLSSWgMCZFUom8PJcMNBoJw==" + }, + "ms@2.1.3": { + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "nock@13.5.6": { + "integrity": "sha512-o2zOYiCpzRqSzPj0Zt/dQ/DqZeYoaQ7TUonc/xUPjCGl9WeHpNbxgVvOquXYAaJzI0M9BXV3HTzG0p8IUAbBTQ==", + "dependencies": [ + "debug", + "json-stringify-safe", + "propagate" + ] + }, + "node-domexception@1.0.0": { + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "deprecated": true + }, + "node-fetch@3.3.2": { + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "dependencies": [ + "data-uri-to-buffer", + "fetch-blob", + "formdata-polyfill" + ] + }, "onetime@5.1.2": { "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "dependencies": [ @@ -287,31 +772,34 @@ "os-tmpdir@1.0.2": { "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==" }, - "ox@0.10.5": { - "integrity": "sha512-mXJRiZswmX46abrzNkJpTN9sPJ/Rhevsp5Dfg0z80D55aoLNmEV4oN+/+feSNW593c2CnHavMqSVBanpJ0lUkQ==", + "ox@0.11.3": { + "integrity": "sha512-1bWYGk/xZel3xro3l8WGg6eq4YEKlaqvyMtVhfMFpbJzK2F6rj4EDRtqDCWVEJMkzcmEi9uW2QxsqELokOlarw==", "dependencies": [ "@adraffy/ens-normalize", "@noble/ciphers", - "@noble/curves", + "@noble/curves@1.9.1", "@noble/hashes", "@scure/bip32", "@scure/bip39", "abitype", - "eventemitter3" + "eventemitter3@5.0.1" ] }, "patch-console@2.0.0": { "integrity": "sha512-0YNdUceMdaQwoKce1gatDScmMo5pu/tfABfnzEqeG0gtTmd7mh/WcwgUjtAeOU7N8nFFlbQBnFK2gXW5fGvmMA==" }, - "react-reconciler@0.33.0_react@19.2.3": { + "propagate@2.0.1": { + "integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==" + }, + "react-reconciler@0.33.0_react@19.2.4": { "integrity": "sha512-KetWRytFv1epdpJc3J4G75I4WrplZE5jOL7Yq0p34+OVOKF4Se7WrdIdVC45XsSSmUTlht2FM/fM1FZb1mfQeA==", "dependencies": [ "react", "scheduler" ] }, - "react@19.2.3": { - "integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==" + "react@19.2.4": { + "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==" }, "restore-cursor@4.0.0": { "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==", @@ -320,6 +808,15 @@ "signal-exit" ] }, + "rxjs@7.8.2": { + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "dependencies": [ + "tslib" + ] + }, + "scale-ts@1.6.1": { + "integrity": "sha512-PBMc2AWc6wSEqJYBDPcyCLUj9/tMKnLX70jLOSndMtcUoLQucP/DM0vnQo1wJAYjTrQiq8iG9rD0q6wFzgjH7g==" + }, "scheduler@0.27.0": { "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==" }, @@ -337,8 +834,14 @@ "is-fullwidth-code-point@5.1.0" ] }, - "solc@0.8.31": { - "integrity": "sha512-wpccgDgu/aE/rRcF2F/LeN+4knK0734XTcjppyaQOticjYd/Giq1AJE3XPQZKEViAsY3sNaFKl7QpMRYrK35vg==", + "smoldot@2.0.26": { + "integrity": "sha512-F+qYmH4z2s2FK+CxGj8moYcd1ekSIKH8ywkdqlOz88Dat35iB1DIYL11aILN46YSGMzQW/lbJNS307zBSDN5Ig==", + "dependencies": [ + "ws@8.19.0" + ] + }, + "solc@0.8.33": { + "integrity": "sha512-qPdHuAeg+DccqYz4Mq4grc7lOdgLA8wB8meCrMHcVXj70noKZhfAMzOCcOD/4RODZnn+sV3V6fWEyUkeVaBPVQ==", "dependencies": [ "command-exists", "commander", @@ -407,22 +910,31 @@ "os-tmpdir" ] }, + "tslib@2.8.1": { + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + }, "type-fest@4.41.0": { "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==" }, - "viem@2.43.1_ws@8.18.3": { - "integrity": "sha512-S33pBNlRvOlVv4+L94Z8ydCMDB1j0cuHFUvaC28i6OTxw3uY1P4M3h1YDFK8YC1H9/lIbeBTTvCRhi0FqU/2iw==", + "undici-types@7.10.0": { + "integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==" + }, + "viem@2.45.0_ws@8.18.3": { + "integrity": "sha512-iVA9qrAgRdtpWa80lCZ6Jri6XzmLOwwA1wagX2HnKejKeliFLpON0KOdyfqvcy+gUpBVP59LBxP2aKiL3aj8fg==", "dependencies": [ - "@noble/curves", + "@noble/curves@1.9.1", "@noble/hashes", "@scure/bip32", "@scure/bip39", "abitype", "isows", "ox", - "ws" + "ws@8.18.3" ] }, + "web-streams-polyfill@3.3.3": { + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==" + }, "widest-line@5.0.0": { "integrity": "sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA==", "dependencies": [ @@ -443,6 +955,9 @@ "ws@8.18.3": { "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==" }, + "ws@8.19.0": { + "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==" + }, "yoga-layout@3.2.1": { "integrity": "sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ==" } @@ -456,6 +971,7 @@ "jsr:@std/path@^1.1.3", "npm:@openzeppelin/contracts-upgradeable@^5.1.0", "npm:@openzeppelin/contracts@^5.1.0", + "npm:@polkadot/api@^16.5.4", "npm:cli-table3@~0.6.5", "npm:ink@^6.5.1", "npm:react@^19.2.3", diff --git a/tools/build.ts b/tools/build.ts index 3b0bfac..52ae096 100644 --- a/tools/build.ts +++ b/tools/build.ts @@ -1,6 +1,6 @@ import { join } from '@std/path' import { parseArgs } from '@std/cli' -import { compile, generateAbiIndex } from '../utils/build.ts' +import { compile, generateAbiIndex, generateLibIndex } from '../utils/build.ts' import { logger } from '../utils/logger.ts' const { filter, solcOnly, clean } = parseArgs(Deno.args, { @@ -116,11 +116,12 @@ for (const contract of contracts) { fileName: contract.name, sources: sourcesObj, rootDir, - compiler: 'resolc', + compiler: (Deno.env.get('RESOLC_BIN') as 'resolc') ?? 'resolc', }) } } await generateAbiIndex(rootDir) +await generateLibIndex(rootDir) logger.info('✨ All contracts compiled successfully') diff --git a/tools/lib/pvm.ts b/tools/lib/pvm.ts new file mode 100644 index 0000000..02a36c7 --- /dev/null +++ b/tools/lib/pvm.ts @@ -0,0 +1,43 @@ +import { ApiPromise, Keyring, WsProvider } from '@polkadot/api' +import { Hex } from 'viem' +import { logger } from '../../utils/logger.ts' + +const U128_MAX = '340282366920938463463374607431768211455' + +export async function uploadCodePVM( + code: Hex, + options?: { + wsUrl?: string + signerUri?: string + signerName?: string + storageDepositLimit?: string + }, +) { + const wsUrl = options?.wsUrl ?? 'ws://localhost:9944' + const signerUri = options?.signerUri ?? '//Alice' + const signerName = options?.signerName ?? 'Alice default' + const storageLimit = options?.storageDepositLimit ?? U128_MAX + + logger.info(`Uploading contract code to PVM...`) + const api = await ApiPromise.create({ + provider: new WsProvider(wsUrl), + noInitWarn: true, // Suppress API initialization warnings + }) + logger.debug(`Connected to ${wsUrl}`) + + try { + const keyring = new Keyring({ type: 'sr25519' }) + const alice = keyring.addFromUri(signerUri, { name: signerName }) + logger.debug('Setup signer') + + const storageDepositLimit = api.createType('Balance', storageLimit) + + const tx = api.tx.revive.uploadCode(code, storageDepositLimit) + logger.debug('Encoded transaction') + + const txHash = await tx.signAndSend(alice) + return txHash + } finally { + await api.disconnect() + } +} diff --git a/utils/build.ts b/utils/build.ts index edc0bbd..e9758d0 100644 --- a/utils/build.ts +++ b/utils/build.ts @@ -10,20 +10,41 @@ export interface CompileOutput { }> contracts: Record< string, - Record< - string, - { - evm?: { - bytecode?: { - object: string - } - } - abi: unknown - } - > + Record > } +export interface CompiledContract { + evm?: { + bytecode?: { + object: string + linkReferences?: LinkReferences + } + } + abi: unknown + missingLibraries?: string[] +} + +export interface LinkReference { + start: number + length: number +} + +export type LinkReferences = { + [fileName: string]: { + [libraryName: string]: LinkReference[] | string + } +} + +export type LibraryLink = { + [fileName: string]: string[] +} + +export interface CompileError { + severity: string + formattedMessage: string +} + async function format(code: string) { const command = new Deno.Command('deno', { args: ['fmt', '-'], @@ -74,11 +95,26 @@ async function checkCompilerExists(compiler: 'solc' | 'resolc') { Deno.exit(1) } } +function getLibraries( + contract: CompiledContract, +): LibraryLink { + const refs = contract.evm?.bytecode?.linkReferences + if (!refs) return {} + + const result: LibraryLink = {} + + for (const [sourceUnit, libraries] of Object.entries(refs)) { + result[sourceUnit] = Object.keys(libraries) + } + + return result +} async function compileWithBinary( compiler: 'solc' | 'resolc', sources: CompileInput, rootDir: string, + libraries?: LinkReferences, ): Promise { await checkCompilerExists(compiler) logger.info(`Compiling with ${compiler} ${compilerVersions[compiler]}`) @@ -98,6 +134,7 @@ async function compileWithBinary( join(rootDir, 'node_modules/@openzeppelin/') }/`, ], + ...(libraries && { libraries }), outputSelection: { '*': { '*': ['abi', 'evm.bytecode', 'metadata'], @@ -163,18 +200,31 @@ export async function compile(options: { rootDir: string compiler: 'solc' | 'resolc' generateAbi?: boolean + libraries?: LinkReferences }) { - const { fileName, sources, rootDir, compiler, generateAbi = false } = - options + const { + fileName, + sources, + rootDir, + compiler, + generateAbi = false, + libraries, + } = options const codegenDir = join(rootDir, 'codegen') const abiDir = join(codegenDir, 'abi') + const libsDir = join(codegenDir, 'libs') const outputDir = compiler.includes('resolc') ? join(codegenDir, 'pvm') : join(codegenDir, 'evm') logger.info(`Compiling ${fileName} with ${compiler}...`) - const output = await compileWithBinary(compiler, sources, rootDir) + const output = await compileWithBinary( + compiler, + sources, + rootDir, + libraries, + ) for (const contracts of Object.values(output.contracts)) { for (const [contractName, contract] of Object.entries(contracts)) { @@ -210,6 +260,22 @@ export async function compile(options: { tsContent, ) } + + const libs = getLibraries(contract) + if (Object.keys(libs).length == 0) continue + logger.info(`📜 Add libraries for ${contractName}`) + const libsName = `${contractName}Libs` + const tsContent = `export const ${libsName} = ${ + JSON.stringify( + libs, + null, + 2, + ) + } as const\n` + Deno.writeTextFileSync( + join(libsDir, `${contractName}.ts`), + tsContent, + ) } } @@ -253,3 +319,41 @@ export async function generateAbiIndex(rootDir: string) { await format(indexCode.join('\n')), ) } + +export async function generateLibIndex(rootDir: string) { + const codegenDir = join(rootDir, 'codegen') + const libDir = join(codegenDir, 'libs') + + logger.debug('📦 Generating Libs index file...') + const indexCode = [ + ` + export type Libs = typeof libs + export const libs = { + `.trimEnd(), + ] + + try { + const libFiles = Array.from(Deno.readDirSync(libDir)) + .filter((f) => f.isFile && f.name.endsWith('.ts')) + .sort((a, b) => a.name.localeCompare(b.name)) + + for (const libFile of libFiles) { + const contractName = libFile.name.replace('.ts', '') + const libName = `${contractName}Libs` + const importStatement = + `import { ${libName} } from './libs/${contractName}.ts'` + indexCode.unshift(importStatement) + indexCode.push(`${contractName}: ${libName},`) + } + } catch (error) { + logger.warn( + `⚠️ Could not generate lib: ${error}`, + ) + } + + indexCode.push('}') + Deno.writeFileSync( + join(codegenDir, `libs.ts`), + await format(indexCode.join('\n')), + ) +} diff --git a/utils/index.ts b/utils/index.ts index 4c9ec0f..6d3142c 100644 --- a/utils/index.ts +++ b/utils/index.ts @@ -39,10 +39,19 @@ type TracerConfig = { export async function createEnv({ rpcUrl, privateKey, + privateKeys, }: { rpcUrl: string - privateKey: Hex + privateKey?: Hex + privateKeys?: readonly [Hex, Hex] }) { + const resolvedPrivateKeys = privateKeys ?? + (privateKey ? [privateKey, privateKey] : undefined) + if (!resolvedPrivateKeys) { + throw new Error('createEnv requires privateKeys or privateKey') + } + const [primaryPrivateKey, secondaryPrivateKey] = resolvedPrivateKeys + const chainId = await (async () => { try { const resp = await fetch(rpcUrl, { @@ -128,19 +137,25 @@ export async function createEnv({ }).extend(publicActions) const wallet = createWalletClient({ - account: privateKeyToAccount(privateKey, { nonceManager }), + account: privateKeyToAccount(primaryPrivateKey, { nonceManager }), + transport, + chain, + }).extend(publicActions) + + const wallet2 = createWalletClient({ + account: privateKeyToAccount(secondaryPrivateKey, { nonceManager }), transport, chain, }).extend(publicActions) - // On geth let's endow the account wallet with some funds, to match the eth-rpc setup - if (chainName == 'Geth') { - const endowment = parseEther('1000') - const balance = await serverWallet.getBalance(wallet.account) + const endowment = parseEther('1000') + for (const targetWallet of [wallet, wallet2]) { + const address = targetWallet.account.address + const balance = await serverWallet.getBalance(targetWallet.account) if (balance < endowment / 2n) { const hash = await serverWallet.sendTransaction({ account: serverWallet.account, - to: wallet.account.address, + to: address, value: endowment, }) await serverWallet.waitForTransactionReceipt({ hash }) @@ -236,7 +251,14 @@ export async function createEnv({ return await wallet.waitForTransactionReceipt({ hash }) } - return { chain, deploy, getByteCode, wallet, debugClient } + return { + chain, + deploy, + getByteCode, + wallet, + wallet2, + debugClient, + } } export function readBytecode(