Skip to content

Commit

Permalink
forward ETH value
Browse files Browse the repository at this point in the history
  • Loading branch information
kasparkallas committed Dec 13, 2024
1 parent c888d18 commit f2f8f55
Show file tree
Hide file tree
Showing 4 changed files with 168 additions and 53 deletions.
26 changes: 21 additions & 5 deletions packages/sdk-core/src/BatchCall.ts
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -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<
Expand Down Expand Up @@ -91,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");
}

Expand Down
4 changes: 4 additions & 0 deletions packages/sdk-core/src/Operation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ export default class Operation {
operationType: batchOperationType,
target: functionArgs["agreementClass"],
data,
value: populatedTransaction.value,
};
}

Expand All @@ -180,6 +181,7 @@ export default class Operation {
operationType: batchOperationType,
target: functionArgs["app"],
data: functionArgs["callData"],
value: populatedTransaction.value,
};
}

Expand All @@ -191,6 +193,7 @@ export default class Operation {
operationType: batchOperationType,
target: populatedTransaction.to,
data: populatedTransaction.data,
value: populatedTransaction.value,
};
}

Expand All @@ -199,6 +202,7 @@ export default class Operation {
operationType: batchOperationType,
target: populatedTransaction.to,
data: removeSigHashFromCallData(populatedTransaction.data),
value: populatedTransaction.value,
};
};
}
76 changes: 29 additions & 47 deletions packages/sdk-core/test/2_operation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ import { expect } from "chai";
import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
import { Framework } from "../src/index";
import { getPerSecondFlowRateByMonth } from "../src";
import { ERC20, ERC20__factory, IConstantFlowAgreementV1__factory, TestToken, TestToken__factory } from "../src/typechain-types";
import { IConstantFlowAgreementV1__factory } from "../src/typechain-types";
import Operation from "../src/Operation";
import hre from "hardhat";
import { SuperAppTester } from "../typechain-types";
import { SuperAppTester__factory } from "../typechain-types";
const cfaInterface = IConstantFlowAgreementV1__factory.createInterface();
import { TestEnvironment, makeSuite } from "./TestEnvironment";
import { BigNumber, Contract, ethers } from "ethers";
import { BigNumber, ethers } from "ethers";
import multiplyGasLimit from "../src/multiplyGasLimit";

/**
Expand Down Expand Up @@ -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<ethers.PopulatedTransaction>,
"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,
Expand Down Expand Up @@ -209,51 +236,6 @@ makeSuite("Operation Tests", (testEnv: TestEnvironment) => {
}
});

it("Should be able to execute SimpleForwarder operation from framework.", 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;
});

context(
"Should be able to get and execute a signed transaction",
async () => {
Expand Down
115 changes: 114 additions & 1 deletion packages/sdk-core/test/3_batch_call.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand Down Expand Up @@ -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<ethers.PopulatedTransaction>,
"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.only("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<ethers.PopulatedTransaction>,
"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<ethers.PopulatedTransaction>,
"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");
});
});

0 comments on commit f2f8f55

Please sign in to comment.