diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 68df95edbe..9bedc01c55 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -41,10 +41,14 @@ jobs: with: version: nightly-f625d0fa7c51e65b4bf1e8f7931cd1c6e2e285e9 - - name: Run e2e tests + - name: Run e2e tests local shell: bash run: e2e_test/run_all_tests.sh + - name: Run e2e tests alfajores + shell: bash + run: NETWORK=alfajores e2e_test/run_all_tests.sh + Lint: runs-on: ["8-cpu","self-hosted","org"] steps: diff --git a/core/celo_genesis.go b/core/celo_genesis.go index 53d882b87f..7ad61f6f2b 100644 --- a/core/celo_genesis.go +++ b/core/celo_genesis.go @@ -41,8 +41,8 @@ var ( DevAddr = common.BytesToAddress(DevAddr32.Bytes()) DevAddr32 = common.HexToHash("0x42cf1bbc38BaAA3c4898ce8790e21eD2738c6A4a") - DevFeeCurrencyAddr = common.HexToAddress("0xce16") // worth half as much as native CELO - DevFeeCurrencyAddr2 = common.HexToAddress("0xce17") // worth twice as much as native CELO + DevFeeCurrencyAddr = common.HexToAddress("0x000000000000000000000000000000000000ce16") // worth half as much as native CELO + DevFeeCurrencyAddr2 = common.HexToAddress("0x000000000000000000000000000000000000ce17") // worth twice as much as native CELO DevBalance, _ = new(big.Int).SetString("100000000000000000000", 10) rateNumerator, _ = new(big.Int).SetString("2000000000000000000000000", 10) rateNumerator2, _ = new(big.Int).SetString("500000000000000000000000", 10) diff --git a/e2e_test/debug-fee-currency/lib.sh b/e2e_test/debug-fee-currency/lib.sh index 263c86fdac..1bedb5a14c 100755 --- a/e2e_test/debug-fee-currency/lib.sh +++ b/e2e_test/debug-fee-currency/lib.sh @@ -21,8 +21,8 @@ function deploy_fee_currency() { exit 1 fi # this always resets the token address for the predeployed oracle3 - cast send --private-key $ACC_PRIVKEY $ORACLE3 'setExchangeRate(address, uint256, uint256)' $fee_currency 2ether 1ether &>/dev/null - cast send --private-key $ACC_PRIVKEY $FEE_CURRENCY_DIRECTORY_ADDR 'setCurrencyConfig(address, address, uint256)' $fee_currency $ORACLE3 60000 &>/dev/null + cast send --private-key $ACC_PRIVKEY $ORACLE3 'setExchangeRate(address, uint256, uint256)' $fee_currency 2ether 1ether > /dev/null + cast send --private-key $ACC_PRIVKEY $FEE_CURRENCY_DIRECTORY_ADDR 'setCurrencyConfig(address, address, uint256)' $fee_currency $ORACLE3 60000 > /dev/null echo "$fee_currency" ) } diff --git a/e2e_test/js-tests/package-lock.json b/e2e_test/js-tests/package-lock.json index e9d3322a24..9f5835a566 100644 --- a/e2e_test/js-tests/package-lock.json +++ b/e2e_test/js-tests/package-lock.json @@ -12,7 +12,7 @@ "chai": "^5.0.0", "ethers": "^6.10.0", "mocha": "^10.2.0", - "viem": "^2.9.6" + "viem": "^2.21.18" } }, "node_modules/@adraffy/ens-normalize": { @@ -43,33 +43,75 @@ } }, "node_modules/@scure/base": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.5.tgz", - "integrity": "sha512-Brj9FiG2W1MRQSTB212YVPRrcbjkv48FoZi/u4l/zds/ieRrqsh7aUf6CLwkAq61oKXr/ZlTzlY66gLIj3TFTQ==", + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.9.tgz", + "integrity": "sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==", + "license": "MIT", "funding": { "url": "https://paulmillr.com/funding/" } }, "node_modules/@scure/bip32": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.2.tgz", - "integrity": "sha512-N1ZhksgwD3OBlwTv3R6KFEcPojl/W4ElJOeCZdi+vuI5QmTFwLq3OFf2zd2ROpKvxFdgZ6hUpb0dx9bVNEwYCA==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.5.0.tgz", + "integrity": "sha512-8EnFYkqEQdnkuGBVpCzKxyIwDCBLDVj3oiX0EKUFre/tOjL/Hqba1D6n/8RcmaQy4f95qQFrO2A8Sr6ybh4NRw==", + "license": "MIT", "dependencies": { - "@noble/curves": "~1.2.0", - "@noble/hashes": "~1.3.2", - "@scure/base": "~1.1.2" + "@noble/curves": "~1.6.0", + "@noble/hashes": "~1.5.0", + "@scure/base": "~1.1.7" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip32/node_modules/@noble/curves": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.6.0.tgz", + "integrity": "sha512-TlaHRXDehJuRNR9TfZDNQ45mMEd5dwUwmicsafcIX4SsNiqnCHKjE/1alYPd/lDRVhxdhUAlv8uEhMCI5zjIJQ==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.5.0" + }, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip32/node_modules/@noble/hashes": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.5.0.tgz", + "integrity": "sha512-1j6kQFb7QRru7eKN3ZDvRcP13rugwdxZqCjbiAVZfIJwgj2A65UmT4TgARXGlXgnRkORLTDTrO19ZErt7+QXgA==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" }, "funding": { "url": "https://paulmillr.com/funding/" } }, "node_modules/@scure/bip39": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.1.tgz", - "integrity": "sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.4.0.tgz", + "integrity": "sha512-BEEm6p8IueV/ZTfQLp/0vhw4NPnT9oWf5+28nvmeUICjP99f4vr2d+qc7AVGDDtwRep6ifR43Yed9ERVmiITzw==", + "license": "MIT", "dependencies": { - "@noble/hashes": "~1.3.0", - "@scure/base": "~1.1.0" + "@noble/hashes": "~1.5.0", + "@scure/base": "~1.1.8" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip39/node_modules/@noble/hashes": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.5.0.tgz", + "integrity": "sha512-1j6kQFb7QRru7eKN3ZDvRcP13rugwdxZqCjbiAVZfIJwgj2A65UmT4TgARXGlXgnRkORLTDTrO19ZErt7+QXgA==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" }, "funding": { "url": "https://paulmillr.com/funding/" @@ -81,9 +123,10 @@ "integrity": "sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q==" }, "node_modules/abitype": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/abitype/-/abitype-1.0.0.tgz", - "integrity": "sha512-NMeMah//6bJ56H5XRj8QCV4AwuW6hB6zqz2LnhhLdcWVQOsXki6/Pn3APeqxCma62nXIcmZWdu1DlHWS74umVQ==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/abitype/-/abitype-1.0.6.tgz", + "integrity": "sha512-MMSqYh4+C/aVqI2RQaWqbvI4Kxo5cQV40WQ4QFtDnNzCkqChm8MuENhElmynZlO0qUy/ObkEUaXtKqYnx1Kp3A==", + "license": "MIT", "funding": { "url": "https://github.com/sponsors/wevm" }, @@ -627,15 +670,16 @@ } }, "node_modules/isows": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/isows/-/isows-1.0.3.tgz", - "integrity": "sha512-2cKei4vlmg2cxEjm3wVSqn8pcoRF/LX/wpifuuNquFO4SQmPwarClT+SUCA2lt+l581tTeZIPIZuIDo2jWN1fg==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/isows/-/isows-1.0.6.tgz", + "integrity": "sha512-lPHCayd40oW98/I0uvgaHKWCSvkzY27LjWLbtzOm64yQ+G3Q5npjjbdppU65iZXkK1Zt+kH9pfegli0AYfwYYw==", "funding": [ { "type": "github", - "url": "https://github.com/sponsors/wagmi-dev" + "url": "https://github.com/sponsors/wevm" } ], + "license": "MIT", "peerDependencies": { "ws": "*" } @@ -953,24 +997,26 @@ "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" }, "node_modules/viem": { - "version": "2.9.15", - "resolved": "https://registry.npmjs.org/viem/-/viem-2.9.15.tgz", - "integrity": "sha512-7kcmHqybc3JhpjL8gKY7YxBYpZt1//qhoTZIU5Ez9JdyRCnYMMnJu20s7wd7Gv6a3zPbq8jV8dCp94a/NLJJcA==", + "version": "2.21.27", + "resolved": "https://registry.npmjs.org/viem/-/viem-2.21.27.tgz", + "integrity": "sha512-lBpldSmwmKZ8jIiVLqoplPnuMs2Cza8tK0/G9m+ZjqfbgEp4+BPviFBJWCDNFxSp8H3gPzAuqj8o1WKQyq3wlg==", "funding": [ { "type": "github", "url": "https://github.com/sponsors/wevm" } ], + "license": "MIT", "dependencies": { - "@adraffy/ens-normalize": "1.10.0", - "@noble/curves": "1.2.0", - "@noble/hashes": "1.3.2", - "@scure/bip32": "1.3.2", - "@scure/bip39": "1.2.1", - "abitype": "1.0.0", - "isows": "1.0.3", - "ws": "8.13.0" + "@adraffy/ens-normalize": "1.11.0", + "@noble/curves": "1.6.0", + "@noble/hashes": "1.5.0", + "@scure/bip32": "1.5.0", + "@scure/bip39": "1.4.0", + "abitype": "1.0.6", + "isows": "1.0.6", + "webauthn-p256": "0.0.10", + "ws": "8.18.0" }, "peerDependencies": { "typescript": ">=5.0.4" @@ -981,10 +1027,44 @@ } } }, + "node_modules/viem/node_modules/@adraffy/ens-normalize": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.11.0.tgz", + "integrity": "sha512-/3DDPKHqqIqxUULp8yP4zODUY1i+2xvVWsv8A79xGWdCAG+8sb0hRh0Rk2QyOJUnnbyPUAZYcpBuRe3nS2OIUg==", + "license": "MIT" + }, + "node_modules/viem/node_modules/@noble/curves": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.6.0.tgz", + "integrity": "sha512-TlaHRXDehJuRNR9TfZDNQ45mMEd5dwUwmicsafcIX4SsNiqnCHKjE/1alYPd/lDRVhxdhUAlv8uEhMCI5zjIJQ==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.5.0" + }, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/viem/node_modules/@noble/hashes": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.5.0.tgz", + "integrity": "sha512-1j6kQFb7QRru7eKN3ZDvRcP13rugwdxZqCjbiAVZfIJwgj2A65UmT4TgARXGlXgnRkORLTDTrO19ZErt7+QXgA==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/viem/node_modules/ws": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", - "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "license": "MIT", "engines": { "node": ">=10.0.0" }, @@ -1001,6 +1081,49 @@ } } }, + "node_modules/webauthn-p256": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/webauthn-p256/-/webauthn-p256-0.0.10.tgz", + "integrity": "sha512-EeYD+gmIT80YkSIDb2iWq0lq2zbHo1CxHlQTeJ+KkCILWpVy3zASH3ByD4bopzfk0uCwXxLqKGLqp2W4O28VFA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wevm" + } + ], + "license": "MIT", + "dependencies": { + "@noble/curves": "^1.4.0", + "@noble/hashes": "^1.4.0" + } + }, + "node_modules/webauthn-p256/node_modules/@noble/curves": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.6.0.tgz", + "integrity": "sha512-TlaHRXDehJuRNR9TfZDNQ45mMEd5dwUwmicsafcIX4SsNiqnCHKjE/1alYPd/lDRVhxdhUAlv8uEhMCI5zjIJQ==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.5.0" + }, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/webauthn-p256/node_modules/@noble/hashes": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.5.0.tgz", + "integrity": "sha512-1j6kQFb7QRru7eKN3ZDvRcP13rugwdxZqCjbiAVZfIJwgj2A65UmT4TgARXGlXgnRkORLTDTrO19ZErt7+QXgA==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/workerpool": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", diff --git a/e2e_test/js-tests/package.json b/e2e_test/js-tests/package.json index 2b8de2061e..c09c0fc483 100644 --- a/e2e_test/js-tests/package.json +++ b/e2e_test/js-tests/package.json @@ -12,6 +12,6 @@ "chai": "^5.0.0", "ethers": "^6.10.0", "mocha": "^10.2.0", - "viem": "^2.9.6" + "viem": "^2.21.18" } } diff --git a/e2e_test/js-tests/send_tx.mjs b/e2e_test/js-tests/send_tx.mjs index 14dcd29f19..483205c1c6 100755 --- a/e2e_test/js-tests/send_tx.mjs +++ b/e2e_test/js-tests/send_tx.mjs @@ -23,18 +23,29 @@ const devChain = defineChain({ }, }); +var chain; +switch(process.env.NETWORK) { + case "alfajores": + chain = celoAlfajores; + break; + default: + chain = devChain; + // Code to run if no cases match +}; + const account = privateKeyToAccount(privateKey); const publicClient = createPublicClient({ account, - chain: devChain, + chain: chain, transport: http(), }); const walletClient = createWalletClient({ account, - chain: devChain, + chain: chain, transport: http(), }); + function sleep(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); } @@ -134,3 +145,4 @@ async function main() { return receipt; } await main(); +process.exit(0); diff --git a/e2e_test/js-tests/test_viem_tx.mjs b/e2e_test/js-tests/test_viem_tx.mjs index 0bb0b1caeb..7c5fd2495c 100644 --- a/e2e_test/js-tests/test_viem_tx.mjs +++ b/e2e_test/js-tests/test_viem_tx.mjs @@ -5,6 +5,7 @@ import { createWalletClient, http, defineChain, + parseAbi, } from "viem"; import { celoAlfajores } from "viem/chains"; import { privateKeyToAccount } from "viem/accounts"; @@ -22,18 +23,35 @@ const devChain = defineChain({ }, }); +const chain = (() => { + switch (process.env.NETWORK) { + case 'alfajores': + return celoAlfajores + default: + return devChain + }; +})(); + // Set up clients/wallet const publicClient = createPublicClient({ - chain: devChain, + chain: chain, transport: http(), }); const account = privateKeyToAccount(process.env.ACC_PRIVKEY); const walletClient = createWalletClient({ account, - chain: devChain, + chain: chain, transport: http(), }); +// Returns the base fee per gas for the current block multiplied by 2 to account for any increase in the subsequent block. +async function getGasFees(publicClient, tip, feeCurrency) { + const rate = await getRate(feeCurrency); + const b = await publicClient.getBlock(); + const tipInFeeCurrency = rate.toFeeCurrency(tip); + return [rate.toFeeCurrency(b.baseFeePerGas) + tipInFeeCurrency, tipInFeeCurrency]; +} + const testNonceBump = async ( firstCap, firstCurrency, @@ -51,7 +69,7 @@ const testNonceBump = async ( account, to: "0x00000000000000000000000000000000DeaDBeef", value: 2, - gas: 90000, + gas: 171000, maxFeePerGas: firstCap, maxPriorityFeePerGas: firstCap, nonce: syncBarrierRequest.nonce + 1, @@ -63,7 +81,7 @@ const testNonceBump = async ( account, to: "0x00000000000000000000000000000000DeaDBeef", value: 3, - gas: 90000, + gas: 171000, maxFeePerGas: secondCap, maxPriorityFeePerGas: secondCap, nonce: syncBarrierRequest.nonce + 1, @@ -77,7 +95,7 @@ const testNonceBump = async ( shouldReplace ) { throw err; // Only throw if unexpected error. - } + } } const syncBarrierSignature = await walletClient.signTransaction(syncBarrierRequest); @@ -110,7 +128,7 @@ describe("viem send tx", () => { assert.equal(receipt.status, "success", "receipt status 'failure'"); }).timeout(10_000); - it("send tx with gas estimation and check receipt", async () => { + it("send basic tx using viem gas estimation and check receipt", async () => { const request = await walletClient.prepareTransactionRequest({ account, to: "0x00000000000000000000000000000000DeaDBeef", @@ -124,15 +142,16 @@ describe("viem send tx", () => { assert.equal(receipt.status, "success", "receipt status 'failure'"); }).timeout(10_000); - it("send fee currency tx and check receipt", async () => { + it("send fee currency tx with explicit gas fields and check receipt", async () => { + const [maxFeePerGas, tip] = await getGasFees(publicClient, 2n, process.env.FEE_CURRENCY); const request = await walletClient.prepareTransactionRequest({ account, to: "0x00000000000000000000000000000000DeaDBeef", value: 2, - gas: 90000, + gas: 171000, feeCurrency: process.env.FEE_CURRENCY, - maxFeePerGas: 2000000000n, - maxPriorityFeePerGas: 2n, + maxFeePerGas: maxFeePerGas, + maxPriorityFeePerGas: tip, }); const signature = await walletClient.signTransaction(request); const hash = await walletClient.sendRawTransaction({ @@ -147,33 +166,51 @@ describe("viem send tx", () => { account, to: "0x00000000000000000000000000000000DeaDBeef", value: 2, - gas: 90000, + gas: 171000, feeCurrency: process.env.FEE_CURRENCY, }); + // Get the raw gas price and maxPriorityFeePerGas const gasPriceNative = await publicClient.getGasPrice({}); var maxPriorityFeePerGasNative = await publicClient.estimateMaxPriorityFeePerGas({}); const block = await publicClient.getBlock({}); + + // Check them against the base fee. assert.equal( BigInt(block.baseFeePerGas) + maxPriorityFeePerGasNative, gasPriceNative, ); - // viem's getGasPrice does not expose additional request parameters, - // but Celo's override 'chain.fees.estimateFeesPerGas' action does. - // this will call the eth_gasPrice and eth_maxPriorityFeePerGas methods - // with the additional feeCurrency parameter internally + // viem's getGasPrice does not expose additional request parameters, but + // Celo's override 'chain.fees.estimateFeesPerGas' action does. This will + // call the eth_gasPrice and eth_maxPriorityFeePerGas methods with the + // additional feeCurrency parameter internally, it also multiplies the base + // fee component of the maxFeePerGas by a multiplier which by default is + // 1.2 or (12n/10n). var fees = await publicClient.estimateFeesPerGas({ type: "eip1559", request: { feeCurrency: process.env.FEE_CURRENCY, }, }); - // first check that the fee currency denominated gas price - // converts properly to the native gas price - assert.equal(fees.maxFeePerGas, gasPriceNative * 2n); - assert.equal(fees.maxPriorityFeePerGas, maxPriorityFeePerGasNative * 2n); + + // Get the exchange rates for the fee currency. + const abi = parseAbi(['function getExchangeRate(address token) public view returns (uint256 numerator, uint256 denominator)']); + const [numerator, denominator] = await publicClient.readContract({ + address: process.env.FEE_CURRENCY_DIRECTORY_ADDR, + abi: abi, + functionName: 'getExchangeRate', + args: [process.env.FEE_CURRENCY], + }); + + // TODO fix this when viem is fixed - https://github.com/celo-org/viem/pull/20 + // The expected value for the max fee should be the (baseFeePerGas * multiplier) + maxPriorityFeePerGas + // Instead what is currently returned is (maxFeePerGas * multiplier) + maxPriorityFeePerGas + const maxPriorityFeeInFeeCurrency = (maxPriorityFeePerGasNative * numerator) / denominator; + const maxFeeInFeeCurrency = ((block.baseFeePerGas +maxPriorityFeePerGasNative)*numerator) /denominator; + assert.equal(fees.maxFeePerGas, ((maxFeeInFeeCurrency*12n)/10n) + maxPriorityFeeInFeeCurrency); + assert.equal(fees.maxPriorityFeePerGas, maxPriorityFeeInFeeCurrency); // check that the prepared transaction request uses the // converted gas price internally @@ -187,7 +224,7 @@ describe("viem send tx", () => { to: "0x00000000000000000000000000000000DeaDBeef", value: 2, feeCurrency: process.env.FEE_CURRENCY, - maxFeePerGas: 2000000000n, + maxFeePerGas: 50000000000n, maxPriorityFeePerGas: 2n, }); const signature = await walletClient.signTransaction(request); @@ -200,19 +237,14 @@ describe("viem send tx", () => { it("send overlapping nonce tx in different currencies", async () => { const priceBump = 1.1; - const rate = 2; + + const rate = await getRate(process.env.FEE_CURRENCY); // Native to FEE_CURRENCY const nativeCap = 30_000_000_000; - const bumpCurrencyCap = BigInt(Math.round(nativeCap * rate * priceBump)); - const failToBumpCurrencyCap = BigInt( - Math.round(nativeCap * rate * priceBump) - 1, - ); - // FEE_CURRENCY to Native - const currencyCap = 60_000_000_000; - const bumpNativeCap = BigInt(Math.round((currencyCap * priceBump) / rate)); - const failToBumpNativeCap = BigInt( - Math.round((currencyCap * priceBump) / rate) - 1, - ); + const bumpCurrencyCap = rate.toFeeCurrency(BigInt(Math.round(nativeCap * priceBump))); + const failToBumpCurrencyCap = rate.toFeeCurrency(BigInt( + Math.round(nativeCap * priceBump) - 1, + )); const tokenCurrency = process.env.FEE_CURRENCY; const nativeCurrency = null; await testNonceBump( @@ -229,6 +261,13 @@ describe("viem send tx", () => { tokenCurrency, false, ); + + // FEE_CURRENCY to Native + const currencyCap = 60_000_000_000; + const bumpNativeCap = rate.toNative(BigInt(Math.round(currencyCap * priceBump))); + const failToBumpNativeCap = rate.toNative(BigInt( + Math.round(currencyCap * priceBump) - 1, + )); await testNonceBump( currencyCap, tokenCurrency, @@ -243,14 +282,14 @@ describe("viem send tx", () => { nativeCurrency, false, ); - }).timeout(20_000); + }).timeout(40_000); it("send tx with unregistered fee currency", async () => { const request = await walletClient.prepareTransactionRequest({ account, to: "0x00000000000000000000000000000000DeaDBeef", value: 2, - gas: 90000, + gas: 171000, feeCurrency: "0x000000000000000000000000000000000badc310", maxFeePerGas: 1000000000n, maxPriorityFeePerGas: 1n, @@ -272,20 +311,36 @@ describe("viem send tx", () => { }).timeout(10_000); it("send fee currency tx with just high enough gas price", async () => { + // The idea of this test is to check that the fee currency is taken into + // account by the server. We do this by using a fee currency that has a + // value greater than celo, so that the base fee in fee currency becomes a + // number significantly lower than the base fee in celo. If the server + // doesn't take into account the fee currency then it will reject the + // transaction because the maxFeePerGas will be too low. + + // If we are running local tests we use FEE_CURRENCY2 since it is worth + // double the value of celo, otherwise we use FEE_CURRENCY which is USDC + // end currently worth roughly double the value of celo. + const fc = process.env.NETWORK == null ? process.env.FEE_CURRENCY2 : process.env.FEE_CURRENCY; + const rate = await getRate(fc); const block = await publicClient.getBlock({}); - // Actually, the base fee will be a bit lower next block, since our blocks - // are always mostly empty. But the difference will be much less than the - // exchange rate of 2, so the test will still check that we take the fee - // currency into account everywhere. - const maxFeePerGas = block.baseFeePerGas / 2n; + // We increment the base fee by 10% to cover the case where the base fee increases next block. + const convertedBaseFee = rate.toFeeCurrency(block.baseFeePerGas * 11n/10n); + + // Check that the converted base fee value is still below the native base + // fee value, if this check fails we will need to consider an alternative + // fee currency to USDC for network tests. + if (convertedBaseFee >= block.baseFeePerGas) { + assert.fail(`Converted base fee (${convertedBaseFee}) not less than native base fee (${block.baseFeePerGas})`); + } const request = await walletClient.prepareTransactionRequest({ account, to: "0x00000000000000000000000000000000DeaDBeef", value: 2, - gas: 90000, - feeCurrency: process.env.FEE_CURRENCY2, - maxFeePerGas, - maxPriorityFeePerGas: 1n, + gas: 171000, + feeCurrency: process.env.FEE_CURRENCY, + maxFeePerGas: convertedBaseFee +2n, + maxPriorityFeePerGas: 2n, }); const signature = await walletClient.signTransaction(request); const hash = await walletClient.sendRawTransaction({ @@ -293,5 +348,19 @@ describe("viem send tx", () => { }); const receipt = await publicClient.waitForTransactionReceipt({ hash }); assert.equal(receipt.status, "success", "receipt status 'failure'"); - }).timeout(5_000); + }).timeout(10_000); }); + +async function getRate(feeCurrencyAddress) { + const abi = parseAbi(['function getExchangeRate(address token) public view returns (uint256 numerator, uint256 denominator)']); + const [numerator, denominator] = await publicClient.readContract({ + address: process.env.FEE_CURRENCY_DIRECTORY_ADDR, + abi: abi, + functionName: 'getExchangeRate', + args: [feeCurrencyAddress], + }); + return { + toFeeCurrency: (v) => (v * numerator) / denominator, + toNative: (v) => (v * denominator) / numerator, + }; +} diff --git a/e2e_test/run_all_tests.sh b/e2e_test/run_all_tests.sh index 1d28f47f0d..5cfd6d621b 100755 --- a/e2e_test/run_all_tests.sh +++ b/e2e_test/run_all_tests.sh @@ -6,32 +6,45 @@ source "$SCRIPT_DIR/shared.sh" TEST_GLOB=$1 -## Start geth -cd "$SCRIPT_DIR/.." || exit 1 -make geth -trap 'kill %%' EXIT # kill bg job at exit -build/bin/geth --dev --http --http.api eth,web3,net --txpool.nolocals &>"$SCRIPT_DIR/geth.log" & - -# Wait for geth to be ready -for _ in {1..10}; do - if cast block &>/dev/null; then - break - fi - sleep 0.2 -done +if [ -z $NETWORK ]; then + ## Start geth + cd "$SCRIPT_DIR/.." || exit 1 + make geth + trap 'kill %%' EXIT # kill bg job at exit + build/bin/geth --dev --http --http.api eth,web3,net --txpool.nolocals &>"$SCRIPT_DIR/geth.log" & + + # Wait for geth to be ready + for _ in {1..10}; do + if cast block &>/dev/null; then + break + fi + sleep 0.2 + done + + ## Run tests + echo Geth ready, start tests +fi -## Run tests -echo Geth ready, start tests cd "$SCRIPT_DIR" || exit 1 - # There's a problem with geth return errors on the first transaction sent. # See https://github.com/ethereum/web3.py/issues/3212 # To work around this, send a transaction before running tests -cast send --async --json --private-key "$ACC_PRIVKEY" "$TOKEN_ADDR" 'transfer(address to, uint256 value) returns (bool)' 0x000000000000000000000000000000000000dEaD 100 +cast send --json --private-key "$ACC_PRIVKEY" "$TOKEN_ADDR" 'transfer(address to, uint256 value) returns (bool)' 0x000000000000000000000000000000000000dEaD 100 > /dev/null || true failures=0 tests=0 +echo "Globbing with \"$TEST_GLOB\"" for f in test_*"$TEST_GLOB"*; do + echo "for file $f" + if [[ -n $NETWORK ]]; then + case $f in + # Skip tests that require a local network. + test_fee_currency_fails_on_credit.sh|test_fee_currency_fails_on_debit.sh|test_fee_currency_fails_intrinsic.sh) + echo "skipping file $f" + continue + ;; + esac + fi echo -e "\nRun $f" if "./$f"; then tput setaf 2 || true diff --git a/e2e_test/shared.sh b/e2e_test/shared.sh index 302ea66172..2a4207bb9a 100644 --- a/e2e_test/shared.sh +++ b/e2e_test/shared.sh @@ -3,16 +3,29 @@ SCRIPT_DIR=$(readlink -f "$(dirname "$0")") export SCRIPT_DIR -export ETH_RPC_URL=http://127.0.0.1:8545 + +case $NETWORK in + alfajores) + export ETH_RPC_URL=https://alfajores-forno.celo-testnet.org + export TOKEN_ADDR=0xF194afDf50B03e69Bd7D057c1Aa9e10c9954E4C9 + export FEE_HANDLER=0xEAaFf71AB67B5d0eF34ba62Ea06Ac3d3E2dAAA38 + export FEE_CURRENCY=0x4822e58de6f5e485eF90df51C41CE01721331dC0 + echo "Using Alfajores network" + ;; + '') + export ETH_RPC_URL=http://127.0.0.1:8545 + export TOKEN_ADDR=0x471ece3750da237f93b8e339c536989b8978a438 + export FEE_HANDLER=0xcd437749e43a154c07f3553504c68fbfd56b8778 + export FEE_CURRENCY=0x000000000000000000000000000000000000ce16 + export FEE_CURRENCY2=0x000000000000000000000000000000000000ce17 + echo "Using local network" + ;; +esac export ACC_ADDR=0x42cf1bbc38BaAA3c4898ce8790e21eD2738c6A4a export ACC_PRIVKEY=0x2771aff413cac48d9f8c114fabddd9195a2129f3c2c436caa07e27bb7f58ead5 export REGISTRY_ADDR=0x000000000000000000000000000000000000ce10 -export TOKEN_ADDR=0x471ece3750da237f93b8e339c536989b8978a438 export FEE_CURRENCY_DIRECTORY_ADDR=0x9212Fb72ae65367A7c887eC4Ad9bE310BAC611BF -export FEE_CURRENCY=0x000000000000000000000000000000000000ce16 -export FEE_CURRENCY2=0x000000000000000000000000000000000000ce17 -export FEE_HANDLER=0xcd437749e43a154c07f3553504c68fbfd56b8778 export ORACLE3=0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb0003 export FIXIDITY_1=1000000000000000000000000 diff --git a/e2e_test/test_base_fee_recipient.sh b/e2e_test/test_base_fee_recipient.sh index 9a8b2f9e79..030c130bf2 100755 --- a/e2e_test/test_base_fee_recipient.sh +++ b/e2e_test/test_base_fee_recipient.sh @@ -6,15 +6,22 @@ set -x source shared.sh # Send token and check balance -balance_before=$(cast balance $FEE_HANDLER) tx_json=$(cast send --json --private-key $ACC_PRIVKEY $TOKEN_ADDR 'transfer(address to, uint256 value) returns (bool)' 0x000000000000000000000000000000000000dEaD 100) -gas_used=$(echo $tx_json | jq -r '.gasUsed') block_number=$(echo $tx_json | jq -r '.blockNumber' | cast to-dec) -base_fee=$(cast base-fee $block_number) +block=$(cast block --json --full $block_number) +gas_used=$(echo $block | jq -r '.gasUsed' | cast to-dec) +base_fee=$(echo $block | jq -r '.baseFeePerGas' | cast to-dec) +if [[ -n $NETWORK ]]; then + # Every block in a non dev system contains a system tx that pays nothing for gas so we must subtract this from the total gas per block + system_tx_hash=$(echo $block | jq -r '.transactions[] | select(.from | ascii_downcase == "0xdeaddeaddeaddeaddeaddeaddeaddeaddead0001") | .hash') + system_tx_gas=$(cast receipt --json $system_tx_hash | jq -r '.cumulativeGasUsed' | cast to-dec) + gas_used=$((gas_used - system_tx_gas)) +fi +balance_before=$(cast balance --block $((block_number-1)) $FEE_HANDLER) +balance_after=$(cast balance --block $block_number $FEE_HANDLER) +balance_change=$((balance_after - balance_before)) expected_balance_change=$((base_fee * gas_used)) -balance_after=$(cast balance $FEE_HANDLER) -echo "Balance change: $balance_before -> $balance_after" -[[ $((balance_before + expected_balance_change)) -eq $balance_after ]] || ( +[[ $expected_balance_change -eq $balance_change ]] || ( echo "Balance did not change as expected" exit 1 ) diff --git a/e2e_test/test_fee_currency_fails_intrinsic.sh b/e2e_test/test_fee_currency_fails_intrinsic.sh index feb2139c8e..c2c515c709 100755 --- a/e2e_test/test_fee_currency_fails_intrinsic.sh +++ b/e2e_test/test_fee_currency_fails_intrinsic.sh @@ -7,6 +7,7 @@ source debug-fee-currency/lib.sh # Expect that the creditGasFees failed and is logged by geth tail -F -n 0 geth.log >debug-fee-currency/geth.intrinsic.log & # start log capture +trap 'kill %%' EXIT # kill bg tail job on exit ( sleep 0.2 fee_currency=$(deploy_fee_currency false false true) @@ -27,7 +28,6 @@ tail -F -n 0 geth.log >debug-fee-currency/geth.intrinsic.log & # start log captu cleanup_fee_currency $fee_currency ) sleep 0.5 -kill %1 # stop log capture # although we sent a transaction wih faulty fee-currency twice, # the EVM call should have been executed only once if [ "$(grep -Ec "fee-currency EVM execution error, temporarily blocking fee-currency in local txpools .+ surpassed maximum allowed intrinsic gas for CreditFees\(\) in fee-currency" debug-fee-currency/geth.intrinsic.log)" -ne 1 ]; then exit 1; fi diff --git a/e2e_test/test_fee_currency_fails_on_credit.sh b/e2e_test/test_fee_currency_fails_on_credit.sh index 8fae3f6f19..e67f1fbef5 100755 --- a/e2e_test/test_fee_currency_fails_on_credit.sh +++ b/e2e_test/test_fee_currency_fails_on_credit.sh @@ -6,6 +6,7 @@ source shared.sh source debug-fee-currency/lib.sh tail -F -n0 geth.log >debug-fee-currency/geth.partial.log & # start log capture +trap 'kill %%' EXIT # kill bg tail job on exit ( sleep 0.2 fee_currency=$(deploy_fee_currency false true false) @@ -26,7 +27,6 @@ tail -F -n0 geth.log >debug-fee-currency/geth.partial.log & # start log capture cleanup_fee_currency $fee_currency ) sleep 0.5 -kill %1 # stop log capture # although we sent a transaction wih faulty fee-currency twice, # the EVM call should have been executed only once grep "" debug-fee-currency/geth.partial.log