From 5d727676ec34d33717c50f04abf87ccabbc0ed25 Mon Sep 17 00:00:00 2001 From: deterclosed <164524498+deterclosed@users.noreply.github.com> Date: Wed, 18 Dec 2024 18:13:28 +0800 Subject: [PATCH] chore: fix some typos in comment (#2041) --- .../workflows/handler.publish-pr-packages.yml | 2 +- flake.nix | 1 + .../contracts/interface/IVestingScheduler.sol | 2 +- .../interface/IVestingSchedulerV2.sol | 4 +- .../src/utils/gasMetering/gasMetering.js | 2 +- packages/sdk-core/CHANGELOG.md | 6 + packages/sdk-core/package.json | 4 +- packages/sdk-core/src/BatchCall.ts | 28 ++++- packages/sdk-core/src/Operation.ts | 21 +++- packages/sdk-core/test/2_operation.test.ts | 29 ++++- packages/sdk-core/test/3_batch_call.test.ts | 115 +++++++++++++++++- packages/subgraph/package.json | 2 +- yarn.lock | 12 +- 13 files changed, 205 insertions(+), 23 deletions(-) diff --git a/.github/workflows/handler.publish-pr-packages.yml b/.github/workflows/handler.publish-pr-packages.yml index 6ea3d23b94..72513e6e32 100644 --- a/.github/workflows/handler.publish-pr-packages.yml +++ b/.github/workflows/handler.publish-pr-packages.yml @@ -39,7 +39,7 @@ jobs: - name: Download artifact continue-on-error: true id: download_artifact - uses: dawidd6/action-download-artifact@v4 + uses: dawidd6/action-download-artifact@v6 with: workflow: ${{ github.event.workflow_run.workflow_id }} workflow_conclusion: success diff --git a/flake.nix b/flake.nix index a49a5fb1d2..471be65563 100644 --- a/flake.nix +++ b/flake.nix @@ -55,6 +55,7 @@ # test utilities lcov actionlint + git ]; # solidity dev inputs diff --git a/packages/automation-contracts/scheduler/contracts/interface/IVestingScheduler.sol b/packages/automation-contracts/scheduler/contracts/interface/IVestingScheduler.sol index 9cb5b20802..f339143436 100644 --- a/packages/automation-contracts/scheduler/contracts/interface/IVestingScheduler.sol +++ b/packages/automation-contracts/scheduler/contracts/interface/IVestingScheduler.sol @@ -59,7 +59,7 @@ interface IVestingScheduler { * @param superToken SuperToken to be vested * @param receiver Vesting receiver * @param startDate Timestamp when the vesting should start - * @param cliffDate Timestamp of cliff exectution - if 0, startDate acts as cliff + * @param cliffDate Timestamp of cliff execution - if 0, startDate acts as cliff * @param flowRate The flowRate for the stream * @param cliffAmount The amount to be transferred at the cliff * @param endDate The timestamp when the stream should stop. diff --git a/packages/automation-contracts/scheduler/contracts/interface/IVestingSchedulerV2.sol b/packages/automation-contracts/scheduler/contracts/interface/IVestingSchedulerV2.sol index 65561080c9..c88ea78922 100644 --- a/packages/automation-contracts/scheduler/contracts/interface/IVestingSchedulerV2.sol +++ b/packages/automation-contracts/scheduler/contracts/interface/IVestingSchedulerV2.sol @@ -45,7 +45,7 @@ interface IVestingSchedulerV2 { * @param receiver Vesting receiver * @param startDate Timestamp when the vesting should start * @param claimValidityDate Date before which the claimable schedule must be claimed - * @param cliffDate Timestamp of cliff exectution - if 0, startDate acts as cliff + * @param cliffDate Timestamp of cliff execution - if 0, startDate acts as cliff * @param flowRate The flowRate for the stream * @param cliffAmount The amount to be transferred at the cliff * @param endDate The timestamp when the stream should stop. @@ -98,7 +98,7 @@ interface IVestingSchedulerV2 { * @param superToken SuperToken to be vested * @param receiver Vesting receiver * @param startDate Timestamp when the vesting should start - * @param cliffDate Timestamp of cliff exectution - if 0, startDate acts as cliff + * @param cliffDate Timestamp of cliff execution - if 0, startDate acts as cliff * @param flowRate The flowRate for the stream * @param cliffAmount The amount to be transferred at the cliff * @param endDate The timestamp when the stream should stop. diff --git a/packages/js-sdk/src/utils/gasMetering/gasMetering.js b/packages/js-sdk/src/utils/gasMetering/gasMetering.js index ec15b69025..f390a7c10d 100644 --- a/packages/js-sdk/src/utils/gasMetering/gasMetering.js +++ b/packages/js-sdk/src/utils/gasMetering/gasMetering.js @@ -56,7 +56,7 @@ module.exports = class GasMeter { // TODO break; default: - throw new Error(`Unsuported report type ${outputFormat}`); + throw new Error(`Unsupported report type ${outputFormat}`); } this.gasPrice = new this.BN(gasPrice); this.records = []; diff --git a/packages/sdk-core/CHANGELOG.md b/packages/sdk-core/CHANGELOG.md index 256e301e42..4d45bb3fcc 100644 --- a/packages/sdk-core/CHANGELOG.md +++ b/packages/sdk-core/CHANGELOG.md @@ -10,6 +10,12 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### Changed ### Fixed +## [0.9.0] - 2024-12-13 + +### Added +- Handle new Operation types with BatchCall +- Forward ETH value with BatchCall and Operation + ## [0.8.0] - 2024-08-01 ### Breaking diff --git a/packages/sdk-core/package.json b/packages/sdk-core/package.json index 0ddafbdba4..518dde768c 100644 --- a/packages/sdk-core/package.json +++ b/packages/sdk-core/package.json @@ -1,12 +1,11 @@ { "name": "@superfluid-finance/sdk-core", "description": "SDK Core for building with Superfluid Protocol", - "version": "0.8.0", + "version": "0.9.0", "bugs": "https://github.com/superfluid-finance/protocol-monorepo/issues", "dependencies": { "@superfluid-finance/ethereum-contracts": "1.12.0", "@superfluid-finance/metadata": "^1.5.2", - "browserify": "17.0.0", "graphql-request": "6.1.0", "lodash": "4.17.21", "tsify": "5.0.4" @@ -16,6 +15,7 @@ "@graphql-codegen/near-operation-file-preset": "^3.0.0", "@graphql-typed-document-node/core": "^3.2.0", "ajv": "^8.17.1", + "browserify": "^17.0.1", "ethers": "^5.7.2", "get-graphql-schema": "^2.1.2", "mocha": "^10.7.3" diff --git a/packages/sdk-core/src/BatchCall.ts b/packages/sdk-core/src/BatchCall.ts index b5c85e3a7a..ac52a87cae 100644 --- a/packages/sdk-core/src/BatchCall.ts +++ b/packages/sdk-core/src/BatchCall.ts @@ -1,5 +1,5 @@ import { JsonFragment } from "@ethersproject/abi"; -import { ethers } from "ethers"; +import { BigNumber, ethers } from "ethers"; import Host from "./Host"; import Operation, { BatchOperationType } from "./Operation"; @@ -15,6 +15,7 @@ export interface OperationStruct { readonly operationType: number; readonly target: string; readonly data: string; + readonly value?: ethers.BigNumber; } export const batchOperationTypeStringToTypeMap = new Map< @@ -30,6 +31,8 @@ export const batchOperationTypeStringToTypeMap = new Map< ["SUPERTOKEN_DOWNGRADE", 102], ["SUPERFLUID_CALL_AGREEMENT", 201], ["CALL_APP_ACTION", 202], + ["SIMPLE_FORWARD_CALL", 301], + ["ERC2771_FORWARD_CALL", 302], ]); /** @@ -89,10 +92,25 @@ export default class BatchCall { const operationStructArray = await Promise.all( this.getOperationStructArrayPromises ); - const tx = - this.host.contract.populateTransaction.batchCall( - operationStructArray - ); + + const values = operationStructArray + .filter((x) => x.value?.gt(BigNumber.from(0))) + .map((x) => x.value); + + if (values.length > 1) { + throw new SFError({ + type: "BATCH_CALL_ERROR", + message: + "There are multiple values in the batch call. The value can only be forwarded to one receiving operation.", + }); + } + + const tx = this.host.contract.populateTransaction.batchCall( + operationStructArray, + { + value: values?.length > 0 ? values[0] : undefined, + } + ); return new Operation(tx, "UNSUPPORTED"); } diff --git a/packages/sdk-core/src/Operation.ts b/packages/sdk-core/src/Operation.ts index ca91f60140..87302d4d87 100644 --- a/packages/sdk-core/src/Operation.ts +++ b/packages/sdk-core/src/Operation.ts @@ -20,7 +20,9 @@ export type BatchOperationType = | "SUPERTOKEN_UPGRADE" // 101 | "SUPERTOKEN_DOWNGRADE" // 102 | "SUPERFLUID_CALL_AGREEMENT" // 201 - | "CALL_APP_ACTION"; // 202 + | "CALL_APP_ACTION" // 202 + | "SIMPLE_FORWARD_CALL" // 301 + | "ERC2771_FORWARD_CALL"; // 302 /** * Operation Helper Class @@ -164,6 +166,7 @@ export default class Operation { operationType: batchOperationType, target: functionArgs["agreementClass"], data, + value: populatedTransaction.value, }; } @@ -178,14 +181,28 @@ export default class Operation { operationType: batchOperationType, target: functionArgs["app"], data: functionArgs["callData"], + value: populatedTransaction.value, }; } - // Handles remaining ERC20/ERC777/SuperToken Operations + if ( + this.type === "SIMPLE_FORWARD_CALL" || + this.type === "ERC2771_FORWARD_CALL" + ) { + return { + operationType: batchOperationType, + target: populatedTransaction.to, + data: populatedTransaction.data, + value: populatedTransaction.value, + }; + } + + // Handles remaining ERC20/ERC777/SuperToken Operations (including SIMPLE_FORWARD_CALL and ERC2771_FORWARD_CALL) return { operationType: batchOperationType, target: populatedTransaction.to, data: removeSigHashFromCallData(populatedTransaction.data), + value: populatedTransaction.value, }; }; } diff --git a/packages/sdk-core/test/2_operation.test.ts b/packages/sdk-core/test/2_operation.test.ts index 94d800ade2..e3d383987b 100644 --- a/packages/sdk-core/test/2_operation.test.ts +++ b/packages/sdk-core/test/2_operation.test.ts @@ -9,7 +9,7 @@ import { SuperAppTester } from "../typechain-types"; import { SuperAppTester__factory } from "../typechain-types"; const cfaInterface = IConstantFlowAgreementV1__factory.createInterface(); import { TestEnvironment, makeSuite } from "./TestEnvironment"; -import { ethers } from "ethers"; +import { BigNumber, ethers } from "ethers"; import multiplyGasLimit from "../src/multiplyGasLimit"; /** @@ -174,6 +174,33 @@ makeSuite("Operation Tests", (testEnv: TestEnvironment) => { expect(txn.gasLimit).to.equal("500000"); }); + it("Should move ETH value passed from the Overrides", async () => { + const value = BigNumber.from(Number(0.1e18).toString()); + + const beforeBalanceAlice = await testEnv.alice.getBalance(); + const beforeBalanceBob = await testEnv.bob.getBalance(); + + expect(beforeBalanceAlice.gt(value)).to.be.true; + + const operation = testEnv.sdkFramework.operation( + testEnv.alice.populateTransaction({ + to: testEnv.bob.address, + value + }) as Promise, + "SIMPLE_FORWARD_CALL" + ); + + const txn = await operation.exec(testEnv.alice, 2); + const txReceipt = await txn.wait(); + expect(txReceipt.status).to.equal(1); + + const afterBalanceAlice = await testEnv.alice.getBalance(); + const afterBalanceBob = await testEnv.bob.getBalance(); + + expect(afterBalanceBob.sub(beforeBalanceBob)).to.equal(value); + expect(beforeBalanceAlice.sub(afterBalanceAlice).gte(value)).to.be.true; + }); + it("Should throw an error when trying to execute a transaction with faulty callData", async () => { const callData = cfaInterface.encodeFunctionData("createFlow", [ testEnv.wrapperSuperToken.address, diff --git a/packages/sdk-core/test/3_batch_call.test.ts b/packages/sdk-core/test/3_batch_call.test.ts index 740a063061..eb521b5dbc 100644 --- a/packages/sdk-core/test/3_batch_call.test.ts +++ b/packages/sdk-core/test/3_batch_call.test.ts @@ -2,10 +2,12 @@ import { expect } from "chai"; import { AUTHORIZE_FULL_CONTROL, Operation, + TestToken, + TestToken__factory, getPerSecondFlowRateByMonth, toBN, } from "../src"; -import { ethers } from "ethers"; +import { BigNumber, Contract, ethers } from "ethers"; import { createCallAppActionOperation } from "./2_operation.test"; import { TestEnvironment, makeSuite } from "./TestEnvironment"; @@ -408,4 +410,115 @@ makeSuite("Batch Call Tests", (testEnv: TestEnvironment) => { "0" ); }); + + + it("Should be able to execute SimpleForwarder batch call", async () => { + const contract = (new Contract( + testEnv.wrapperSuperToken.underlyingToken.address, + TestToken__factory.abi + ) as TestToken).connect(testEnv.alice); + + const beforeBalance = await contract.balanceOf(testEnv.alice.address); + + const txn1 = contract + .populateTransaction.mint( + testEnv.alice.address, + "100" + ); + const txn2 = contract + .populateTransaction.mint( + testEnv.alice.address, + "200" + ); + + const operation1 = testEnv.sdkFramework.operation( + txn1, + "SIMPLE_FORWARD_CALL" + ); + const operation2 = testEnv.sdkFramework.operation( + txn2, + "SIMPLE_FORWARD_CALL" + ); + + const batchCall = testEnv.sdkFramework.batchCall( + [ + operation1, + operation2 + ] + ); + + const txResponse = await batchCall.exec(testEnv.alice); + const txReceipt = await txResponse.wait(); + expect(txReceipt.status).to.equal(1); + + const afterBalance = await contract.balanceOf(testEnv.alice.address); + + expect(beforeBalance.lt(afterBalance)).to.be.true; + expect(afterBalance.sub(BigNumber.from(300)).eq(beforeBalance)).to.be.true; + }); + + it("Should be able to move ETH value", async () => { + const value = BigNumber.from(Number(0.1e18).toString()); + + const beforeBalanceAlice = await testEnv.alice.getBalance(); + const beforeBalanceBob = await testEnv.bob.getBalance(); + + expect(beforeBalanceAlice.gt(value)).to.be.true; + + const operation = testEnv.sdkFramework.operation( + testEnv.alice.populateTransaction({ + to: testEnv.bob.address, + value, + data: "0x" + }) as Promise, + "SIMPLE_FORWARD_CALL" + ); + + const batchCall = testEnv.sdkFramework.batchCall( + [ + operation + ] + ); + + const txn = await batchCall.exec(testEnv.alice, 2); + + const txReceipt = await txn.wait(); + expect(txReceipt.status).to.equal(1); + + const afterBalanceAlice = await testEnv.alice.getBalance(); + const afterBalanceBob = await testEnv.bob.getBalance(); + + expect(afterBalanceBob.sub(beforeBalanceBob)).to.equal(value); + expect(beforeBalanceAlice.sub(afterBalanceAlice).gte(value)).to.be.true; + }); + + it("Should throw an error when multiple Operations with ETH value", async () => { + const operation1 = testEnv.sdkFramework.operation( + testEnv.alice.populateTransaction({ + to: testEnv.bob.address, + value: BigNumber.from(Number(0.1e18).toString()), + data: "0x" + }) as Promise, + "SIMPLE_FORWARD_CALL" + ); + + const operation2 = testEnv.sdkFramework.operation( + testEnv.alice.populateTransaction({ + to: testEnv.bob.address, + value: BigNumber.from(Number(0.2e18).toString()), + data: "0x" + }) as Promise, + "SIMPLE_FORWARD_CALL" + ); + + const batchCall = testEnv.sdkFramework.batchCall( + [ + operation1, + operation2 + ] + ); + + await expect(batchCall.exec(testEnv.alice, 2)) + .to.be.rejectedWith("multiple values in the batch call"); + }); }); diff --git a/packages/subgraph/package.json b/packages/subgraph/package.json index 7902cee2de..9ae9af6f61 100644 --- a/packages/subgraph/package.json +++ b/packages/subgraph/package.json @@ -5,7 +5,7 @@ "dependencies": { "@graphprotocol/graph-cli": "0.80.1", "@graphprotocol/graph-ts": "0.35.1", - "@superfluid-finance/sdk-core": "0.8.0", + "@superfluid-finance/sdk-core": "0.9.0", "mustache": "4.2.0" }, "devDependencies": { diff --git a/yarn.lock b/yarn.lock index b0787bf07c..584c5423e4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6446,10 +6446,10 @@ browserify-zlib@~0.2.0: dependencies: pako "~1.0.5" -browserify@17.0.0: - version "17.0.0" - resolved "https://registry.yarnpkg.com/browserify/-/browserify-17.0.0.tgz#4c48fed6c02bfa2b51fd3b670fddb805723cdc22" - integrity sha512-SaHqzhku9v/j6XsQMRxPyBrSP3gnwmE27gLJYZgMT2GeK3J0+0toN+MnuNYDfHwVGQfLiMZ7KSNSIXHemy905w== +browserify@^17.0.1: + version "17.0.1" + resolved "https://registry.yarnpkg.com/browserify/-/browserify-17.0.1.tgz#d822fa701431ca94cb405b033bb2551951e5d97d" + integrity sha512-pxhT00W3ylMhCHwG5yfqtZjNnFuX5h2IJdaBfSo4ChaaBsIp9VLrEMQ1bHV+Xr1uLPXuNDDM1GlJkjli0qkRsw== dependencies: JSONStream "^1.0.3" assert "^1.4.0" @@ -6468,7 +6468,7 @@ browserify@17.0.0: duplexer2 "~0.1.2" events "^3.0.0" glob "^7.1.0" - has "^1.0.0" + hasown "^2.0.0" htmlescape "^1.1.0" https-browserify "^1.0.0" inherits "~2.0.1" @@ -11083,7 +11083,7 @@ has-unicode@2.0.1: resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" integrity sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ== -has@^1.0.0, has@^1.0.3: +has@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==