From a2ecd2a586824a073a77002c7bf1829f91cddaaf Mon Sep 17 00:00:00 2001 From: martonp Date: Thu, 21 Mar 2024 16:52:40 -0400 Subject: [PATCH] dex/networks/eth: ETH fidelity bonds contract This diff introduces the solidity contract that supports posting fidelity bonds on ETH. --- .../eth/bondcontracts/updatecontract.sh | 47 ++ dex/networks/eth/bondcontracts/v0/.gitignore | 12 + .../eth/bondcontracts/v0/BinRuntimeV0.go | 6 + dex/networks/eth/bondcontracts/v0/contract.go | 622 ++++++++++++++++++ .../bondcontracts/v0/contracts/ETHBondV0.sol | 133 ++++ .../eth/bondcontracts/v0/hardhat.config.ts | 9 + .../eth/bondcontracts/v0/package.json | 9 + .../bondcontracts/v0/test/ETHBondV0.test.ts | 266 ++++++++ .../eth/bondcontracts/v0/tsconfig.json | 36 + dex/testing/eth/harness.sh | 40 ++ 10 files changed, 1180 insertions(+) create mode 100755 dex/networks/eth/bondcontracts/updatecontract.sh create mode 100644 dex/networks/eth/bondcontracts/v0/.gitignore create mode 100644 dex/networks/eth/bondcontracts/v0/BinRuntimeV0.go create mode 100644 dex/networks/eth/bondcontracts/v0/contract.go create mode 100644 dex/networks/eth/bondcontracts/v0/contracts/ETHBondV0.sol create mode 100644 dex/networks/eth/bondcontracts/v0/hardhat.config.ts create mode 100644 dex/networks/eth/bondcontracts/v0/package.json create mode 100644 dex/networks/eth/bondcontracts/v0/test/ETHBondV0.test.ts create mode 100644 dex/networks/eth/bondcontracts/v0/tsconfig.json diff --git a/dex/networks/eth/bondcontracts/updatecontract.sh b/dex/networks/eth/bondcontracts/updatecontract.sh new file mode 100755 index 0000000000..6317b97cd2 --- /dev/null +++ b/dex/networks/eth/bondcontracts/updatecontract.sh @@ -0,0 +1,47 @@ +#!/usr/bin/env bash +# This script does 3 things: +# +# 1. Updates contract.go to reflect updated solidity code. +# 2. Generates the runtime bytecoode. This is used to compare against the runtime +# bytecode on chain in order to verify that the expected contract is deployed. +# 3. Updates the bytecode in the harness test. + +if [ "$#" -ne 1 ] +then + echo "Usage: $0 version" >&2 + exit 1 +fi + +VERSION=$1 +PKG_NAME=v${VERSION} +CONTRACT_NAME=ETHBond +SOLIDITY_FILE=./v${VERSION}/contracts/${CONTRACT_NAME}V${VERSION}.sol +if [ ! -f ${SOLIDITY_FILE} ] +then + echo "${SOLIDITY_FILE} does not exist" >&2 + exit 1 +fi + +mkdir temp + +solc --abi --bin --bin-runtime --overwrite --optimize ${SOLIDITY_FILE} -o ./temp/ +BYTECODE=$(<./temp/${CONTRACT_NAME}.bin-runtime) + +cat > "./${PKG_NAME}/BinRuntimeV${VERSION}.go" < bytes32) with only one commitment, but this + // would allow a troublemaker to post a bond blocking the actual + // account owner from doing so. + mapping(bytes32 => bytes32[]) public bondCommits; + + // senderIsOrigin ensures that this contract cannot be used by other + // contracts, which reduces possible attack vectors. + modifier senderIsOrigin() { + require(tx.origin == msg.sender, "sender != origin"); + _; + } + + // hashBonds creates a hash that commits to the sender and the provided bonds. + function hashBonds(Bond[] memory bonds, address sender) public pure returns (bytes32) { + return keccak256(abi.encode(sender, bonds)); + } + + // createBond creates a new bond for the provided account ID. This should + // be used to create the initial bond for an account. The index of the + // commitment should be retrieved from the BondCreated event, and then used + // when calling updateBonds. + function createBond(bytes32 acctID, Bond calldata bond) external payable senderIsOrigin() { + require(msg.value == bond.value, "insufficient funds"); + Bond[] memory bonds = new Bond[](1); + bonds[0] = bond; + bondCommits[acctID].push(hashBonds(bonds, msg.sender)); + emit BondCreated(acctID, bondCommits[acctID].length - 1, bond); + } + + // updateBond updates the bonds for the provided account ID and index. The + // bonds currently stored for this commitment must be passed in as oldBonds, + // and the new bonds should be passed in as newBonds. + // - The bonds must be sorted by decreasing locktime. + // - The value of oldBonds that are reused in newBonds must be locked for + // the same amount of time or longer. + // - If the total value of new bonds is greater than the total value of the + // old bonds, the difference must be sent as a payment to this contract. + // - If the total value of new bonds is less than the total value of the old + // bonds, the difference will be refunded to the sender, but only if the + // locktimes of the bonds to be refunded has passed. + function updateBonds(bytes32 acctID, Bond[] calldata oldBonds, Bond[] calldata newBonds, uint index) external payable senderIsOrigin() { + emit BondUpdated(acctID, index, oldBonds, newBonds); + + bytes32 commit = bondCommits[acctID][index]; + require(oldBonds.length > 0, "need old bonds"); + require(hashBonds(oldBonds, msg.sender) == commit, "incorrect commit"); + + // Quick path for pure refund. + if (newBonds.length == 0) { + uint256 refund = 0; + for (uint i = 0; i < oldBonds.length; i++) { + require(oldBonds[i].locktime < block.timestamp, "cannot refund unexpired bond"); + refund += oldBonds[i].value; + } + bondCommits[acctID][index] = bytes32(0); + (bool ok, ) = payable(msg.sender).call{value: refund}(""); + require(ok == true, "full refund failed"); + return; + } + + uint64 stamp = newBonds[0].locktime; + uint256 newFunds = 0; + uint256 totalBondsValue = 0; + uint256 oldBondsIndex = 0; + + Bond[] memory bondReserves = oldBonds; + + // Fund new bonds. + for (uint i = 0; i < newBonds.length; i++) { + Bond memory newBond = newBonds[i]; + require(newBond.locktime <= stamp, "bonds not sorted"); + stamp = newBond.locktime; + uint256 toFund = newBond.value; + totalBondsValue += toFund; + + while (oldBondsIndex < bondReserves.length) { + require(bondReserves[oldBondsIndex].locktime <= newBond.locktime, "illegal lock time shortening"); + if (bondReserves[oldBondsIndex].value >= toFund) { + bondReserves[oldBondsIndex].value -= toFund; + toFund = 0; + break; + } else { + toFund -= bondReserves[oldBondsIndex].value; + bondReserves[oldBondsIndex].value = 0; + oldBondsIndex++; + } + } + + newFunds += toFund; + } + + require (msg.value == newFunds, "incorrect amount"); + + bondCommits[acctID][index] = hashBonds(newBonds, msg.sender); + + uint256 unextended = 0; + for (; oldBondsIndex < bondReserves.length; oldBondsIndex++) { + if (bondReserves[oldBondsIndex].value > 0) { + require(bondReserves[oldBondsIndex].locktime < block.timestamp, "unextended bond is not old enough to refund"); + unextended += bondReserves[oldBondsIndex].value; + } + } + + if (unextended > 0) { + (bool ok, ) = payable(msg.sender).call{value: unextended}(""); + require(ok == true, "refund failed"); + } + } +} diff --git a/dex/networks/eth/bondcontracts/v0/hardhat.config.ts b/dex/networks/eth/bondcontracts/v0/hardhat.config.ts new file mode 100644 index 0000000000..34f65511c8 --- /dev/null +++ b/dex/networks/eth/bondcontracts/v0/hardhat.config.ts @@ -0,0 +1,9 @@ +import "@nomiclabs/hardhat-waffle"; +import "@typechain/hardhat"; +import "hardhat-deploy"; +import "solidity-coverage"; +import "@nomiclabs/hardhat-ethers"; + +module.exports = { + solidity: "0.8.18", +}; diff --git a/dex/networks/eth/bondcontracts/v0/package.json b/dex/networks/eth/bondcontracts/v0/package.json new file mode 100644 index 0000000000..42aac4d058 --- /dev/null +++ b/dex/networks/eth/bondcontracts/v0/package.json @@ -0,0 +1,9 @@ +{ + "name": "hardhat-project", + "devDependencies": { + "@nomicfoundation/hardhat-toolbox": "^2.0.2", + "@nomiclabs/hardhat-waffle": "^2.0.5", + "chai": "^4.3.7", + "hardhat-deploy": "^0.11.25" + } +} diff --git a/dex/networks/eth/bondcontracts/v0/test/ETHBondV0.test.ts b/dex/networks/eth/bondcontracts/v0/test/ETHBondV0.test.ts new file mode 100644 index 0000000000..42ec5d1c6d --- /dev/null +++ b/dex/networks/eth/bondcontracts/v0/test/ETHBondV0.test.ts @@ -0,0 +1,266 @@ +import { expect } from "chai"; +import { ethers } from "hardhat"; +import { Contract, Signer } from "ethers"; + +describe("ETHBond", () => { + let ethBond: Contract; + let addr1: Signer; + let addr2: Signer; + + const accountID = ethers.utils.id("account1"); + let bondIDs : string[] = []; + let addrs : Signer[] = []; + + const reset = async () => { + const EthBondFactory = await ethers.getContractFactory("ETHBond"); + const signers = await ethers.getSigners(); + addr1 = signers[0]; + addr2 = signers[1]; + + bondIDs = []; + for (let i = 0; i < 10; i++) { + bondIDs.push(await signers[i + 3].getAddress()); + addrs.push(signers[i + 3]); + } + + ethBond = await EthBondFactory.deploy(); + } + + beforeEach(reset); + + it("should test gas used", async () => { + /* === Gas usage === + + Create 1: 72076 (first creation is higher due to creating the commitment array) + Create 2: 54976 + Create 3: 54976 + + # Old +# New 0 1 2 3 4 5 6 + 1 41143, 42793, 50628, 55233, 59839, 64444, 69050 + 2 45002, 57281, 52524, 60360, 64965, 69571, 74177 + 3 48850, 61829, 67001, 62245, 70080, 74686, 79292 + 4 52697, 66376, 71549, 76722, 71965, 79802, 84408 + 5 56545, 70924, 76097, 81270, 86443, 81687, 89524 + 6 60392, 75472, 80645, 85818, 90992, 96165, 91410 + */ + + const eth = (x : number) : BigInt => { + return ethers.utils.parseEther(x.toString()); + } + + for (let i = 0; i < 3; i++) { + const createTx = await ethBond.connect(addr1).createBond(accountID, {value: eth(1), locktime: 1000}, {value: eth(1)}); + const receipt = await createTx.wait(); + console.log(`Create ${i+1}: ${receipt.gasUsed}`); + } + + const updateGasMatrix = []; + for (let i = 0; i < 7; i++) { + updateGasMatrix[i] = []; + } + + const makeBonds = (numBonds: number) : any => { + const bonds : any = []; + for (let i = 0; i < numBonds; i++) { + bonds.push({value: eth(1), locktime: 1000}); + } + return bonds; + } + + for (let oldBonds = 1; oldBonds < 7; oldBonds++) { + for (let newBonds = 0; newBonds < 7; newBonds++) { + + await reset(); + + const initialBond = {value: eth(1), locktime: 1000}; + await ethBond.connect(addr1).createBond(accountID, initialBond, {value: eth(1)}); + + let opts = undefined; + if (oldBonds > 1) { + await ethBond.connect(addr1).updateBonds(accountID, [initialBond], makeBonds(oldBonds), 0, {value: eth(oldBonds - 1)}); + } else { + await ethBond.connect(addr1).updateBonds(accountID, [initialBond], makeBonds(oldBonds), 0); + } + + opts = undefined; + let updateTx; + if (newBonds > oldBonds) { + updateTx = await ethBond.connect(addr1).updateBonds(accountID, makeBonds(oldBonds), makeBonds(newBonds), 0, {value: eth(newBonds - oldBonds)}); + } else { + updateTx = await ethBond.connect(addr1).updateBonds(accountID, makeBonds(oldBonds), makeBonds(newBonds), 0); + } + const receipt = await updateTx.wait(); + const gas : any = updateGasMatrix[oldBonds] + gas[newBonds] = receipt.gasUsed; + } + } + + // Print gas matrix + for (let i = 0; i < 7; i++) { + console.log(updateGasMatrix[i].join(", ")); + } + }); + + it("should emit events after creation and update", async() => { + const createBond = {value: ethers.utils.parseEther("1"), locktime: 1000}; + const update = [ + {value: ethers.utils.parseEther("1"), locktime: 2000}, + {value: ethers.utils.parseEther("2"), locktime: 1000} + ]; + + const createTx = await ethBond.connect(addr1).createBond(accountID, createBond, { value: createBond.value }); + const createReceipt = await createTx.wait(); + const createEvent = createReceipt.events[0]; + expect(createEvent.event).to.equal("BondCreated"); + expect(createEvent.args.acctID).to.equal(accountID); + expect(createEvent.args.index).to.equal(0); + expect(createEvent.args.bond.value).to.equal(createBond.value); + expect(createEvent.args.bond.locktime).to.equal(createBond.locktime); + + const createTx2 = await ethBond.connect(addr1).createBond(accountID, createBond, { value: createBond.value }); + const createReceipt2 = await createTx2.wait(); + const createEvent2 = createReceipt2.events[0]; + expect(createEvent2.event).to.equal("BondCreated"); + expect(createEvent2.args.acctID).to.equal(accountID); + expect(createEvent2.args.index).to.equal(1); + expect(createEvent2.args.bond.value).to.equal(createBond.value); + expect(createEvent2.args.bond.locktime).to.equal(createBond.locktime); + + const updateTx = await ethBond.connect(addr1).updateBonds(accountID, [createBond], update, 0, {value: ethers.utils.parseEther("2")}); + const updateReceipt = await updateTx.wait(); + const updateEvent = updateReceipt.events[0]; + expect(updateEvent.event).to.equal("BondUpdated"); + expect(updateEvent.args.acctID).to.equal(accountID); + expect(updateEvent.args.index).to.equal(0); + expect(updateEvent.args.oldBonds[0].value).to.equal(createBond.value); + expect(updateEvent.args.oldBonds[0].locktime).to.equal(createBond.locktime); + expect(updateEvent.args.newBonds[0].value).to.equal(update[0].value); + expect(updateEvent.args.newBonds[0].locktime).to.equal(update[0].locktime); + expect(updateEvent.args.newBonds[1].value).to.equal(update[1].value); + expect(updateEvent.args.newBonds[1].locktime).to.equal(update[1].locktime); + }); + + it("should update bonds if correct index", async () => { + const createBond1 = {value: ethers.utils.parseEther("1"), locktime: 1000}; + const createBond2 = {value: ethers.utils.parseEther("2"), locktime: 2000}; + + await ethBond.connect(addr1).createBond(accountID, createBond1, { value: createBond1.value }); + await ethBond.connect(addr1).createBond(accountID, createBond2, { value: createBond2.value }); + + await expect(ethBond.connect(addr1).updateBonds(accountID, [createBond1], [], 1)).to.be.revertedWith("incorrect commit"); + await expect(ethBond.connect(addr1).updateBonds(accountID, [createBond2], [], 0)).to.be.revertedWith("incorrect commit"); + + const balanceBefore = await addr1.getBalance(); + const updateTx1 = await ethBond.connect(addr1).updateBonds(accountID, [createBond1], [], 0); + const updateTx2 = await ethBond.connect(addr1).updateBonds(accountID, [createBond2], [], 1); + const tx1Receipt = await updateTx1.wait(); + const tx2Receipt = await updateTx2.wait(); + const totalFee = tx1Receipt.gasUsed * tx1Receipt.effectiveGasPrice + tx2Receipt.gasUsed * tx2Receipt.effectiveGasPrice; + const balanceAfter = await addr1.getBalance(); + expect(balanceAfter.sub(balanceBefore).add(totalFee).toString()).to.equal(ethers.utils.parseEther("3").toString()); + }); + + it("should refund all bonds", async () => { + const createBond = {value: ethers.utils.parseEther("1"), locktime: 1000}; + const update1 = [ + {value: ethers.utils.parseEther("1"), locktime: 2000}, + {value: ethers.utils.parseEther("2"), locktime: 1000} + ]; + const update2 : any = []; + await ethBond.connect(addr1).createBond(accountID, createBond, { value: createBond.value }); + await ethBond.connect(addr1).updateBonds(accountID, [createBond], update1, 0, { value: ethers.utils.parseEther("2") }); + const balanceBefore = await addr1.getBalance(); + const updateTx = await ethBond.connect(addr1).updateBonds(accountID, update1, update2, 0); + const updateReceipt = await updateTx.wait(); + const gasPrice = updateReceipt.gasUsed * updateReceipt.effectiveGasPrice; + const balanceAfter = await addr1.getBalance(); + expect(balanceAfter.sub(balanceBefore).add(gasPrice).toString()).to.equal(ethers.utils.parseEther("3").toString()); + }); + + it("should extend bonds, consolidate 2 into one", async () => { + const createBond = {value: ethers.utils.parseEther("1"), locktime: 1000}; + const update1 = [ + {value: ethers.utils.parseEther("1"), locktime: 2000}, + {value: ethers.utils.parseEther("2"), locktime: 1000} + ]; + const update2 = [ + {value: ethers.utils.parseEther("3"), locktime: 4000} + ]; + await ethBond.connect(addr1).createBond(accountID, createBond, { value: createBond.value }); + await ethBond.connect(addr1).updateBonds(accountID, [createBond], update1, 0, { value: ethers.utils.parseEther("2") }); + await ethBond.connect(addr1).updateBonds(accountID, update1, update2, 0); + }); + + it("should extend bonds, add additional value", async () => { + const createBond = {value: ethers.utils.parseEther("1"), locktime: 1000}; + const update1 = [ + {value: ethers.utils.parseEther("1"), locktime: 2000}, + {value: ethers.utils.parseEther("2"), locktime: 1000} + ]; + const update2 = [ + {value: ethers.utils.parseEther("4"), locktime: 4000} + ]; + await ethBond.connect(addr1).createBond(accountID, createBond, { value: createBond.value }); + await ethBond.connect(addr1).updateBonds(accountID, [createBond], update1, 0, { value: ethers.utils.parseEther("2") }); + await ethBond.connect(addr1).updateBonds(accountID, update1, update2, 0, { value: ethers.utils.parseEther("1") }); + }); + + it("should error if extending with incorrect amount", async () => { + const createBond = {value: ethers.utils.parseEther("1"), locktime: 1000}; + const update1 = [ + {value: ethers.utils.parseEther("1"), locktime: 2000}, + {value: ethers.utils.parseEther("2"), locktime: 1000} + ]; + const update2 = [ + {value: ethers.utils.parseEther("4"), locktime: 4000} + ]; + await ethBond.connect(addr1).createBond(accountID, createBond, { value: createBond.value }); + await ethBond.connect(addr1).updateBonds(accountID, [createBond], update1, 0, { value: ethers.utils.parseEther("2") }); + await expect(ethBond.connect(addr1).updateBonds(accountID, update1, update2, 0, { value: ethers.utils.parseEther("0.5") })).to.be.revertedWith("incorrect amount"); + }); + + it("should extend bonds, extend / refund", async () => { + const createBond = {value: ethers.utils.parseEther("1"), locktime: 1000}; + const update1 = [ + {value: ethers.utils.parseEther("1"), locktime: 2000}, + {value: ethers.utils.parseEther("2"), locktime: 1000} + ]; + const update2 = [ + {value: ethers.utils.parseEther("1.5"), locktime: 4000} + ]; + + await ethBond.connect(addr1).createBond(accountID, createBond, { value: createBond.value }); + await ethBond.connect(addr1).updateBonds(accountID, [createBond], update1, 0, { value: ethers.utils.parseEther("2") }); + const balanceBefore = await addr1.getBalance(); + const updateTx = await ethBond.connect(addr1).updateBonds(accountID, update1, update2, 0); + const updateReceipt = await updateTx.wait(); + const gasPrice = updateReceipt.gasUsed * updateReceipt.effectiveGasPrice; + const balanceAfter = await addr1.getBalance(); + expect(balanceAfter.sub(balanceBefore).add(gasPrice).toString()).to.equal(ethers.utils.parseEther("1.5").toString()); + }); + + it("should error if shortening locktime", async() => { + const createBond = {value: ethers.utils.parseEther("1"), locktime: 1000}; + const update1 = [ + {value: ethers.utils.parseEther("2"), locktime: 2000}, + {value: ethers.utils.parseEther("1"), locktime: 1000} + ] + const update2 = [ + {value: ethers.utils.parseEther("3"), locktime: 1999} + ]; + await ethBond.connect(addr1).createBond(accountID, createBond, { value: createBond.value }); + await ethBond.connect(addr1).updateBonds(accountID, [createBond], update1, 0, { value: ethers.utils.parseEther("2") }); + await expect(ethBond.connect(addr1).updateBonds(accountID, update1, update2, 0)).to.be.revertedWith("illegal lock time shortening"); + }); + + it("should error if new bonds unsorted", async() => { + const oldBond = {value: ethers.utils.parseEther("1"), locktime: 1000}; + const newBonds = [ + {value: ethers.utils.parseEther("1"), locktime: 1000}, + {value: ethers.utils.parseEther("1"), locktime: 2000} + ]; + await ethBond.connect(addr1).createBond(accountID, oldBond, { value: oldBond.value }); + await expect(ethBond.connect(addr1).updateBonds(accountID, [oldBond], newBonds, 0, {value: ethers.utils.parseEther("1")})).to.be.revertedWith("bonds not sorted"); + }); +}); diff --git a/dex/networks/eth/bondcontracts/v0/tsconfig.json b/dex/networks/eth/bondcontracts/v0/tsconfig.json new file mode 100644 index 0000000000..5bf6feef0d --- /dev/null +++ b/dex/networks/eth/bondcontracts/v0/tsconfig.json @@ -0,0 +1,36 @@ +{ + "compilerOptions": { + "target": "ES2018", + "module": "commonjs", + "strict": true, + "esModuleInterop": true, + "moduleResolution": "node", + "resolveJsonModule": true, + "forceConsistentCasingInFileNames": true, + "composite": true, + "skipLibCheck": true, + "typeRoots": ["node_modules/@types", "node_modules", "types"], + "types": ["mocha", "node", "hardhat-deploy"], + "lib": ["ES2018", "DOM"], + "baseUrl": ".", + "paths": { + "@app/*": ["./app/*"], + "@test/*": ["./test/*"], + "@types/*": ["./types/*"] + } + }, + "include": ["./test/**/*.ts", "./app/**/*.ts", "./types/**/*.ts"], + "exclude": ["node_modules", "coverage"], + "references": [ + { + "path": "./app" + }, + { + "path": "./test" + }, + { + "path": "./types" + } + ] + } + \ No newline at end of file diff --git a/dex/testing/eth/harness.sh b/dex/testing/eth/harness.sh index c4ff6eb141..2501887587 100755 --- a/dex/testing/eth/harness.sh +++ b/dex/testing/eth/harness.sh @@ -44,6 +44,7 @@ ETH_SWAP_V0="608060405234801561001057600080fd5b50610b7a806100206000396000f3fe608 ERC20_SWAP_V0="60a060405234801561001057600080fd5b50604051610e92380380610e9283398101604081905261002f91610040565b6001600160a01b0316608052610070565b60006020828403121561005257600080fd5b81516001600160a01b038116811461006957600080fd5b9392505050565b608051610df361009f6000396000818160c50152818161029b0152818161066b01526109f30152610df36000f3fe608060405234801561001057600080fd5b506004361061007d5760003560e01c8063a8793f941161005b578063a8793f94146100ff578063d0f761c014610112578063eb84e7f214610135578063f4fd17f9146101a457600080fd5b80637249fbb61461008257806376467cbd146100975780638c8e8fee146100c0575b600080fd5b610095610090366004610ac8565b6101b7565b005b6100aa6100a5366004610ac8565b610376565b6040516100b79190610b19565b60405180910390f35b6100e77f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016100b7565b61009561010d366004610b7e565b610451565b610125610120366004610ac8565b61074c565b60405190151581526020016100b7565b610191610143366004610ac8565b60006020819052908152604090208054600182015460028301546003840154600485015460059095015493949293919290916001600160a01b0391821691811690600160a01b900460ff1687565b6040516100b79796959493929190610bf3565b6100956101b2366004610c3f565b6107ac565b3233146101df5760405162461bcd60e51b81526004016101d690610ca2565b60405180910390fd5b6101e88161074c565b6102255760405162461bcd60e51b815260206004820152600e60248201526d6e6f7420726566756e6461626c6560901b60448201526064016101d6565b60008181526020818152604080832060058101805460ff60a01b1916600360a01b17905560018101548251336024820152604480820192909252835180820390920182526064018352928301805163a9059cbb60e01b6001600160e01b0390911617905290519092916060916001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016916102c591610ccc565b6000604051808303816000865af19150503d8060008114610302576040519150601f19603f3d011682016040523d82523d6000602084013e610307565b606091505b5090925090508180156103325750805115806103325750808060200190518101906103329190610cfb565b6103705760405162461bcd60e51b815260206004820152600f60248201526e1d1c985b9cd9995c8819985a5b1959608a1b60448201526064016101d6565b50505050565b6103b36040805160e081018252600080825260208201819052918101829052606081018290526080810182905260a081018290529060c082015290565b60008281526020818152604091829020825160e08101845281548152600182015492810192909252600281015492820192909252600380830154606083015260048301546001600160a01b039081166080840152600584015490811660a084015291929160c0840191600160a01b90910460ff169081111561043757610437610ae1565b600381111561044857610448610ae1565b90525092915050565b3233146104705760405162461bcd60e51b81526004016101d690610ca2565b6000805b82811015610615573684848381811061048f5761048f610d1d565b9050608002019050600080600083602001358152602001908152602001600020905060008260600135116104ed5760405162461bcd60e51b81526020600482015260056024820152640c081d985b60da1b60448201526064016101d6565b813561052f5760405162461bcd60e51b815260206004820152601160248201527003020726566756e6454696d657374616d7607c1b60448201526064016101d6565b60006005820154600160a01b900460ff16600381111561055157610551610ae1565b146105905760405162461bcd60e51b815260206004820152600f60248201526e0c8eae040e6cac6e4cae840d0c2e6d608b1b60448201526064016101d6565b436002820155813560038201556004810180546001600160a01b031916331790556105c16060830160408401610d33565b6005820180546060850135600185018190556001600160a01b03939093166001600160a81b031990911617600160a01b1790556105fe9085610d72565b93505050808061060d90610d8b565b915050610474565b5060408051336024820152306044820152606480820184905282518083039091018152608490910182526020810180516001600160e01b03166323b872dd60e01b17905290516000916060916001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169161069591610ccc565b6000604051808303816000865af19150503d80600081146106d2576040519150601f19603f3d011682016040523d82523d6000602084013e6106d7565b606091505b5090925090508180156107025750805115806107025750808060200190518101906107029190610cfb565b6107455760405162461bcd60e51b81526020600482015260146024820152731d1c985b9cd9995c88199c9bdb4819985a5b195960621b60448201526064016101d6565b5050505050565b600081815260208190526040812060016005820154600160a01b900460ff16600381111561077c5761077c610ae1565b148015610795575060048101546001600160a01b031633145b80156107a5575080600301544210155b9392505050565b3233146107cb5760405162461bcd60e51b81526004016101d690610ca2565b6000805b828110156109a357368484838181106107ea576107ea610d1d565b6020604091820293909301838101356000908152938490529220919250600190506005820154600160a01b900460ff16600381111561082b5761082b610ae1565b146108645760405162461bcd60e51b815260206004820152600960248201526862616420737461746560b81b60448201526064016101d6565b60058101546001600160a01b031633146108b25760405162461bcd60e51b815260206004820152600f60248201526e189859081c185c9d1a58da5c185b9d608a1b60448201526064016101d6565b8160200135600283600001356040516020016108d091815260200190565b60408051601f19818403018152908290526108ea91610ccc565b602060405180830381855afa158015610907573d6000803e3d6000fd5b5050506040513d601f19601f8201168201806040525081019061092a9190610da4565b146109645760405162461bcd60e51b815260206004820152600a602482015269189859081cd958dc995d60b21b60448201526064016101d6565b60058101805460ff60a01b1916600160a11b17905581358155600181015461098c9085610d72565b93505050808061099b90610d8b565b9150506107cf565b5060408051336024820152604480820184905282518083039091018152606490910182526020810180516001600160e01b031663a9059cbb60e01b17905290516000916060916001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001691610a1d91610ccc565b6000604051808303816000865af19150503d8060008114610a5a576040519150601f19603f3d011682016040523d82523d6000602084013e610a5f565b606091505b509092509050818015610a8a575080511580610a8a575080806020019051810190610a8a9190610cfb565b6107455760405162461bcd60e51b815260206004820152600f60248201526e1d1c985b9cd9995c8819985a5b1959608a1b60448201526064016101d6565b600060208284031215610ada57600080fd5b5035919050565b634e487b7160e01b600052602160045260246000fd5b60048110610b1557634e487b7160e01b600052602160045260246000fd5b9052565b600060e08201905082518252602083015160208301526040830151604083015260608301516060830152608083015160018060a01b0380821660808501528060a08601511660a0850152505060c0830151610b7760c0840182610af7565b5092915050565b60008060208385031215610b9157600080fd5b823567ffffffffffffffff80821115610ba957600080fd5b818501915085601f830112610bbd57600080fd5b813581811115610bcc57600080fd5b8660208260071b8501011115610be157600080fd5b60209290920196919550909350505050565b8781526020810187905260408101869052606081018590526001600160a01b038481166080830152831660a082015260e08101610c3360c0830184610af7565b98975050505050505050565b60008060208385031215610c5257600080fd5b823567ffffffffffffffff80821115610c6a57600080fd5b818501915085601f830112610c7e57600080fd5b813581811115610c8d57600080fd5b8660208260061b8501011115610be157600080fd5b60208082526010908201526f39b2b73232b910109e9037b934b3b4b760811b604082015260600190565b6000825160005b81811015610ced5760208186018101518583015201610cd3565b506000920191825250919050565b600060208284031215610d0d57600080fd5b815180151581146107a557600080fd5b634e487b7160e01b600052603260045260246000fd5b600060208284031215610d4557600080fd5b81356001600160a01b03811681146107a557600080fd5b634e487b7160e01b600052601160045260246000fd5b80820180821115610d8557610d85610d5c565b92915050565b600060018201610d9d57610d9d610d5c565b5060010190565b600060208284031215610db657600080fd5b505191905056fea2646970667358221220a055a4890a5ecf3876dbee91dfbeb46ba11b5f7c09b6d935173932d93f8fb92264736f6c63430008120033" TEST_TOKEN="608060405234801561001057600080fd5b506040805180820190915260098152682a32b9ba2a37b5b2b760b91b602082015260039061003e90826101ad565b506040805180820190915260038152621514d560ea1b602082015260049061006690826101ad565b506909513ea9de0243800000600255600060208190526902544faa778090e000007f7d4921c2bc32c0110a31d16f4efb43c7a1228f1df7af765f608241dee5c62ebc8190557f59603491850c7d11499afe95b334ccfd92b48b36a15df31ef59ff5813fe3708281905573d12ab7cf72ccf1f3882ec99ddc53cd415635c3be9091527f5bd8dfce2dbb581d0922a094c40bab2f7d2f0ea9aaf275bf0fcc0f027a2ff91d5561026c565b634e487b7160e01b600052604160045260246000fd5b600181811c9082168061013857607f821691505b60208210810361015857634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156101a857600081815260208120601f850160051c810160208610156101855750805b601f850160051c820191505b818110156101a457828155600101610191565b5050505b505050565b81516001600160401b038111156101c6576101c661010e565b6101da816101d48454610124565b8461015e565b602080601f83116001811461020f57600084156101f75750858301515b600019600386901b1c1916600185901b1785556101a4565b600085815260208120601f198616915b8281101561023e5788860151825594840194600190910190840161021f565b508582101561025c5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b6107c08061027b6000396000f3fe608060405234801561001057600080fd5b50600436106100a95760003560e01c806370a082311161007157806370a08231146101235780638ba4cc3c1461014c57806395d89b4114610161578063a9059cbb14610169578063ce714b511461017c578063dd62ed3e1461018f57600080fd5b806306fdde03146100ae578063095ea7b3146100cc57806318160ddd146100ef57806323b872dd14610101578063313ce56714610114575b600080fd5b6100b66101c8565b6040516100c3919061060a565b60405180910390f35b6100df6100da366004610674565b61025a565b60405190151581526020016100c3565b6002545b6040519081526020016100c3565b6100df61010f36600461069e565b610271565b604051601281526020016100c3565b6100f36101313660046106da565b6001600160a01b031660009081526020819052604090205490565b61015f61015a366004610674565b610320565b005b6100b6610368565b6100df610177366004610674565b610377565b6100df61018a36600461069e565b610384565b6100f361019d3660046106fc565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b6060600380546101d79061072f565b80601f01602080910402602001604051908101604052809291908181526020018280546102039061072f565b80156102505780601f1061022557610100808354040283529160200191610250565b820191906000526020600020905b81548152906001019060200180831161023357829003601f168201915b5050505050905090565b600061026733848461039b565b5060015b92915050565b600061027e84848461048a565b6001600160a01b0384166000908152600160209081526040808320338452909152902054828110156103085760405162461bcd60e51b815260206004820152602860248201527f45524332303a207472616e7366657220616d6f756e74206578636565647320616044820152676c6c6f77616e636560c01b60648201526084015b60405180910390fd5b610315853385840361039b565b506001949350505050565b80600260008282546103329190610769565b90915550506001600160a01b0382166000908152602081905260408120805483929061035f908490610769565b90915550505050565b6060600480546101d79061072f565b600061026733848461048a565b600061039184848461039b565b5060019392505050565b6001600160a01b0383166103fd5760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b60648201526084016102ff565b6001600160a01b03821661045e5760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b60648201526084016102ff565b6001600160a01b0392831660009081526001602090815260408083209490951682529290925291902055565b6001600160a01b0383166104ee5760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b60648201526084016102ff565b6001600160a01b0382166105505760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b60648201526084016102ff565b6001600160a01b038316600090815260208190526040902054818110156105c85760405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b60648201526084016102ff565b6001600160a01b038085166000908152602081905260408082208585039055918516815290812080548492906105ff908490610769565b909155505050505050565b600060208083528351808285015260005b818110156106375785810183015185820160400152820161061b565b506000604082860101526040601f19601f8301168501019250505092915050565b80356001600160a01b038116811461066f57600080fd5b919050565b6000806040838503121561068757600080fd5b61069083610658565b946020939093013593505050565b6000806000606084860312156106b357600080fd5b6106bc84610658565b92506106ca60208501610658565b9150604084013590509250925092565b6000602082840312156106ec57600080fd5b6106f582610658565b9392505050565b6000806040838503121561070f57600080fd5b61071883610658565b915061072660208401610658565b90509250929050565b600181811c9082168061074357607f821691505b60208210810361076357634e487b7160e01b600052602260045260246000fd5b50919050565b8082018082111561026b57634e487b7160e01b600052601160045260246000fdfea2646970667358221220e48184f15ae575a1ab3966580821ddaff3654cd0137084d70ceeeb4d906b3c5364736f6c63430008120033" MULTIBALANCE_BIN="608060405234801561001057600080fd5b50610483806100206000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c8063d3e5ca8714610030575b600080fd5b61004361003e3660046102a5565b610059565b604051610050919061032b565b60405180910390f35b60606000610068836001610385565b67ffffffffffffffff8111156100805761008061039e565b6040519080825280602002602001820160405280156100a9578160200160208202803683370190505b509050846001600160a01b031631816000815181106100ca576100ca6103b4565b60200260200101818152505060005b838110156102805760008585838181106100f5576100f56103b4565b905060200201602081019061010a91906103ca565b905060006060826001600160a01b03167f70a08231b98ef4ca268c9cc3f6b4590e4bfec28280db06bb5d45e689f2a360be8a60405160240161015b91906001600160a01b0391909116815260200190565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b031990941693909317909252905161019991906103ec565b600060405180830381855afa9150503d80600081146101d4576040519150601f19603f3d011682016040523d82523d6000602084013e6101d9565b606091505b509092509050816102285760405162461bcd60e51b815260206004820152601560248201527418985b185b98d953d98818d85b1b0819985a5b1959605a1b604482015260640160405180910390fd5b60008180602001905181019061023e919061041b565b9050808661024d876001610385565b8151811061025d5761025d6103b4565b60200260200101818152505050505050808061027890610434565b9150506100d9565b50949350505050565b80356001600160a01b03811681146102a057600080fd5b919050565b6000806000604084860312156102ba57600080fd5b6102c384610289565b9250602084013567ffffffffffffffff808211156102e057600080fd5b818601915086601f8301126102f457600080fd5b81358181111561030357600080fd5b8760208260051b850101111561031857600080fd5b6020830194508093505050509250925092565b6020808252825182820181905260009190848201906040850190845b8181101561036357835183529284019291840191600101610347565b50909695505050505050565b634e487b7160e01b600052601160045260246000fd5b808201808211156103985761039861036f565b92915050565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b6000602082840312156103dc57600080fd5b6103e582610289565b9392505050565b6000825160005b8181101561040d57602081860181015185830152016103f3565b506000920191825250919050565b60006020828403121561042d57600080fd5b5051919050565b6000600182016104465761044661036f565b506001019056fea26469706673582212207074e3e189a2692e2841f7943a9de24bcdbca943ee6bfcbd83a2fa6e43ec497b64736f6c63430008120033" +BOND_V0="608060405234801561001057600080fd5b50610f5b806100206000396000f3fe60806040526004361061003f5760003560e01c8063145bc8c01461004457806344d961cc1461007657806388cefcf91461008b578063c34db360146100ab575b600080fd5b34801561005057600080fd5b5061006461005f366004610b8b565b6100be565b60405190815260200160405180910390f35b610089610084366004610c4d565b6100f2565b005b34801561009757600080fd5b506100646100a6366004610c85565b610274565b6100896100b9366004610cf2565b6102a5565b600081836040516020016100d3929190610d73565b6040516020818303038152906040528051906020012090505b92915050565b3233146101395760405162461bcd60e51b815260206004820152601060248201526f39b2b73232b910109e9037b934b3b4b760811b60448201526064015b60405180910390fd5b3481351461017e5760405162461bcd60e51b8152602060048201526012602482015271696e73756666696369656e742066756e647360701b6044820152606401610130565b604080516001808252818301909252600091816020015b60408051808201909152600080825260208201528152602001906001900390816101955790505090506101cd36839003830183610ddc565b816000815181106101e0576101e0610dff565b602002602001018190525060008084815260200190815260200160002061020782336100be565b81546001818101845560009384526020808520909201929092558583528290526040909120546102379190610e2b565b837fccf60a1e2943f56dc23571a8ec5ebf81752de8f7f9a362f0a87ed78d06ce9c60846040516102679190610e60565b60405180910390a3505050565b6000602052816000526040600020818154811061029057600080fd5b90600052602060002001600091509150505481565b3233146102e75760405162461bcd60e51b815260206004820152601060248201526f39b2b73232b910109e9037b934b3b4b760811b6044820152606401610130565b80867f775bfb8f27aeb5ee1199727eed7c9db5bd83dc1de5abd12c71fbedb9778798cf8787878760405161031e9493929190610eac565b60405180910390a3600086815260208190526040812080548390811061034657610346610dff565b6000918252602090912001549050846103925760405162461bcd60e51b815260206004820152600e60248201526d6e656564206f6c6420626f6e647360901b6044820152606401610130565b806103ef8787808060200260200160405190810160405280939291908181526020016000905b828210156103e4576103d560408302860136819003810190610ddc565b815260200190600101906103b8565b5050505050336100be565b1461042f5760405162461bcd60e51b815260206004820152601060248201526f1a5b98dbdc9c9958dd0818dbdb5b5a5d60821b6044820152606401610130565b60008390036105c5576000805b868110156104fe574288888381811061045757610457610dff565b905060400201602001602081019061046f9190610ede565b6001600160401b0316106104c55760405162461bcd60e51b815260206004820152601c60248201527f63616e6e6f7420726566756e6420756e6578706972656420626f6e64000000006044820152606401610130565b8787828181106104d7576104d7610dff565b6104ea9260409091020135905083610ef9565b9150806104f681610f0c565b91505061043c565b50600088815260208190526040812080548590811061051f5761051f610dff565b6000918252602082200191909155604051339083908381818185875af1925050503d806000811461056c576040519150601f19603f3d011682016040523d82523d6000602084013e610571565b606091505b50909150506001811515146105bd5760405162461bcd60e51b8152602060048201526012602482015271199d5b1b081c99599d5b990819985a5b195960721b6044820152606401610130565b505050610ab4565b6000848460008181106105da576105da610dff565b90506040020160200160208101906105f29190610ede565b90506000806000808a8a808060200260200160405190810160405280939291908181526020016000905b828210156106485761063960408302860136819003810190610ddc565b8152602001906001019061061c565b5050505050905060005b8881101561085b5760008a8a8381811061066e5761066e610dff565b9050604002018036038101906106849190610ddc565b9050866001600160401b031681602001516001600160401b031611156106df5760405162461bcd60e51b815260206004820152601060248201526f189bdb991cc81b9bdd081cdbdc9d195960821b6044820152606401610130565b602081015181519097506106f38187610ef9565b95505b835185101561083a5781602001516001600160401b031684868151811061071f5761071f610dff565b6020026020010151602001516001600160401b031611156107825760405162461bcd60e51b815260206004820152601c60248201527f696c6c6567616c206c6f636b2074696d652073686f7274656e696e67000000006044820152606401610130565b8084868151811061079557610795610dff565b602002602001015160000151106107dd57808486815181106107b9576107b9610dff565b60200260200101516000018181516107d19190610e2b565b9052506000905061083a565b8385815181106107ef576107ef610dff565b602002602001015160000151816108069190610e2b565b9050600084868151811061081c5761081c610dff565b6020908102919091010151528461083281610f0c565b9550506106f6565b6108448188610ef9565b96505050808061085390610f0c565b915050610652565b5083341461089e5760405162461bcd60e51b815260206004820152601060248201526f1a5b98dbdc9c9958dd08185b5bdd5b9d60821b6044820152606401610130565b6108ef8989808060200260200160405190810160405280939291908181526020016000905b828210156103e4576108e060408302860136819003810190610ddc565b815260200190600101906108c3565b60008d815260208190526040902080548990811061090f5761090f610dff565b906000526020600020018190555060005b8151831015610a1657600082848151811061093d5761093d610dff565b6020026020010151600001511115610a04574282848151811061096257610962610dff565b6020026020010151602001516001600160401b0316106109d85760405162461bcd60e51b815260206004820152602b60248201527f756e657874656e64656420626f6e64206973206e6f74206f6c6420656e6f756760448201526a1a081d1bc81c99599d5b9960aa1b6064820152608401610130565b8183815181106109ea576109ea610dff565b60200260200101516000015181610a019190610ef9565b90505b82610a0e81610f0c565b935050610920565b8015610aac57604051600090339083908381818185875af1925050503d8060008114610a5e576040519150601f19603f3d011682016040523d82523d6000602084013e610a63565b606091505b5090915050600181151514610aaa5760405162461bcd60e51b815260206004820152600d60248201526c1c99599d5b990819985a5b1959609a1b6044820152606401610130565b505b505050505050505b505050505050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b0381118282101715610afa57610afa610abc565b604052919050565b80356001600160401b0381168114610b1957600080fd5b919050565b600060408284031215610b3057600080fd5b604051604081018181106001600160401b0382111715610b5257610b52610abc565b60405282358152905080610b6860208401610b02565b60208201525092915050565b80356001600160a01b0381168114610b1957600080fd5b6000806040808486031215610b9f57600080fd5b83356001600160401b0380821115610bb657600080fd5b818601915086601f830112610bca57600080fd5b8135602082821115610bde57610bde610abc565b610bec818360051b01610ad2565b828152818101935060069290921b840181019189831115610c0c57600080fd5b938101935b82851015610c3257610c238a86610b1e565b84529385019392810192610c11565b9650610c3f888201610b74565b955050505050509250929050565b6000808284036060811215610c6157600080fd5b833592506040601f1982011215610c7757600080fd5b506020830190509250929050565b60008060408385031215610c9857600080fd5b50508035926020909101359150565b60008083601f840112610cb957600080fd5b5081356001600160401b03811115610cd057600080fd5b6020830191508360208260061b8501011115610ceb57600080fd5b9250929050565b60008060008060008060808789031215610d0b57600080fd5b8635955060208701356001600160401b0380821115610d2957600080fd5b610d358a838b01610ca7565b90975095506040890135915080821115610d4e57600080fd5b50610d5b89828a01610ca7565b979a9699509497949695606090950135949350505050565b6001600160a01b038316815260406020808301829052835183830181905260009291858101916060860190855b81811015610dce578451805184528401516001600160401b0316848401529383019391850191600101610da0565b509098975050505050505050565b600060408284031215610dee57600080fd5b610df88383610b1e565b9392505050565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b818103818111156100ec576100ec610e15565b803582526001600160401b03610e5660208301610b02565b1660208301525050565b604081016100ec8284610e3e565b81835260208301925060008160005b84811015610ea257610e8f8683610e3e565b6040958601959190910190600101610e7d565b5093949350505050565b604081526000610ec0604083018688610e6e565b8281036020840152610ed3818587610e6e565b979650505050505050565b600060208284031215610ef057600080fd5b610df882610b02565b808201808211156100ec576100ec610e15565b600060018201610f1e57610f1e610e15565b506001019056fea2646970667358221220af04925865e9f8c11251aaba57556ae3659c84311c01a161f4474ba5d6f478d464736f6c63430008120033" # PASSWORD is the password used to unlock all accounts/wallets/addresses. PASSWORD="abc" @@ -102,6 +103,9 @@ cat > "${NODES_ROOT}/genesis.json" < "${NODES_ROOT}/test_token_contract_address.txt" < "${NODES_ROOT}/eth_bond_contract_address.txt" < "${NODES_ROOT}/harness-ctl/loadTestToken.js" < "${NODES_ROOT}/harness-ctl/loadBondContract.js" < { + if (el.stateMutability === "view") { + // the constant field was deprecated and the output from solc no + // longer contains it, but the geth console version of web3 still + // seems to require it. + el.constant = true; + } + }) + var contract = web3.eth.contract(bondABI) + var bondContract = contract.at('${ETH_BOND_CONTRACT_ADDR}') + web3.eth.defaultAccount = web3.eth.accounts.length > 1 ? web3.eth.accounts[1] : web3.eth.accounts[0] + personal.unlockAccount(web3.eth.defaultAccount, '${PASSWORD}') +EOF + +cat > "${NODES_ROOT}/harness-ctl/alphaWithBonds.sh" <