From 929fca9c14e6b8ad95ee9c886ccf10ed6f0c9d0d Mon Sep 17 00:00:00 2001 From: Brian Stafford Date: Fri, 14 Jul 2023 07:46:40 -0500 Subject: [PATCH] eth v1 contract Implements the version 1 contracts for ethereum and tokens. Based on feedback in #1426, everything is now encoded in the "contract data". This "contract data", is the msgjson.Init.Contract -> msgjson.Audit.Contract -> MatchMetaData.Proof.CounterContract, AuditInfo.Contract -> Redemption.Spends.Contract. A few new terms are introduced to differentiate various encodings and data sets. The aforementioned contract data did encode a version and a secret hash. It now encodes a version and a "locator", which is a []byte whose length and content depend on the version. For version 0, the locator is still just the secretHash[:]. For v1, the locator encodes all of the immutable data that defines the swap. This immutable data is now collected in something called a "vector" (dexeth.SwapVector). For version 0, some vector data is stored on-chain indexed by the secret hash. For version 1, all vector data is encoded in the locator. I've also made an effort to standardize the use of status/step, and eliminated the use of ambiguous "ver" variables throughout. A "status" is now the collection of mutable contract data: the step, the init block height, and the secret. The status and vector collectively fully characterize the swap. client/asset/eth: New contractV1 and tokenContractorV1 interfaces. To avoid duplication, the ERC20 parts of the tokenContractors are separated into a new type erc20Contractor that is embedded by both versions. Getters for status and vector are added in place of the old method "swap". assetWallet and embedding types are updated to work with the new version-dependent locators and the status and vector model. dex/networks/{eth,erc20}: New contracts added. New methods for dealing with locators. Simnet entries added for eth and dextt.eth in the ContractAddresses and Tokens maps. txDataHandler interace is replaced with versioned package-level functions. server/asset/eth: Server is fully switched to version 1. No option to use version 0. Translation to new version was straightforward, with one notable difference that we can no longer get a block height from the contract once the swap is redeemed. --- .gitignore | 1 + client/asset/eth/cmd/getgas/main.go | 4 +- client/asset/eth/contractor.go | 51 +- client/asset/eth/eth.go | 96 ++-- client/asset/eth/eth_test.go | 65 +-- client/asset/eth/nodeclient.go | 3 +- client/asset/eth/nodeclient_harness_test.go | 118 ++-- client/core/simnet_trade.go | 2 +- dex/networks/erc20/contracts/ERC20SwapV0.sol | 2 +- dex/networks/erc20/contracts/ERC20SwapV1.sol | 34 +- .../erc20/contracts/v1/BinRuntimeV1.go | 2 +- dex/networks/erc20/contracts/v1/contract.go | 76 +-- dex/networks/eth/contracts/ETHSwapV0.sol | 2 +- dex/networks/eth/contracts/ETHSwapV1.sol | 24 +- dex/networks/eth/contracts/v1/BinRuntimeV1.go | 2 +- dex/networks/eth/contracts/v1/contract.go | 78 +-- dex/networks/eth/params.go | 122 +++-- dex/networks/eth/tokens.go | 6 +- dex/networks/eth/txdata.go | 15 +- dex/testing/eth/harness.sh | 4 +- server/asset/eth/coiner.go | 241 ++++++--- server/asset/eth/coiner_test.go | 511 +++++++++++------- server/asset/eth/eth.go | 219 ++++---- server/asset/eth/eth_test.go | 510 ++++++++++------- server/asset/eth/rpcclient.go | 93 +++- server/asset/eth/rpcclient_harness_test.go | 12 +- server/asset/eth/tokener.go | 204 ++++++- server/asset/polygon/polygon.go | 45 +- server/cmd/dcrdex/evm-protocol-overrides.json | 8 + 29 files changed, 1548 insertions(+), 1002 deletions(-) create mode 100644 server/cmd/dcrdex/evm-protocol-overrides.json diff --git a/.gitignore b/.gitignore index e545b4f958..2d36e707e4 100644 --- a/.gitignore +++ b/.gitignore @@ -48,3 +48,4 @@ server/cmd/validatemarkets client/cmd/translationsreport/translationsreport client/cmd/translationsreport/worksheets server/cmd/dexadm/dexadm +server/cmd/dcrdex/evm-protocol-overrides.json diff --git a/client/asset/eth/cmd/getgas/main.go b/client/asset/eth/cmd/getgas/main.go index d02f3bb64e..a8667c4d80 100644 --- a/client/asset/eth/cmd/getgas/main.go +++ b/client/asset/eth/cmd/getgas/main.go @@ -48,8 +48,8 @@ func mainErr() error { flag.BoolVar(&useMainnet, "mainnet", false, "use mainnet") flag.BoolVar(&useTestnet, "testnet", false, "use testnet") flag.BoolVar(&useSimnet, "simnet", false, "use simnet") - flag.BoolVar(&trace, "trace", false, "use simnet") - flag.BoolVar(&debug, "debug", false, "use simnet") + flag.BoolVar(&trace, "trace", false, "use trace logging") + flag.BoolVar(&debug, "debug", false, "use debug logging") flag.IntVar(&maxSwaps, "n", 5, "max number of swaps per transaction. minimum is 2. test will run from 2 swap up to n swaps.") flag.StringVar(&chain, "chain", "eth", "symbol of the base chain") flag.StringVar(&token, "token", "", "symbol of the token. if token is not specified, will check gas for base chain") diff --git a/client/asset/eth/contractor.go b/client/asset/eth/contractor.go index 49bd262aea..aff7084f89 100644 --- a/client/asset/eth/contractor.go +++ b/client/asset/eth/contractor.go @@ -220,11 +220,11 @@ func (c *contractorV0) vector(ctx context.Context, locator []byte) (*dexeth.Swap return nil, err } vector := &dexeth.SwapVector{ - From: swap.Participant, - To: swap.Initiator, - Value: dexeth.WeiToGwei(swap.Value), + From: swap.Initiator, + To: swap.Participant, + Value: swap.Value, SecretHash: secretHash, - LockTime: uint64(swap.LockTime.UnixMilli()), + LockTime: uint64(swap.LockTime.Unix()), } return vector, nil } @@ -242,11 +242,11 @@ func (c *contractorV0) statusAndVector(ctx context.Context, locator []byte) (*de return nil, nil, err } vector := &dexeth.SwapVector{ - From: swap.Participant, - To: swap.Initiator, - Value: dexeth.WeiToGwei(swap.Value), + From: swap.Initiator, + To: swap.Participant, + Value: swap.Value, SecretHash: secretHash, - LockTime: uint64(swap.LockTime.UnixMilli()), + LockTime: uint64(swap.LockTime.Unix()), } status := &dexeth.SwapStatus{ Step: swap.State, @@ -442,7 +442,6 @@ func (c *erc20Contractor) balance(ctx context.Context) (*big.Int, error) { // allowance exposes the read-only allowance method of the erc20 token contract. func (c *erc20Contractor) allowance(ctx context.Context) (*big.Int, error) { callOpts := &bind.CallOpts{ - Pending: true, From: c.acct, Context: ctx, } @@ -575,8 +574,7 @@ func (c *tokenContractorV0) tokenAddress() common.Address { type contractorV1 struct { contractV1 - abi *abi.ABI - // net dex.Network + abi *abi.ABI contractAddr common.Address acctAddr common.Address cb bind.ContractBackend @@ -588,19 +586,18 @@ type contractorV1 struct { var _ contractor = (*contractorV1)(nil) func newV1Contractor(net dex.Network, contractAddr, acctAddr common.Address, cb bind.ContractBackend) (contractor, error) { - c, err := swapv1.NewETHSwap(contractAddr, cb) if err != nil { return nil, err } return &contractorV1{ - contractV1: c, - abi: dexeth.ABIs[1], - // net: net, + contractV1: c, + abi: dexeth.ABIs[1], contractAddr: contractAddr, acctAddr: acctAddr, cb: cb, atomize: dexeth.WeiToGwei, + evmify: dexeth.GweiToWei, }, nil } @@ -656,7 +653,7 @@ func (c *contractorV1) initiate(txOpts *bind.TransactOpts, contracts []*asset.Co v := &dexeth.SwapVector{ From: c.acctAddr, To: common.HexToAddress(ac.Address), - Value: ac.Value, + Value: c.evmify(ac.Value), LockTime: ac.LockTime, } copy(v.SecretHash[:], ac.SecretHash) @@ -682,7 +679,7 @@ func (c *contractorV1) redeem(txOpts *bind.TransactOpts, redeems []*asset.Redemp // Not checking version from DecodeLocator because it was already // audited and incorrect version locator would err below anyway. - _, locator, err := dexeth.DecodeLocator(r.Spends.Contract) + _, locator, err := dexeth.DecodeContractData(r.Spends.Contract) if err != nil { return nil, fmt.Errorf("error parsing locator redeem: %w", err) } @@ -716,7 +713,7 @@ func (c *contractorV1) estimateInitGas(ctx context.Context, n int) (uint64, erro SecretHash: secretHash, Initiator: c.acctAddr, Participant: common.BytesToAddress(encode.RandomBytes(20)), - Value: 1, + Value: big.NewInt(dexeth.GweiFactor), }) } @@ -791,23 +788,23 @@ func (c *contractorV1) isRefundable(locator []byte) (bool, error) { func (c *contractorV1) incomingValue(ctx context.Context, tx *types.Transaction) (uint64, error) { if redeems, err := dexeth.ParseRedeemDataV1(tx.Data()); err == nil { - var redeemed uint64 + var redeemed *big.Int for _, r := range redeems { - redeemed += r.Contract.Value + redeemed.Add(redeemed, r.Contract.Value) } - return redeemed, nil + return c.atomize(redeemed), nil } refund, err := dexeth.ParseRefundDataV1(tx.Data()) if err != nil { return 0, nil } - return refund.Value, nil + return c.atomize(refund.Value), nil } func (c *contractorV1) outgoingValue(tx *types.Transaction) (swapped uint64) { if inits, err := dexeth.ParseInitiateDataV1(tx.Data()); err == nil { for _, init := range inits { - swapped += init.Value + swapped += c.atomize(init.Value) } } return @@ -933,14 +930,6 @@ func (c *tokenContractorV1) tokenAddress() common.Address { var _ contractor = (*tokenContractorV1)(nil) var _ tokenContractor = (*tokenContractorV1)(nil) -// readOnlyCallOpts is the CallOpts used for read-only contract method calls. -func readOnlyCallOpts(ctx context.Context) *bind.CallOpts { - return &bind.CallOpts{ - Pending: true, - Context: ctx, - } -} - func estimateGas(ctx context.Context, from, to common.Address, abi *abi.ABI, cb bind.ContractBackend, value *big.Int, method string, args ...interface{}) (uint64, error) { data, err := abi.Pack(method, args...) if err != nil { diff --git a/client/asset/eth/eth.go b/client/asset/eth/eth.go index 6f4172481b..863ff4ce22 100644 --- a/client/asset/eth/eth.go +++ b/client/asset/eth/eth.go @@ -163,7 +163,7 @@ var ( // exposed though any Driver methods or assets/driver functions. Use the // parent wallet's WalletInfo via (*Driver).Info if you need a token's // supported versions before a wallet is available. - SupportedVersions: []uint32{0}, + SupportedVersions: []uint32{0, 1}, UnitInfo: dexeth.UnitInfo, AvailableWallets: []*asset.WalletDefinition{ // { @@ -598,14 +598,7 @@ func privKeyFromSeed(seed []byte) (pk []byte, zero func(), err error) { // contractVersion converts a server version to a contract version. It applies // to both tokens and eth right now, but that may not always be the case. func contractVersion(serverVer uint32) uint32 { - switch serverVer { - case 0: - return 0 - case 1: - return 1 - default: - return contractVersionUnknown - } + return dexeth.ProtocolVersion(serverVer).ContractVersion() } func CreateEVMWallet(chainID int64, createWalletParams *asset.CreateWalletParams, compat *CompatibilityData, skipConnect bool) error { @@ -1538,9 +1531,10 @@ func (w *baseWallet) MaxFundingFees(_ uint32, _ uint64, _ map[string]string) uin // SingleLotSwapRefundFees returns the fees for a swap transaction for a single lot. func (w *assetWallet) SingleLotSwapRefundFees(serverVer uint32, feeSuggestion uint64, _ bool) (swapFees uint64, refundFees uint64, err error) { - g := w.gases(contractVersion(serverVer)) + contractVer := contractVersion(serverVer) + g := w.gases(contractVer) if g == nil { - return 0, 0, fmt.Errorf("no gases known for %d version %d", w.assetID, serverVer) + return 0, 0, fmt.Errorf("no gases known for %d, contract version %d", w.assetID, contractVer) } return g.Swap * feeSuggestion, g.Refund * feeSuggestion, nil } @@ -1725,7 +1719,7 @@ func (w *TokenWallet) FundOrder(ord *asset.Order) (asset.Coins, []dex.Bytes, uin g, err := w.initGasEstimate(int(ord.MaxSwapCount), contractVer, ord.RedeemVersion, ord.RedeemAssetID) if err != nil { - return nil, nil, 0, fmt.Errorf("error estimating swap gas: %vlaptop apart comic equip remove adult system tuna office discover toddler can keep fury aware amazing injury typical", err) + return nil, nil, 0, fmt.Errorf("error estimating swap gas: %v", err) } ethToLock := ord.MaxFeeRate * g.Swap * ord.MaxSwapCount @@ -2224,7 +2218,7 @@ func (w *ETHWallet) Swap(swaps *asset.Swaps) ([]asset.Receipt, asset.Coin, uint6 expiration: time.Unix(int64(swap.LockTime), 0), value: swap.Value, txHash: txHash, - locator: acToLocator(contractVer, swap, w.addr), + locator: acToLocator(contractVer, swap, dexeth.GweiToWei(swap.Value), w.addr), contractVer: contractVer, contractAddr: w.versionedContracts[contractVer].String(), }) @@ -2242,7 +2236,7 @@ func (w *ETHWallet) Swap(swaps *asset.Swaps) ([]asset.Receipt, asset.Coin, uint6 } // acToLocator converts the asset.Contract to a version-specific locator. -func acToLocator(contractVer uint32, swap *asset.Contract, from common.Address) []byte { +func acToLocator(contractVer uint32, swap *asset.Contract, evmValue *big.Int, from common.Address) []byte { switch contractVer { case 0: return swap.SecretHash @@ -2252,7 +2246,7 @@ func acToLocator(contractVer uint32, swap *asset.Contract, from common.Address) return (&dexeth.SwapVector{ From: from, To: common.HexToAddress(swap.Address), - Value: swap.Value, + Value: evmValue, SecretHash: secretHash, LockTime: swap.LockTime, }).Locator() @@ -2338,7 +2332,7 @@ func (w *TokenWallet) Swap(swaps *asset.Swaps) ([]asset.Receipt, asset.Coin, uin expiration: time.Unix(int64(swap.LockTime), 0), value: swap.Value, txHash: txHash, - locator: acToLocator(contractVer, swap, w.addr), + locator: acToLocator(contractVer, swap, w.evmify(swap.Value), w.addr), contractVer: contractVer, contractAddr: contractAddr, }) @@ -2397,7 +2391,7 @@ func (w *assetWallet) Redeem(form *asset.RedeemForm, feeWallet *assetWallet, non // from redemption.Spends.Contract. Even for scriptable UTXO assets, the // redeem script in this Contract field is redundant with the SecretHash // field as ExtractSwapDetails can be applied to extract the hash. - ver, locator, err := dexeth.DecodeLocator(redemption.Spends.Contract) + ver, locator, err := dexeth.DecodeContractData(redemption.Spends.Contract) if err != nil { return fail(fmt.Errorf("Redeem: invalid versioned swap contract data: %w", err)) } @@ -2433,7 +2427,7 @@ func (w *assetWallet) Redeem(form *asset.RedeemForm, feeWallet *assetWallet, non if status.Step != dexeth.SSInitiated { return nil, nil, 0, asset.ErrSwapNotInitiated } - redeemedValue += vector.Value + redeemedValue += w.atomize(vector.Value) } g := w.gases(contractVer) @@ -2541,7 +2535,7 @@ func recoverPubkey(msgHash, sig []byte) ([]byte, error) { // tokenBalance checks the token balance of the account handled by the wallet. func (w *assetWallet) tokenBalance() (bal *big.Int, err error) { // We don't care about the version. - return bal, w.withTokenContractor(w.assetID, contractVersionERC20, func(c tokenContractor) error { + return bal, w.withTokenContractor(w.assetID, dexeth.ContractVersionERC20, func(c tokenContractor) error { bal, err = c.balance(w.ctx) return err }) @@ -2550,7 +2544,7 @@ func (w *assetWallet) tokenBalance() (bal *big.Int, err error) { // tokenAllowance checks the amount of tokens that the swap contract is approved // to spend on behalf of the account handled by the wallet. func (w *assetWallet) tokenAllowance() (allowance *big.Int, err error) { - return allowance, w.withTokenContractor(w.assetID, contractVersionERC20, func(c tokenContractor) error { + return allowance, w.withTokenContractor(w.assetID, dexeth.ContractVersionERC20, func(c tokenContractor) error { allowance, err = c.allowance(w.ctx) return err }) @@ -2909,7 +2903,7 @@ func (w *assetWallet) AuditContract(coinID, contract, serializedTx dex.Bytes, re return nil, fmt.Errorf("AuditContract: coin id != txHash - coin id: %x, txHash: %s", coinID, tx.Hash()) } - version, locator, err := dexeth.DecodeLocator(contract) + version, locator, err := dexeth.DecodeContractData(contract) if err != nil { return nil, fmt.Errorf("AuditContract: failed to decode contract data: %w", err) } @@ -2951,21 +2945,10 @@ func (w *assetWallet) AuditContract(coinID, contract, serializedTx dex.Bytes, re if !ok { return nil, errors.New("AuditContract: tx does not initiate secret hash") } - // Check vector equivalence. Secret hash equivalence is implied by the - // vectors presence in the map returned from ParseInitiateData. - if vec.Value != txVec.Value { - return nil, errors.New("tx data value doesn't match reported locator data") - } - if vec.To != txVec.To { - return nil, errors.New("tx to address doesn't match reported locator data") - } - if vec.From != txVec.From { - return nil, errors.New("tx from address doesn't match reported locator data") - } - if vec.LockTime != txVec.LockTime { - return nil, errors.New("tx lock time doesn't match reported locator data") + if !dexeth.CompareVectors(vec, txVec) { + return nil, fmt.Errorf("tx vector doesn't match expectation. %+v != %+v", txVec, vec) } - val = vec.Value + val = w.atomize(vec.Value) participant = vec.To.String() lockTime = time.Unix(int64(vec.LockTime), 0) secretHashB = vec.SecretHash[:] @@ -3001,7 +2984,7 @@ func (w *assetWallet) LockTimeExpired(ctx context.Context, lockTime time.Time) ( // ContractLockTimeExpired returns true if the specified contract's locktime has // expired, making it possible to issue a Refund. func (w *assetWallet) ContractLockTimeExpired(ctx context.Context, contract dex.Bytes) (bool, time.Time, error) { - contractVer, locator, err := dexeth.DecodeLocator(contract) + contractVer, locator, err := dexeth.DecodeContractData(contract) if err != nil { return false, time.Time{}, err } @@ -3068,7 +3051,7 @@ func (w *assetWallet) FindRedemption(ctx context.Context, _, contract dex.Bytes) // contract, so we are basically doing the next best thing here. const coinIDTmpl = coinIDTakerFoundMakerRedemption + "%s" - contractVer, locator, err := dexeth.DecodeLocator(contract) + contractVer, locator, err := dexeth.DecodeContractData(contract) if err != nil { return nil, nil, err } @@ -3147,7 +3130,7 @@ func (w *assetWallet) findSecret(locator []byte, contractVer uint32) ([]byte, st // Refund refunds a contract. This can only be used after the time lock has // expired. func (w *assetWallet) Refund(_, contract dex.Bytes, feeRate uint64) (dex.Bytes, error) { - contractVer, locator, err := dexeth.DecodeLocator(contract) + contractVer, locator, err := dexeth.DecodeContractData(contract) if err != nil { return nil, fmt.Errorf("Refund: failed to decode contract: %w", err) } @@ -3186,7 +3169,7 @@ func (w *assetWallet) Refund(_, contract dex.Bytes, feeRate uint64) (dex.Bytes, return nil, fmt.Errorf("Refund: failed to get network tip cap: %w", err) } - tx, err := w.refund(locator, vector.Value, maxFeeRate, tipRate, contractVer) + tx, err := w.refund(locator, w.atomize(vector.Value), maxFeeRate, tipRate, contractVer) if err != nil { return nil, fmt.Errorf("Refund: failed to call refund: %w", err) } @@ -3244,7 +3227,7 @@ func (w *ETHWallet) EstimateRegistrationTxFee(feeRate uint64) uint64 { // EstimateRegistrationTxFee returns an estimate for the tx fee needed to // pay the registration fee using the provided feeRate. func (w *TokenWallet) EstimateRegistrationTxFee(feeRate uint64) uint64 { - g := w.gases(contractVersionERC20) + g := w.gases(dexeth.ContractVersionERC20) if g == nil { w.log.Errorf("no gas table") return math.MaxUint64 @@ -3321,7 +3304,7 @@ func (w *TokenWallet) canSend(value uint64, verifyBalance, isPreEstimate bool) ( } maxFeeRateGwei := dexeth.WeiToGweiCeil(maxFeeRate) - g := w.gases(contractVersionERC20) + g := w.gases(dexeth.ContractVersionERC20) if g == nil { return 0, nil, nil, fmt.Errorf("gas table not found") } @@ -3412,7 +3395,7 @@ func (w *ETHWallet) RestorationInfo(seed []byte) ([]*asset.WalletRestoration, er // SwapConfirmations gets the number of confirmations and the spend status // for the specified swap. func (w *assetWallet) SwapConfirmations(ctx context.Context, coinID dex.Bytes, contract dex.Bytes, _ time.Time) (confs uint32, spent bool, err error) { - contractVer, secretHash, err := dexeth.DecodeLocator(contract) + contractVer, secretHash, err := dexeth.DecodeContractData(contract) if err != nil { return 0, false, err } @@ -3594,7 +3577,7 @@ func (w *baseWallet) swapOrRedemptionFeesPaid( contractData dex.Bytes, isInit bool, ) (fee uint64, locators [][]byte, err error) { - contractVer, locator, err := dexeth.DecodeLocator(contractData) + contractVer, locator, err := dexeth.DecodeContractData(contractData) if err != nil { return 0, nil, err } @@ -3670,7 +3653,7 @@ func extractSecretHashes(tx *types.Transaction, contractVer uint32, isInit bool) } locators = make([][]byte, 0, len(inits)) for k := range inits { - copyK := k // TODO: Is this really necessary? + copyK := k locators = append(locators, copyK[:]) } } else { @@ -3909,7 +3892,7 @@ func (w *assetWallet) confirmRedemption(coinID dex.Bytes, redemption *asset.Rede var txHash common.Hash copy(txHash[:], coinID) - contractVer, locator, err := dexeth.DecodeLocator(redemption.Spends.Contract) + contractVer, locator, err := dexeth.DecodeContractData(redemption.Spends.Contract) if err != nil { return nil, fmt.Errorf("failed to decode contract data: %w", err) } @@ -4505,8 +4488,8 @@ func (w *assetWallet) loadContractors() error { // withContractor runs the provided function with the versioned contractor. func (w *assetWallet) withContractor(contractVer uint32, f func(contractor) error) error { - if contractVer == contractVersionERC20 { - // For ERC02 methods, use the most recent contractor version. + if contractVer == dexeth.ContractVersionERC20 { + // For ERC20 methods, use the most recent contractor version. var bestVer uint32 var bestContractor contractor for ver, c := range w.contractors { @@ -4538,7 +4521,7 @@ func (w *assetWallet) withTokenContractor(assetID, ver uint32, f func(tokenContr // estimateApproveGas estimates the gas required for a transaction approving a // spender for an ERC20 contract. func (w *assetWallet) estimateApproveGas(newGas *big.Int) (gas uint64, err error) { - return gas, w.withTokenContractor(w.assetID, contractVersionERC20, func(c tokenContractor) error { + return gas, w.withTokenContractor(w.assetID, dexeth.ContractVersionERC20, func(c tokenContractor) error { gas, err = c.estimateApproveGas(w.ctx, newGas) return err }) @@ -4546,8 +4529,9 @@ func (w *assetWallet) estimateApproveGas(newGas *big.Int) (gas uint64, err error // estimateTransferGas estimates the gas needed for a token transfer call to an // ERC20 contract. +// TODO: Delete this and contractor methods. Unused. func (w *assetWallet) estimateTransferGas(val uint64) (gas uint64, err error) { - return gas, w.withTokenContractor(w.assetID, contractVersionERC20, func(c tokenContractor) error { + return gas, w.withTokenContractor(w.assetID, dexeth.ContractVersionERC20, func(c tokenContractor) error { gas, err = c.estimateTransferGas(w.ctx, w.evmify(val)) return err }) @@ -5921,8 +5905,9 @@ func (getGas) Estimate(ctx context.Context, net dex.Network, assetID, contractVe var approvalClient *multiRPCClient var approvalContractor tokenContractor + evmify := dexeth.GweiToWei if isToken { - + evmify = wParams.Token.AtomicToEVM atomicBal := wParams.Token.EVMToAtomic(tokenBal) convUnit := ui.Conventional.Unit @@ -5964,7 +5949,7 @@ func (getGas) Estimate(ctx context.Context, net dex.Network, assetID, contractVe } log.Debugf("Getting gas estimates") - return getGasEstimates(ctx, cl, approvalClient, c, approvalContractor, maxSwaps, contractVer, wParams.Gas, log) + return getGasEstimates(ctx, cl, approvalClient, c, approvalContractor, maxSwaps, contractVer, wParams.Gas, evmify, log) } // getGasEstimate is used to get a gas table for an asset's contract(s). The @@ -5979,7 +5964,7 @@ func (getGas) Estimate(ctx context.Context, net dex.Network, assetID, contractVe // gas estimate. These are only needed when the asset is a token. For eth, they // can be nil. func getGasEstimates(ctx context.Context, cl, acl ethFetcher, c contractor, ac tokenContractor, - maxSwaps int, contractVer uint32, g *dexeth.Gases, log dex.Logger) (err error) { + maxSwaps int, contractVer uint32, g *dexeth.Gases, evmify func(v uint64) *big.Int, log dex.Logger) (err error) { tc, isToken := c.(tokenContractor) @@ -6120,6 +6105,7 @@ func getGasEstimates(ctx context.Context, cl, acl ethFetcher, c contractor, ac t stats.transfers = append(stats.transfers, receipt.GasUsed) } + var v uint64 = 1 for n := 1; n <= maxSwaps; n++ { contracts := make([]*asset.Contract, 0, n) secrets := make([][32]byte, 0, n) @@ -6131,7 +6117,7 @@ func getGasEstimates(ctx context.Context, cl, acl ethFetcher, c contractor, ac t secretHash := sha256.Sum256(secretB) contracts = append(contracts, &asset.Contract{ Address: cl.address().String(), // trading with self - Value: 1, + Value: v, SecretHash: secretHash[:], LockTime: uint64(lockTime.Unix()), }) @@ -6167,7 +6153,7 @@ func getGasEstimates(ctx context.Context, cl, acl ethFetcher, c contractor, ac t stats.swaps = append(stats.swaps, receipt.GasUsed) // Estimate a refund - refundGas, err := c.estimateRefundGas(ctx, acToLocator(contractVer, contracts[0], cl.address())) + refundGas, err := c.estimateRefundGas(ctx, acToLocator(contractVer, contracts[0], evmify(v), cl.address())) if err != nil { return fmt.Errorf("error estimate refund gas: %w", err) } @@ -6180,7 +6166,7 @@ func getGasEstimates(ctx context.Context, cl, acl ethFetcher, c contractor, ac t Spends: &asset.AuditInfo{ Recipient: cl.address().String(), Expiration: lockTime, - Contract: dexeth.EncodeContractData(contractVer, acToLocator(contractVer, contract, cl.address())), + Contract: dexeth.EncodeContractData(contractVer, acToLocator(contractVer, contract, evmify(v), cl.address())), SecretHash: contract.SecretHash, }, Secret: secrets[i][:], diff --git a/client/asset/eth/eth_test.go b/client/asset/eth/eth_test.go index 207e6a9ae1..d19a1b13a2 100644 --- a/client/asset/eth/eth_test.go +++ b/client/asset/eth/eth_test.go @@ -414,9 +414,9 @@ func (c *tContractor) vector(ctx context.Context, locator []byte) (*dexeth.SwapV return nil, errors.New("swap not in map") } v := &dexeth.SwapVector{ - From: swap.Participant, - To: swap.Initiator, - Value: dexeth.WeiToGwei(swap.Value), + From: swap.Initiator, + To: swap.Participant, + Value: swap.Value, SecretHash: secretHash, LockTime: uint64(swap.LockTime.Unix()), } @@ -436,9 +436,9 @@ func (c *tContractor) statusAndVector(ctx context.Context, locator []byte) (*dex return nil, nil, errors.New("swap not in map") } v := &dexeth.SwapVector{ - From: swap.Participant, - To: swap.Initiator, - Value: dexeth.WeiToGwei(swap.Value), + From: swap.Initiator, + To: swap.Participant, + Value: swap.Value, SecretHash: vector.SecretHash, LockTime: uint64(swap.LockTime.Unix()), } @@ -1717,7 +1717,7 @@ func testRefund(t *testing.T, assetID uint32) { v1Vector := dexeth.SwapVector{ From: testAddressA, To: testAddressB, - Value: 1, + Value: dexeth.GweiToWei(1), SecretHash: secretHash, LockTime: uint64(time.Now().Unix()), } @@ -2711,14 +2711,14 @@ func testSwap(t *testing.T, assetID uint32) { testName, receipt.Coin().Value(), contract.Value) } contractData := receipt.Contract() - contractVer, locator, err := dexeth.DecodeLocator(contractData) + contractVer, locator, err := dexeth.DecodeContractData(contractData) if err != nil { t.Fatalf("failed to decode contract data: %v", err) } if swaps.Version != contractVer { t.Fatal("wrong contract version") } - chkLocator := acToLocator(contractVer, contract, node.addr) + chkLocator := acToLocator(contractVer, contract, dexeth.GweiToWei(contract.Value), node.addr) if !bytes.Equal(locator, chkLocator) { t.Fatalf("%v, contract: %x != locator in input: %x", testName, receipt.Contract(), locator) @@ -3305,7 +3305,7 @@ func testRedeem(t *testing.T, assetID uint32) { // Check that value of output coin is as axpected var totalSwapValue uint64 for _, redemption := range test.form.Redemptions { - _, locator, err := dexeth.DecodeLocator(redemption.Spends.Contract) + _, locator, err := dexeth.DecodeContractData(redemption.Spends.Contract) if err != nil { t.Fatalf("DecodeLocator: %v", err) } @@ -3549,15 +3549,6 @@ func TestMaxOrder(t *testing.T) { } } -func overMaxWei() *big.Int { - maxInt := ^uint64(0) - maxWei := new(big.Int).SetUint64(maxInt) - gweiFactorBig := big.NewInt(dexeth.GweiFactor) - maxWei.Mul(maxWei, gweiFactorBig) - overMaxWei := new(big.Int).Set(maxWei) - return overMaxWei.Add(overMaxWei, gweiFactorBig) -} - func packInitiateDataV0(initiations []*dexeth.Initiation) ([]byte, error) { abiInitiations := make([]swapv0.ETHSwapInitiation, 0, len(initiations)) for _, init := range initiations { @@ -3747,7 +3738,7 @@ func testAuditContract(t *testing.T, assetID uint32) { t.Fatalf(`"%v": expected contract %x != actual %x`, test.name, test.contract, auditInfo.Contract) } - _, expectedSecretHash, err := dexeth.DecodeLocator(test.contract) + _, expectedSecretHash, err := dexeth.DecodeContractData(test.contract) if err != nil { t.Fatalf(`"%v": failed to decode versioned bytes: %v`, test.name, err) } @@ -4332,24 +4323,14 @@ func testRefundReserves(t *testing.T, assetID uint32) { node.swapMap = map[[32]byte]*dexeth.SwapState{secretHash: {}} feeWallet := eth - gasesV0 := dexeth.VersionedGases[0] - gasesV1 := &dexeth.Gases{Refund: 1e6} + gasesV0 := eth.versionedGases[0] + gasesV1 := eth.versionedGases[1] assetV0 := *tETHV0 assetV1 := *tETHV0 - if assetID == BipID { - eth.versionedGases[1] = gasesV1 - } else { + if assetID != BipID { feeWallet = node.tokenParent assetV0 = *tTokenV0 assetV1 = *tTokenV0 - tokenContracts := dexeth.Tokens[usdcTokenID].NetTokens[dex.Simnet].SwapContracts - gasesV0 = &tokenGasesV0 - tc := *tokenContracts[0] - tc.Gas = *gasesV1 - tokenContracts[1] = &tc - defer delete(tokenContracts, 1) - eth.versionedGases[0] = gasesV0 - eth.versionedGases[1] = gasesV1 node.tokenContractor.bal = dexeth.GweiToWei(1e9) } @@ -4430,9 +4411,9 @@ func testRedemptionReserves(t *testing.T, assetID uint32) { var secretHash [32]byte node.tContractor.swapMap[secretHash] = &dexeth.SwapState{} - gasesV1 := &dexeth.Gases{Redeem: 1e6, RedeemAdd: 85e5} + gasesV0 := eth.versionedGases[0] + gasesV1 := eth.versionedGases[1] eth.versionedGases[1] = gasesV1 - gasesV0 := dexeth.VersionedGases[0] assetV0 := *tETHV0 assetV1 := *tETHV0 feeWallet := eth @@ -4441,12 +4422,6 @@ func testRedemptionReserves(t *testing.T, assetID uint32) { feeWallet = node.tokenParent assetV0 = *tTokenV0 assetV1 = *tTokenV0 - tokenContracts := dexeth.Tokens[usdcTokenID].NetTokens[dex.Simnet].SwapContracts - gasesV0 = &tokenGasesV0 - tc := *tokenContracts[0] - tc.Gas = *gasesV1 - tokenContracts[1] = &tc - defer delete(tokenContracts, 1) } assetV0.MaxFeeRate = 45 @@ -4553,7 +4528,7 @@ func testSend(t *testing.T, assetID uint32) { maxFeeRate, _, _ := eth.recommendedMaxFeeRate(eth.ctx) ethFees := dexeth.WeiToGwei(maxFeeRate) * defaultSendGasLimit - tokenFees := dexeth.WeiToGwei(maxFeeRate) * tokenGasesV0.Transfer + tokenFees := dexeth.WeiToGwei(maxFeeRate) * tokenGasesV1.Transfer const val = 10e9 const testAddr = "dd93b447f7eBCA361805eBe056259853F3912E04" @@ -4606,12 +4581,12 @@ func testSend(t *testing.T, assetID uint32) { coin, err := w.Send(test.addr, val, 0) if test.wantErr { if err == nil { - t.Fatalf("expected error for test %v", test.name) + t.Fatalf("expected error for test %q", test.name) } continue } if err != nil { - t.Fatalf("unexpected error for test %v: %v", test.name, err) + t.Fatalf("unexpected error for test %q: %v", test.name, err) } if !bytes.Equal(txHash[:], coin.ID()) { t.Fatal("coin is not the tx hash") @@ -4851,7 +4826,7 @@ func testEstimateSendTxFee(t *testing.T, assetID uint32) { maxFeeRate, _, _ := eth.recommendedMaxFeeRate(eth.ctx) ethFees := dexeth.WeiToGwei(maxFeeRate) * defaultSendGasLimit - tokenFees := dexeth.WeiToGwei(maxFeeRate) * tokenGasesV0.Transfer + tokenFees := dexeth.WeiToGwei(maxFeeRate) * tokenGasesV1.Transfer ethFees = ethFees * 12 / 10 tokenFees = tokenFees * 12 / 10 diff --git a/client/asset/eth/nodeclient.go b/client/asset/eth/nodeclient.go index 25358dba87..f0d0ad8d19 100644 --- a/client/asset/eth/nodeclient.go +++ b/client/asset/eth/nodeclient.go @@ -425,9 +425,10 @@ func newTxOpts(ctx context.Context, from common.Address, val, maxGas uint64, max } func gases(contractVer uint32, versionedGases map[uint32]*dexeth.Gases) *dexeth.Gases { - if contractVer != contractVersionERC20 { + if contractVer != dexeth.ContractVersionERC20 { return versionedGases[contractVer] } + var bestVer uint32 var bestGases *dexeth.Gases for ver, gases := range versionedGases { diff --git a/client/asset/eth/nodeclient_harness_test.go b/client/asset/eth/nodeclient_harness_test.go index c6e1b22a85..1fef32d9fb 100644 --- a/client/asset/eth/nodeclient_harness_test.go +++ b/client/asset/eth/nodeclient_harness_test.go @@ -92,12 +92,10 @@ var ( participantAddr common.Address participantAcct *accounts.Account participantEthClient ethFetcher - ethSwapContractAddr common.Address simnetContractor contractor participantContractor contractor simnetTokenContractor tokenContractor participantTokenContractor tokenContractor - ethGases = dexeth.VersionedGases[0] ethGases *dexeth.Gases tokenGases *dexeth.Gases testnetSecPerBlock = 15 * time.Second @@ -130,8 +128,8 @@ var ( usdcID, _ = dex.BipSymbolID("usdc.eth") masterToken *dexeth.Token - v1 bool - ver uint32 + v1 bool + contractVer uint32 ) func newContract(stamp uint64, secretHash [32]byte, val uint64) *asset.Contract { @@ -148,11 +146,11 @@ func acLocator(c *asset.Contract) []byte { } func makeLocator(secretHash [32]byte, valg, lockTime uint64) []byte { - if ver == 1 { + if contractVer == 1 { return (&dexeth.SwapVector{ From: ethClient.address(), To: participantEthClient.address(), - Value: valg, + Value: dexeth.GweiToWei(valg), SecretHash: secretHash, LockTime: lockTime, }).Locator() @@ -170,7 +168,7 @@ func newRedeem(secret, secretHash [32]byte, valg, lockTime uint64) *asset.Redemp // id: txHash, value: valg, }, - Contract: dexeth.EncodeContractData(ver, makeLocator(secretHash, valg, lockTime)), + Contract: dexeth.EncodeContractData(contractVer, makeLocator(secretHash, valg, lockTime)), }, Secret: secret[:], } @@ -209,7 +207,7 @@ func waitForMinedRPC() error { if err != nil { return err } - const targetConfs = 1 + const targetConfs = 3 currentHeight := hdr.Number barrierHeight := new(big.Int).Add(currentHeight, big.NewInt(targetConfs)) fmt.Println("Waiting for RPC blocks") @@ -260,6 +258,8 @@ out: // NOTE: Not effectual for providers. waitForMinedRPC // above handles waiting for mined blocks that we assume // have our transactions. + + // ./alpha attach --exec "eth.pendingTransactions" txsa, err := ethClient.(txPoolFetcher).pendingTransactions() if err != nil { return fmt.Errorf("initiator pendingTransactions error: %v", err) @@ -383,16 +383,16 @@ func runSimnet(m *testing.M) (int, error) { return 1, fmt.Errorf("error creating participant wallet dir: %v", err) } - tokenGases = &dexeth.Tokens[usdcID].NetTokens[dex.Simnet].SwapContracts[ver].Gas + tokenGases = &dexeth.Tokens[usdcID].NetTokens[dex.Simnet].SwapContracts[contractVer].Gas // ETH swap contract. masterToken = dexeth.Tokens[usdcID] token := masterToken.NetTokens[dex.Simnet] - fmt.Printf("ETH swap contract address is %v\n", dexeth.ContractAddresses[ver][dex.Simnet]) - fmt.Printf("Token swap contract addr is %v\n", token.SwapContracts[ver].Address) + fmt.Printf("ETH swap contract address is %v\n", dexeth.ContractAddresses[contractVer][dex.Simnet]) + fmt.Printf("Token swap contract addr is %v\n", token.SwapContracts[contractVer].Address) fmt.Printf("Test token contract addr is %v\n", token.Address) - ethSwapContractAddr = dexeth.ContractAddresses[ver][dex.Simnet] + contractAddr := dexeth.ContractAddresses[contractVer][dex.Simnet] initiatorProviders, participantProviders := rpcEndpoints(dex.Simnet) @@ -427,9 +427,9 @@ func runSimnet(m *testing.M) (int, error) { simnetAddr = simnetAcct.Address participantAddr = participantAcct.Address - contractAddr, exists := dexeth.ContractAddresses[ver][dex.Simnet] + contractAddr, exists := dexeth.ContractAddresses[contractVer][dex.Simnet] if !exists || contractAddr == (common.Address{}) { - return 1, fmt.Errorf("no contract address for version %d", ver) + return 1, fmt.Errorf("no contract address for contract version %d", contractVer) } if v1 { @@ -493,7 +493,7 @@ func runSimnet(m *testing.M) (int, error) { func runTestnet(m *testing.M) (int, error) { masterToken = dexeth.Tokens[usdcID] - tokenGases = &masterToken.NetTokens[dex.Testnet].SwapContracts[ver].Gas + tokenGases = &masterToken.NetTokens[dex.Testnet].SwapContracts[contractVer].Gas if testnetWalletSeed == "" || testnetParticipantWalletSeed == "" { return 1, errors.New("testnet seeds not set") } @@ -508,8 +508,8 @@ func runTestnet(m *testing.M) (int, error) { return 1, fmt.Errorf("error creating testnet participant wallet dir: %v", err) } secPerBlock = testnetSecPerBlock - ethSwapContractAddr = dexeth.ContractAddresses[ver][dex.Testnet] - fmt.Printf("ETH swap contract address is %v\n", ethSwapContractAddr) + contractAddr := dexeth.ContractAddresses[contractVer][dex.Testnet] + fmt.Printf("Swap contract address is %v\n", contractAddr) initiatorRPC, participantRPC := rpcEndpoints(dex.Testnet) @@ -564,14 +564,14 @@ func runTestnet(m *testing.M) (int, error) { } ctor, tokenCtor := newV0Contractor, newV0TokenContractor - if ver == 1 { - ctor = newV1Contractor, newV1TokenContractor + if contractVer == 1 { + ctor, tokenCtor = newV1Contractor, newV1TokenContractor } - if simnetContractor, err = ctor(dex.Testnet, simnetAddr, ethClient.contractBackend()); err != nil { + if simnetContractor, err = ctor(dex.Testnet, contractAddr, simnetAddr, ethClient.contractBackend()); err != nil { return 1, fmt.Errorf("newV0Contractor error: %w", err) } - if participantContractor, err = ctor(dex.Testnet, participantAddr, participantEthClient.contractBackend()); err != nil { + if participantContractor, err = ctor(dex.Testnet, contractAddr, participantAddr, participantEthClient.contractBackend()); err != nil { return 1, fmt.Errorf("participant newV0Contractor error: %w", err) } @@ -620,14 +620,18 @@ func prepareV1SimnetContractors() (err error) { } func prepareSimnetContractors(c contractorConstructor, tc tokenContractorConstructor) (err error) { - if simnetContractor, err = c(dex.Simnet, simnetAddr, ethClient.contractBackend()); err != nil { + contractAddr := dexeth.ContractAddresses[contractVer][dex.Simnet] + + if simnetContractor, err = c(dex.Simnet, contractAddr, simnetAddr, ethClient.contractBackend()); err != nil { return fmt.Errorf("new contractor error: %w", err) } - if participantContractor, err = c(dex.Simnet, participantAddr, participantEthClient.contractBackend()); err != nil { + if participantContractor, err = c(dex.Simnet, contractAddr, participantAddr, participantEthClient.contractBackend()); err != nil { return fmt.Errorf("participant new contractor error: %w", err) } - if simnetTokenContractor, err = tc(dex.Simnet, testTokenID, simnetAddr, ethClient.contractBackend()); err != nil { + token := dexeth.Tokens[usdcID] + + if simnetTokenContractor, err = tc(dex.Simnet, token, simnetAddr, ethClient.contractBackend()); err != nil { return fmt.Errorf("new token contractor error: %w", err) } @@ -636,7 +640,7 @@ func prepareSimnetContractors(c contractorConstructor, tc tokenContractorConstru // (*BoundContract).Call while calling (*ERC20Swap).TokenAddress. time.Sleep(time.Second) - if participantTokenContractor, err = tc(dex.Simnet, testTokenID, participantAddr, participantEthClient.contractBackend()); err != nil { + if participantTokenContractor, err = tc(dex.Simnet, token, participantAddr, participantEthClient.contractBackend()); err != nil { return fmt.Errorf("participant new token contractor error: %w", err) } return @@ -672,10 +676,10 @@ func TestMain(m *testing.M) { flag.Parse() if v1 { - ver = 1 + contractVer = 1 } - ethGases = dexeth.VersionedGases[ver] + ethGases = dexeth.VersionedGases[contractVer] if isTestnet { tmpDir, err := os.MkdirTemp("", "") @@ -835,7 +839,7 @@ func TestBasicRetrieval(t *testing.T) { func TestPeering(t *testing.T) { t.Run("testAddPeer", testAddPeer) t.Run("testSyncProgress", testSyncProgress) - t.Run("testGetCodeAt", testGetCodeAt) + // t.Run("testGetCodeAt", testGetCodeAt) } func TestAccount(t *testing.T) { @@ -860,7 +864,7 @@ func TestContract(t *testing.T) { } func TestGas(t *testing.T) { - t.Run("testInitiateGas", func(t *testing.T) { testInitiateGas(t, BipID) }) + // t.Run("testInitiateGas", func(t *testing.T) { testInitiateGas(t, BipID) }) t.Run("testRedeemGas", func(t *testing.T) { testRedeemGas(t, BipID) }) t.Run("testRefundGas", func(t *testing.T) { testRefundGas(t, BipID) }) } @@ -875,7 +879,7 @@ func TestTokenContract(t *testing.T) { func TestTokenGas(t *testing.T) { t.Run("testTransferGas", testTransferGas) t.Run("testApproveGas", testApproveGas) - t.Run("testInitiateTokenGas", func(t *testing.T) { testInitiateGas(t, usdcID) }) + // t.Run("testInitiateTokenGas", func(t *testing.T) { testInitiateGas(t, usdcID) }) t.Run("testRedeemTokenGas", func(t *testing.T) { testRedeemGas(t, usdcID) }) t.Run("testRefundTokenGas", func(t *testing.T) { testRefundGas(t, usdcID) }) } @@ -1165,16 +1169,14 @@ func testSyncProgress(t *testing.T) { } func testInitiateGas(t *testing.T, assetID uint32) { + gases := ethGases + c := simnetContractor if assetID != BipID { + c = simnetTokenContractor prepareTokenClients(t) + gases = tokenGases } - net := dex.Simnet - if isTestnet { - net = dex.Testnet - } - gases := gases(BipID, assetID, ver, net) - var previousGas uint64 maxSwaps := 50 for i := 1; i <= maxSwaps; i++ { @@ -1281,8 +1283,12 @@ func testInitiate(t *testing.T, assetID uint32) { return simnetTokenContractor.balance(ctx) } gases = tokenGases - tc := sc.(*tokenContractorV0) - evmify = tc.evmify + switch contractVer { + case 0: + evmify = sc.(*tokenContractorV0).evmify + case 1: + evmify = sc.(*tokenContractorV1).evmify + } } // Create a slice of random secret hashes that can be used in the tests and @@ -2304,23 +2310,23 @@ func testApproveGas(t *testing.T) { fmt.Printf("replacement tx hash: %s\n", tx.Hash()) }*/ -func testGetCodeAt(t *testing.T) { - cl, is := ethClient.(*nodeClient) - if !is { - t.Skip("getCode tests only run for nodeClient") - } - byteCode, err := cl.getCodeAt(ctx, ethSwapContractAddr) - if err != nil { - t.Fatalf("Failed to get bytecode: %v", err) - } - c, err := hex.DecodeString(swapv0.ETHSwapRuntimeBin) - if err != nil { - t.Fatalf("Error decoding") - } - if !bytes.Equal(byteCode, c) { - t.Fatal("Contract on chain does not match one in code") - } -} +// func testGetCodeAt(t *testing.T) { +// cl, is := ethClient.(*nodeClient) +// if !is { +// t.Skip("getCode tests only run for nodeClient") +// } +// byteCode, err := cl.getCodeAt(ctx, contractAddr) +// if err != nil { +// t.Fatalf("Failed to get bytecode: %v", err) +// } +// c, err := hex.DecodeString(swapv0.ETHSwapRuntimeBin) +// if err != nil { +// t.Fatalf("Error decoding") +// } +// if !bytes.Equal(byteCode, c) { +// t.Fatal("Contract on chain does not match one in code") +// } +// } func testSignMessage(t *testing.T) { msg := []byte("test message") @@ -2348,7 +2354,7 @@ func TestTokenGasEstimates(t *testing.T) { runSimnetMiner(ctx, "eth", tLogger) prepareTokenClients(t) tLogger.SetLevel(dex.LevelInfo) - if err := getGasEstimates(ctx, ethClient, participantEthClient, simnetTokenContractor, participantTokenContractor, 5, ver, tokenGases, tLogger); err != nil { + if err := getGasEstimates(ctx, ethClient, participantEthClient, simnetTokenContractor, participantTokenContractor, 5, contractVer, tokenGases, dexeth.GweiToWei, tLogger); err != nil { t.Fatalf("getGasEstimates error: %v", err) } } diff --git a/client/core/simnet_trade.go b/client/core/simnet_trade.go index e57e97fea6..650609c355 100644 --- a/client/core/simnet_trade.go +++ b/client/core/simnet_trade.go @@ -2131,7 +2131,7 @@ func (s *simulationTest) assertBalanceChanges(client *simulationClient, isRefund if isRefund { // NOTE: Gas price may be higher if the eth harness has // had a lot of use. The minimum is the gas tip cap. - ethRefundFees := int64(dexeth.RefundGas(0 /*version*/)) * dexeth.MinGasTipCap + ethRefundFees := int64(dexeth.RefundGas(1 /*version*/)) * dexeth.MinGasTipCap msgTx := wire.NewMsgTx(0) prevOut := wire.NewOutPoint(&chainhash.Hash{}, 0) diff --git a/dex/networks/erc20/contracts/ERC20SwapV0.sol b/dex/networks/erc20/contracts/ERC20SwapV0.sol index 4037f3bed5..76ba9d4c0a 100644 --- a/dex/networks/erc20/contracts/ERC20SwapV0.sol +++ b/dex/networks/erc20/contracts/ERC20SwapV0.sol @@ -120,7 +120,7 @@ contract ERC20Swap { } // redeem redeems an array of swaps contract. It checks that the sender is - // not a contract, and that the secret hash hashes to secretHash. The ERC20 + // not a contract, and that the secret hashes to secretHash. The ERC20 // tokens are transferred from the contract to the sender. function redeem(Redemption[] calldata redemptions) public diff --git a/dex/networks/erc20/contracts/ERC20SwapV1.sol b/dex/networks/erc20/contracts/ERC20SwapV1.sol index e5a7eaef45..c9542b10eb 100644 --- a/dex/networks/erc20/contracts/ERC20SwapV1.sol +++ b/dex/networks/erc20/contracts/ERC20SwapV1.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: BlueOak-1.0.0 // pragma should be as specific as possible to allow easier validation. -pragma solidity = 0.8.15; +pragma solidity = 0.8.18; // ERC20Swap creates a contract to be deployed on an ethereum network. In // order to save on gas fees, a separate ERC20Swap contract is deployed @@ -14,11 +14,11 @@ pragma solidity = 0.8.15; // When calling initiate, the necessary tokens for swaps are transferred to // the swap contract. At this point the funds belong to the contract, and // cannot be accessed by anyone else, not even the contract's deployer. The -// initiator sets a secret hash, a blocktime the funds will be accessible should -// they not be redeemed, and a participant who can redeem before or after the -// locktime. The participant can redeem at any time after the initiation -// transaction is mined if they have the secret that hashes to the secret hash. -// Otherwise, the initiator can refund funds any time after the locktime. +// initiator commits to the swap parameters, including a locktime after which +// the funds will be accessible for refund should they not be redeemed. The +// participant can redeem at any time after the initiation transaction is mined +// if they have the secret that hashes to the secret hash. Otherwise, the +// initiator can refund funds any time after the locktime. // // This contract has no limits on gas used for any transactions. // @@ -30,7 +30,7 @@ contract ERC20Swap { address public immutable token_address; - // Step is a type that hold's a contract's current step. Empty is the + // Step is a type that hold's a contract's current step. Empty is the // uninitiated or null value. enum Step { Empty, Filled, Redeemed, Refunded } @@ -41,6 +41,7 @@ contract ERC20Swap { } bytes32 constant RefundRecord = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; + bytes32 constant RefundRecordHash = 0xAF9613760F72635FBDB44A5A0A63C39F12AF30F950A6EE5C971BE188E89C4051; // swaps is a map of contract hashes to the "swap record". The swap record // has the following interpretation. @@ -58,16 +59,16 @@ contract ERC20Swap { // the swap record. struct Vector { bytes32 secretHash; + uint256 value; address initiator; uint64 refundTimestamp; address participant; - uint64 value; } // contractKey generates a key hash which commits to the contract data. The // generated hash is used as a key in the swaps map. function contractKey(Vector calldata v) public pure returns (bytes32) { - return sha256(bytes.concat(v.secretHash, bytes20(v.initiator), bytes20(v.participant), bytes8(v.value), bytes8(v.refundTimestamp))); + return sha256(bytes.concat(v.secretHash, bytes20(v.initiator), bytes20(v.participant), bytes32(v.value), bytes8(v.refundTimestamp))); } // Redemption is the information necessary to redeem a Vector. Since we @@ -134,6 +135,7 @@ contract ERC20Swap { require(v.value > 0, "0 val"); require(v.refundTimestamp > 0, "0 refundTimestamp"); + require(v.secretHash != RefundRecordHash, "illegal secret hash (refund record hash)"); bytes32 k = contractKey(v); bytes32 record = swaps[k]; @@ -144,7 +146,7 @@ contract ERC20Swap { swaps[k] = record; - initVal += v.value * 1 gwei; + initVal += v.value; } bool success; @@ -153,7 +155,7 @@ contract ERC20Swap { require(success && (data.length == 0 || abi.decode(data, (bool))), 'transfer from failed'); } - // isRedeemable returns whether or not a swap identified by secretHash + // isRedeemable returns whether or not a swap identified by vector // can be redeemed using secret. function isRedeemable(Vector calldata v) public @@ -165,7 +167,7 @@ contract ERC20Swap { } // redeem redeems a Vector. It checks that the sender is not a contract, - // and that the secret hash hashes to secretHash. msg.value is tranfered + // and that the secret hashes to secretHash. msg.value is tranfered // from ETHSwap to the sender. // // To prevent reentry attack, it is very important to check the state of the @@ -196,7 +198,7 @@ contract ERC20Swap { require(secretValidates(r.secret, r.v.secretHash), "invalid secret"); swaps[k] = r.secret; - amountToRedeem += r.v.value * 1 gwei; + amountToRedeem += r.v.value; } bool success; @@ -223,14 +225,14 @@ contract ERC20Swap { (bytes32 k, bytes32 record, uint256 blockNum) = retrieveStatus(v); // Is this swap initialized? + // This check also guarantees that the swap has not already been + // refunded i.e. record != RefundRecord, since RefundRecord is certainly + // greater than block.number. require(blockNum > 0 && blockNum <= block.number, "swap not active"); // Is it already redeemed? require(!secretValidates(record, v.secretHash), "swap already redeemed"); - // Is it already refunded? - require(record != RefundRecord, "swap already refunded"); - swaps[k] = RefundRecord; bool success; diff --git a/dex/networks/erc20/contracts/v1/BinRuntimeV1.go b/dex/networks/erc20/contracts/v1/BinRuntimeV1.go index 790689b5be..64f014706b 100644 --- a/dex/networks/erc20/contracts/v1/BinRuntimeV1.go +++ b/dex/networks/erc20/contracts/v1/BinRuntimeV1.go @@ -3,4 +3,4 @@ package v1 -const ERC20SwapRuntimeBin = "6080604052600436106100865760003560e01c80637802689d116100595780637802689d146101265780638c8e8fee14610146578063d2544c0614610192578063eb84e7f2146101b2578063ed7cbed7146101ed57600080fd5b8063428b16e11461008b57806361a16e33146100ad57806364a97bff146100e357806377d7e031146100f6575b600080fd5b34801561009757600080fd5b506100ab6100a6366004610dea565b61020d565b005b3480156100b957600080fd5b506100cd6100c8366004610e5f565b610533565b6040516100da9190610e8d565b60405180910390f35b6100ab6100f1366004610ed0565b6105ef565b34801561010257600080fd5b50610116610111366004610f33565b610918565b60405190151581526020016100da565b34801561013257600080fd5b50610116610141366004610e5f565b610992565b34801561015257600080fd5b5061017a7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016100da565b34801561019e57600080fd5b506100ab6101ad366004610e5f565b6109c5565b3480156101be57600080fd5b506101df6101cd366004610f55565b60006020819052908152604090205481565b6040519081526020016100da565b3480156101f957600080fd5b506101df610208366004610e5f565b610cc5565b3233146102355760405162461bcd60e51b815260040161022c90610f6e565b60405180910390fd5b6000805b82811015610407573684848381811061025457610254610f98565b60c0029190910191503390506102706080830160608401610fae565b6001600160a01b0316146102b35760405162461bcd60e51b815260206004820152600a6024820152691b9bdd08185d5d1a195960b21b604482015260640161022c565b600080806102c084610dbe565b9250925092506000811180156102d557504381105b6103115760405162461bcd60e51b815260206004820152600d60248201526c0756e66696c6c6564207377617609c1b604482015260640161022c565b61031c828535610918565b1561035c5760405162461bcd60e51b815260206004820152601060248201526f185b1c9958591e481c995919595b595960821b604482015260640161022c565b61036b60a08501358535610918565b6103a85760405162461bcd60e51b815260206004820152600e60248201526d1a5b9d985b1a59081cd958dc995d60921b604482015260640161022c565b600083815260208190526040902060a0850180359091556103cc9060808601610fde565b6103da90633b9aca0061101e565b6103ee9067ffffffffffffffff168761104e565b95505050505080806103ff90611066565b915050610239565b5060408051336024820152604480820184905282518083039091018152606490910182526020810180516001600160e01b031663a9059cbb60e01b17905290516000916060916001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016916104819161107f565b6000604051808303816000865af19150503d80600081146104be576040519150601f19603f3d011682016040523d82523d6000602084013e6104c3565b606091505b5090925090508180156104ee5750805115806104ee5750808060200190518101906104ee91906110ba565b61052c5760405162461bcd60e51b815260206004820152600f60248201526e1d1c985b9cd9995c8819985a5b1959608a1b604482015260640161022c565b5050505050565b60408051606081018252600080825260208201819052918101829052908061055a84610dbe565b92509250506105846040805160608101909152806000815260006020820181905260409091015290565b816000036105ab578060005b908160038111156105a3576105a3610e77565b9052506105e7565b600183016105bb57806003610590565b6105c6838635610918565b156105db5760028152602081018390526105e7565b60018152604081018290525b949350505050565b32331461060e5760405162461bcd60e51b815260040161022c90610f6e565b6000805b828110156107e8573684848381811061062d5761062d610f98565b905060a002019050600081608001602081019061064a9190610fde565b67ffffffffffffffff16116106895760405162461bcd60e51b81526020600482015260056024820152640c081d985b60da1b604482015260640161022c565b600061069b6060830160408401610fde565b67ffffffffffffffff16116106e65760405162461bcd60e51b815260206004820152601160248201527003020726566756e6454696d657374616d7607c1b604482015260640161022c565b60006106f182610cc5565b60008181526020819052604090205490915080156107425760405162461bcd60e51b815260206004820152600e60248201526d73776170206e6f7420656d70747960901b604482015260640161022c565b504361074f818435610918565b1561078d5760405162461bcd60e51b815260206004820152600e60248201526d3430b9b41031b7b63634b9b4b7b760911b604482015260640161022c565b60008281526020819052604090208190556107ae60a0840160808501610fde565b6107bc90633b9aca0061101e565b6107d09067ffffffffffffffff168661104e565b945050505080806107e090611066565b915050610612565b5060408051336024820152306044820152606480820184905282518083039091018152608490910182526020810180516001600160e01b03166323b872dd60e01b17905290516000916060916001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016916108689161107f565b6000604051808303816000865af19150503d80600081146108a5576040519150601f19603f3d011682016040523d82523d6000602084013e6108aa565b606091505b5090925090508180156108d55750805115806108d55750808060200190518101906108d591906110ba565b61052c5760405162461bcd60e51b81526020600482015260146024820152731d1c985b9cd9995c88199c9bdb4819985a5b195960621b604482015260640161022c565b60008160028460405160200161093091815260200190565b60408051601f198184030181529082905261094a9161107f565b602060405180830381855afa158015610967573d6000803e3d6000fd5b5050506040513d601f19601f8201168201806040525081019061098a91906110dc565b149392505050565b60008060006109a084610dbe565b9250925050806000141580156105e757506109bc828535610918565b15949350505050565b3233146109e45760405162461bcd60e51b815260040161022c90610f6e565b6109f46060820160408301610fde565b67ffffffffffffffff16421015610a445760405162461bcd60e51b81526020600482015260146024820152731b1bd8dadd1a5b59481b9bdd08195e1c1a5c995960621b604482015260640161022c565b6000806000610a5284610dbe565b925092509250600081118015610a685750438111155b610aa65760405162461bcd60e51b815260206004820152600f60248201526e73776170206e6f742061637469766560881b604482015260640161022c565b610ab1828535610918565b15610af65760405162461bcd60e51b81526020600482015260156024820152741cddd85c08185b1c9958591e481c995919595b5959605a1b604482015260640161022c565b60018201610b3e5760405162461bcd60e51b81526020600482015260156024820152741cddd85c08185b1c9958591e481c99599d5b991959605a1b604482015260640161022c565b6000838152602081905260408120600019905560606001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000167fa9059cbb2ab09eb219583f4a59a5d0623ade346d962bcd4e46b11da047c9049b33610baf60a08a0160808b01610fde565b6040516001600160a01b03909216602483015267ffffffffffffffff16604482015260640160408051601f198184030181529181526020820180516001600160e01b03166001600160e01b0319909416939093179092529051610c12919061107f565b6000604051808303816000865af19150503d8060008114610c4f576040519150601f19603f3d011682016040523d82523d6000602084013e610c54565b606091505b509092509050818015610c7f575080511580610c7f575080806020019051810190610c7f91906110ba565b610cbd5760405162461bcd60e51b815260206004820152600f60248201526e1d1c985b9cd9995c8819985a5b1959608a1b604482015260640161022c565b505050505050565b600060028235610cdb6040850160208601610fae565b60601b846060016020810190610cf19190610fae565b60601b610d0460a0870160808801610fde565b60c01b610d176060880160408901610fde565b6040805160208101969096526bffffffffffffffffffffffff19948516908601529190921660548401526001600160c01b0319918216606884015260c01b16607082015260780160408051601f1981840301815290829052610d789161107f565b602060405180830381855afa158015610d95573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190610db891906110dc565b92915050565b600080600080610dcd85610cc5565b600081815260208190526040902054909690955085945092505050565b60008060208385031215610dfd57600080fd5b823567ffffffffffffffff80821115610e1557600080fd5b818501915085601f830112610e2957600080fd5b813581811115610e3857600080fd5b86602060c083028501011115610e4d57600080fd5b60209290920196919550909350505050565b600060a08284031215610e7157600080fd5b50919050565b634e487b7160e01b600052602160045260246000fd5b8151606082019060048110610eb257634e487b7160e01b600052602160045260246000fd5b80835250602083015160208301526040830151604083015292915050565b60008060208385031215610ee357600080fd5b823567ffffffffffffffff80821115610efb57600080fd5b818501915085601f830112610f0f57600080fd5b813581811115610f1e57600080fd5b86602060a083028501011115610e4d57600080fd5b60008060408385031215610f4657600080fd5b50508035926020909101359150565b600060208284031215610f6757600080fd5b5035919050565b60208082526010908201526f39b2b73232b910109e9037b934b3b4b760811b604082015260600190565b634e487b7160e01b600052603260045260246000fd5b600060208284031215610fc057600080fd5b81356001600160a01b0381168114610fd757600080fd5b9392505050565b600060208284031215610ff057600080fd5b813567ffffffffffffffff81168114610fd757600080fd5b634e487b7160e01b600052601160045260246000fd5b600067ffffffffffffffff8083168185168183048111821515161561104557611045611008565b02949350505050565b6000821982111561106157611061611008565b500190565b60006001820161107857611078611008565b5060010190565b6000825160005b818110156110a05760208186018101518583015201611086565b818111156110af576000828501525b509190910192915050565b6000602082840312156110cc57600080fd5b81518015158114610fd757600080fd5b6000602082840312156110ee57600080fd5b505191905056fea2646970667358221220319d89b87a0d5782925310133ed5d12d2ff562b0786611308396804fcad176fc64736f6c634300080f0033" +const ERC20SwapRuntimeBin = "6080604052600436106100865760003560e01c80638cd8dd97116100595780638cd8dd9714610141578063a76f9f2d14610161578063d5cfd0491461018f578063db3b419c146101af578063eb84e7f2146101dc57600080fd5b806323f0388b1461008b5780633da59631146100ad57806377d7e031146100c05780638c8e8fee146100f5575b600080fd5b34801561009757600080fd5b506100ab6100a6366004610d69565b610209565b005b6100ab6100bb366004610dde565b61050c565b3480156100cc57600080fd5b506100e06100db366004610e41565b61087a565b60405190151581526020015b60405180910390f35b34801561010157600080fd5b506101297f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016100ec565b34801561014d57600080fd5b506100ab61015c366004610e63565b6108f6565b34801561016d57600080fd5b5061018161017c366004610e63565b610b66565b6040519081526020016100ec565b34801561019b57600080fd5b506100e06101aa366004610e63565b610c4e565b3480156101bb57600080fd5b506101cf6101ca366004610e63565b610c82565b6040516100ec9190610e91565b3480156101e857600080fd5b506101816101f7366004610ed4565b60006020819052908152604090205481565b3233146102315760405162461bcd60e51b815260040161022890610eed565b60405180910390fd5b6000805b828110156103e0573684848381811061025057610250610f17565b60c00291909101915033905061026c60a0830160808401610f2d565b6001600160a01b0316146102af5760405162461bcd60e51b815260206004820152600a6024820152691b9bdd08185d5d1a195960b21b6044820152606401610228565b600080806102bc84610d3d565b9250925092506000811180156102d157504381105b61030d5760405162461bcd60e51b815260206004820152600d60248201526c0756e66696c6c6564207377617609c1b6044820152606401610228565b61031882853561087a565b156103585760405162461bcd60e51b815260206004820152601060248201526f185b1c9958591e481c995919595b595960821b6044820152606401610228565b61036760a0850135853561087a565b6103a45760405162461bcd60e51b815260206004820152600e60248201526d1a5b9d985b1a59081cd958dc995d60921b6044820152606401610228565b60008381526020818152604090912060a086013590556103c79085013587610f73565b95505050505080806103d890610f86565b915050610235565b5060408051336024820152604480820184905282518083039091018152606490910182526020810180516001600160e01b031663a9059cbb60e01b17905290516000916060916001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169161045a91610f9f565b6000604051808303816000865af19150503d8060008114610497576040519150601f19603f3d011682016040523d82523d6000602084013e61049c565b606091505b5090925090508180156104c75750805115806104c75750808060200190518101906104c79190610fce565b6105055760405162461bcd60e51b815260206004820152600f60248201526e1d1c985b9cd9995c8819985a5b1959608a1b6044820152606401610228565b5050505050565b32331461052b5760405162461bcd60e51b815260040161022890610eed565b6000805b8281101561074a573684848381811061054a5761054a610f17565b905060a002019050600081602001351161058e5760405162461bcd60e51b81526020600482015260056024820152640c081d985b60da1b6044820152606401610228565b60006105a06080830160608401610ff0565b67ffffffffffffffff16116105eb5760405162461bcd60e51b815260206004820152601160248201527003020726566756e6454696d657374616d7607c1b6044820152606401610228565b7f5069ec89f08d9ca0424bb5a5f59c3c60ed50cf06af5911a368e41e771763bfaf81350161066c5760405162461bcd60e51b815260206004820152602860248201527f696c6c6567616c2073656372657420686173682028726566756e64207265636f604482015267726420686173682960c01b6064820152608401610228565b600061067782610b66565b60008181526020819052604090205490915080156106c85760405162461bcd60e51b815260206004820152600e60248201526d73776170206e6f7420656d70747960901b6044820152606401610228565b50436106d581843561087a565b156107135760405162461bcd60e51b815260206004820152600e60248201526d3430b9b41031b7b63634b9b4b7b760911b6044820152606401610228565b6000828152602081815260409091208290556107329084013586610f73565b9450505050808061074290610f86565b91505061052f565b5060408051336024820152306044820152606480820184905282518083039091018152608490910182526020810180516001600160e01b03166323b872dd60e01b17905290516000916060916001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016916107ca91610f9f565b6000604051808303816000865af19150503d8060008114610807576040519150601f19603f3d011682016040523d82523d6000602084013e61080c565b606091505b5090925090508180156108375750805115806108375750808060200190518101906108379190610fce565b6105055760405162461bcd60e51b81526020600482015260146024820152731d1c985b9cd9995c88199c9bdb4819985a5b195960621b6044820152606401610228565b60008160028460405160200161089291815260200190565b60408051601f19818403018152908290526108ac91610f9f565b602060405180830381855afa1580156108c9573d6000803e3d6000fd5b5050506040513d601f19601f820116820180604052508101906108ec919061101a565b1490505b92915050565b3233146109155760405162461bcd60e51b815260040161022890610eed565b6109256080820160608301610ff0565b67ffffffffffffffff164210156109755760405162461bcd60e51b81526020600482015260146024820152731b1bd8dadd1a5b59481b9bdd08195e1c1a5c995960621b6044820152606401610228565b600080600061098384610d3d565b9250925092506000811180156109995750438111155b6109d75760405162461bcd60e51b815260206004820152600f60248201526e73776170206e6f742061637469766560881b6044820152606401610228565b6109e282853561087a565b15610a275760405162461bcd60e51b81526020600482015260156024820152741cddd85c08185b1c9958591e481c995919595b5959605a1b6044820152606401610228565b6000838152602081815260408083206000199055805133602482015287830135604480830191909152825180830390910181526064909101825291820180516001600160e01b031663a9059cbb60e01b179052516060917f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031691610ab39190610f9f565b6000604051808303816000865af19150503d8060008114610af0576040519150601f19603f3d011682016040523d82523d6000602084013e610af5565b606091505b509092509050818015610b20575080511580610b20575080806020019051810190610b209190610fce565b610b5e5760405162461bcd60e51b815260206004820152600f60248201526e1d1c985b9cd9995c8819985a5b1959608a1b6044820152606401610228565b505050505050565b600060028235610b7c6060850160408601610f2d565b60601b610b8f60a0860160808701610f2d565b60601b856020013560001b866060016020810190610bad9190610ff0565b6040805160208101969096526bffffffffffffffffffffffff199485169086015292909116605484015260688301526001600160c01b031960c09190911b16608882015260900160408051601f1981840301815290829052610c0e91610f9f565b602060405180830381855afa158015610c2b573d6000803e3d6000fd5b5050506040513d601f19601f820116820180604052508101906108f0919061101a565b6000806000610c5c84610d3d565b925092505080600014158015610c7a5750610c7882853561087a565b155b949350505050565b604080516060810182526000808252602082018190529181018290529080610ca984610d3d565b9250925050610cd36040805160608101909152806000815260006020820181905260409091015290565b81600003610cfa578060005b90816003811115610cf257610cf2610e7b565b905250610c7a565b60018301610d0a57806003610cdf565b610d1583863561087a565b15610d2a576002815260208101839052610c7a565b6001815260408101919091529392505050565b600080600080610d4c85610b66565b600081815260208190526040902054909690955085945092505050565b60008060208385031215610d7c57600080fd5b823567ffffffffffffffff80821115610d9457600080fd5b818501915085601f830112610da857600080fd5b813581811115610db757600080fd5b86602060c083028501011115610dcc57600080fd5b60209290920196919550909350505050565b60008060208385031215610df157600080fd5b823567ffffffffffffffff80821115610e0957600080fd5b818501915085601f830112610e1d57600080fd5b813581811115610e2c57600080fd5b86602060a083028501011115610dcc57600080fd5b60008060408385031215610e5457600080fd5b50508035926020909101359150565b600060a08284031215610e7557600080fd5b50919050565b634e487b7160e01b600052602160045260246000fd5b8151606082019060048110610eb657634e487b7160e01b600052602160045260246000fd5b80835250602083015160208301526040830151604083015292915050565b600060208284031215610ee657600080fd5b5035919050565b60208082526010908201526f39b2b73232b910109e9037b934b3b4b760811b604082015260600190565b634e487b7160e01b600052603260045260246000fd5b600060208284031215610f3f57600080fd5b81356001600160a01b0381168114610f5657600080fd5b9392505050565b634e487b7160e01b600052601160045260246000fd5b808201808211156108f0576108f0610f5d565b600060018201610f9857610f98610f5d565b5060010190565b6000825160005b81811015610fc05760208186018101518583015201610fa6565b506000920191825250919050565b600060208284031215610fe057600080fd5b81518015158114610f5657600080fd5b60006020828403121561100257600080fd5b813567ffffffffffffffff81168114610f5657600080fd5b60006020828403121561102c57600080fd5b505191905056fea2646970667358221220acfd007366a18ada193efab33a7139cee67cfd39d1c89075b5534cc57862baa464736f6c63430008120033" diff --git a/dex/networks/erc20/contracts/v1/contract.go b/dex/networks/erc20/contracts/v1/contract.go index 7afaf0f4f0..68eeb5d44d 100644 --- a/dex/networks/erc20/contracts/v1/contract.go +++ b/dex/networks/erc20/contracts/v1/contract.go @@ -31,8 +31,8 @@ var ( // ERC20SwapMetaData contains all meta data concerning the ERC20Swap contract. var ERC20SwapMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"secretHash\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"initiator\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"refundTimestamp\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"participant\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"value\",\"type\":\"uint64\"}],\"internalType\":\"structERC20Swap.Vector\",\"name\":\"v\",\"type\":\"tuple\"}],\"name\":\"contractKey\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"secretHash\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"initiator\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"refundTimestamp\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"participant\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"value\",\"type\":\"uint64\"}],\"internalType\":\"structERC20Swap.Vector[]\",\"name\":\"contracts\",\"type\":\"tuple[]\"}],\"name\":\"initiate\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"secretHash\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"initiator\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"refundTimestamp\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"participant\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"value\",\"type\":\"uint64\"}],\"internalType\":\"structERC20Swap.Vector\",\"name\":\"v\",\"type\":\"tuple\"}],\"name\":\"isRedeemable\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"secretHash\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"initiator\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"refundTimestamp\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"participant\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"value\",\"type\":\"uint64\"}],\"internalType\":\"structERC20Swap.Vector\",\"name\":\"v\",\"type\":\"tuple\"},{\"internalType\":\"bytes32\",\"name\":\"secret\",\"type\":\"bytes32\"}],\"internalType\":\"structERC20Swap.Redemption[]\",\"name\":\"redemptions\",\"type\":\"tuple[]\"}],\"name\":\"redeem\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"secretHash\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"initiator\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"refundTimestamp\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"participant\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"value\",\"type\":\"uint64\"}],\"internalType\":\"structERC20Swap.Vector\",\"name\":\"v\",\"type\":\"tuple\"}],\"name\":\"refund\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"secret\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"secretHash\",\"type\":\"bytes32\"}],\"name\":\"secretValidates\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"secretHash\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"initiator\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"refundTimestamp\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"participant\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"value\",\"type\":\"uint64\"}],\"internalType\":\"structERC20Swap.Vector\",\"name\":\"v\",\"type\":\"tuple\"}],\"name\":\"status\",\"outputs\":[{\"components\":[{\"internalType\":\"enumERC20Swap.Step\",\"name\":\"step\",\"type\":\"uint8\"},{\"internalType\":\"bytes32\",\"name\":\"secret\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"blockNumber\",\"type\":\"uint256\"}],\"internalType\":\"structERC20Swap.Status\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"swaps\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"token_address\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", - Bin: "0x60a060405234801561001057600080fd5b506040516111cb3803806111cb83398101604081905261002f91610040565b6001600160a01b0316608052610070565b60006020828403121561005257600080fd5b81516001600160a01b038116811461006957600080fd5b9392505050565b60805161112b6100a060003960008181610158015281816104570152818161083e0152610b5d015261112b6000f3fe6080604052600436106100865760003560e01c80637802689d116100595780637802689d146101265780638c8e8fee14610146578063d2544c0614610192578063eb84e7f2146101b2578063ed7cbed7146101ed57600080fd5b8063428b16e11461008b57806361a16e33146100ad57806364a97bff146100e357806377d7e031146100f6575b600080fd5b34801561009757600080fd5b506100ab6100a6366004610dea565b61020d565b005b3480156100b957600080fd5b506100cd6100c8366004610e5f565b610533565b6040516100da9190610e8d565b60405180910390f35b6100ab6100f1366004610ed0565b6105ef565b34801561010257600080fd5b50610116610111366004610f33565b610918565b60405190151581526020016100da565b34801561013257600080fd5b50610116610141366004610e5f565b610992565b34801561015257600080fd5b5061017a7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016100da565b34801561019e57600080fd5b506100ab6101ad366004610e5f565b6109c5565b3480156101be57600080fd5b506101df6101cd366004610f55565b60006020819052908152604090205481565b6040519081526020016100da565b3480156101f957600080fd5b506101df610208366004610e5f565b610cc5565b3233146102355760405162461bcd60e51b815260040161022c90610f6e565b60405180910390fd5b6000805b82811015610407573684848381811061025457610254610f98565b60c0029190910191503390506102706080830160608401610fae565b6001600160a01b0316146102b35760405162461bcd60e51b815260206004820152600a6024820152691b9bdd08185d5d1a195960b21b604482015260640161022c565b600080806102c084610dbe565b9250925092506000811180156102d557504381105b6103115760405162461bcd60e51b815260206004820152600d60248201526c0756e66696c6c6564207377617609c1b604482015260640161022c565b61031c828535610918565b1561035c5760405162461bcd60e51b815260206004820152601060248201526f185b1c9958591e481c995919595b595960821b604482015260640161022c565b61036b60a08501358535610918565b6103a85760405162461bcd60e51b815260206004820152600e60248201526d1a5b9d985b1a59081cd958dc995d60921b604482015260640161022c565b600083815260208190526040902060a0850180359091556103cc9060808601610fde565b6103da90633b9aca0061101e565b6103ee9067ffffffffffffffff168761104e565b95505050505080806103ff90611066565b915050610239565b5060408051336024820152604480820184905282518083039091018152606490910182526020810180516001600160e01b031663a9059cbb60e01b17905290516000916060916001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016916104819161107f565b6000604051808303816000865af19150503d80600081146104be576040519150601f19603f3d011682016040523d82523d6000602084013e6104c3565b606091505b5090925090508180156104ee5750805115806104ee5750808060200190518101906104ee91906110ba565b61052c5760405162461bcd60e51b815260206004820152600f60248201526e1d1c985b9cd9995c8819985a5b1959608a1b604482015260640161022c565b5050505050565b60408051606081018252600080825260208201819052918101829052908061055a84610dbe565b92509250506105846040805160608101909152806000815260006020820181905260409091015290565b816000036105ab578060005b908160038111156105a3576105a3610e77565b9052506105e7565b600183016105bb57806003610590565b6105c6838635610918565b156105db5760028152602081018390526105e7565b60018152604081018290525b949350505050565b32331461060e5760405162461bcd60e51b815260040161022c90610f6e565b6000805b828110156107e8573684848381811061062d5761062d610f98565b905060a002019050600081608001602081019061064a9190610fde565b67ffffffffffffffff16116106895760405162461bcd60e51b81526020600482015260056024820152640c081d985b60da1b604482015260640161022c565b600061069b6060830160408401610fde565b67ffffffffffffffff16116106e65760405162461bcd60e51b815260206004820152601160248201527003020726566756e6454696d657374616d7607c1b604482015260640161022c565b60006106f182610cc5565b60008181526020819052604090205490915080156107425760405162461bcd60e51b815260206004820152600e60248201526d73776170206e6f7420656d70747960901b604482015260640161022c565b504361074f818435610918565b1561078d5760405162461bcd60e51b815260206004820152600e60248201526d3430b9b41031b7b63634b9b4b7b760911b604482015260640161022c565b60008281526020819052604090208190556107ae60a0840160808501610fde565b6107bc90633b9aca0061101e565b6107d09067ffffffffffffffff168661104e565b945050505080806107e090611066565b915050610612565b5060408051336024820152306044820152606480820184905282518083039091018152608490910182526020810180516001600160e01b03166323b872dd60e01b17905290516000916060916001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016916108689161107f565b6000604051808303816000865af19150503d80600081146108a5576040519150601f19603f3d011682016040523d82523d6000602084013e6108aa565b606091505b5090925090508180156108d55750805115806108d55750808060200190518101906108d591906110ba565b61052c5760405162461bcd60e51b81526020600482015260146024820152731d1c985b9cd9995c88199c9bdb4819985a5b195960621b604482015260640161022c565b60008160028460405160200161093091815260200190565b60408051601f198184030181529082905261094a9161107f565b602060405180830381855afa158015610967573d6000803e3d6000fd5b5050506040513d601f19601f8201168201806040525081019061098a91906110dc565b149392505050565b60008060006109a084610dbe565b9250925050806000141580156105e757506109bc828535610918565b15949350505050565b3233146109e45760405162461bcd60e51b815260040161022c90610f6e565b6109f46060820160408301610fde565b67ffffffffffffffff16421015610a445760405162461bcd60e51b81526020600482015260146024820152731b1bd8dadd1a5b59481b9bdd08195e1c1a5c995960621b604482015260640161022c565b6000806000610a5284610dbe565b925092509250600081118015610a685750438111155b610aa65760405162461bcd60e51b815260206004820152600f60248201526e73776170206e6f742061637469766560881b604482015260640161022c565b610ab1828535610918565b15610af65760405162461bcd60e51b81526020600482015260156024820152741cddd85c08185b1c9958591e481c995919595b5959605a1b604482015260640161022c565b60018201610b3e5760405162461bcd60e51b81526020600482015260156024820152741cddd85c08185b1c9958591e481c99599d5b991959605a1b604482015260640161022c565b6000838152602081905260408120600019905560606001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000167fa9059cbb2ab09eb219583f4a59a5d0623ade346d962bcd4e46b11da047c9049b33610baf60a08a0160808b01610fde565b6040516001600160a01b03909216602483015267ffffffffffffffff16604482015260640160408051601f198184030181529181526020820180516001600160e01b03166001600160e01b0319909416939093179092529051610c12919061107f565b6000604051808303816000865af19150503d8060008114610c4f576040519150601f19603f3d011682016040523d82523d6000602084013e610c54565b606091505b509092509050818015610c7f575080511580610c7f575080806020019051810190610c7f91906110ba565b610cbd5760405162461bcd60e51b815260206004820152600f60248201526e1d1c985b9cd9995c8819985a5b1959608a1b604482015260640161022c565b505050505050565b600060028235610cdb6040850160208601610fae565b60601b846060016020810190610cf19190610fae565b60601b610d0460a0870160808801610fde565b60c01b610d176060880160408901610fde565b6040805160208101969096526bffffffffffffffffffffffff19948516908601529190921660548401526001600160c01b0319918216606884015260c01b16607082015260780160408051601f1981840301815290829052610d789161107f565b602060405180830381855afa158015610d95573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190610db891906110dc565b92915050565b600080600080610dcd85610cc5565b600081815260208190526040902054909690955085945092505050565b60008060208385031215610dfd57600080fd5b823567ffffffffffffffff80821115610e1557600080fd5b818501915085601f830112610e2957600080fd5b813581811115610e3857600080fd5b86602060c083028501011115610e4d57600080fd5b60209290920196919550909350505050565b600060a08284031215610e7157600080fd5b50919050565b634e487b7160e01b600052602160045260246000fd5b8151606082019060048110610eb257634e487b7160e01b600052602160045260246000fd5b80835250602083015160208301526040830151604083015292915050565b60008060208385031215610ee357600080fd5b823567ffffffffffffffff80821115610efb57600080fd5b818501915085601f830112610f0f57600080fd5b813581811115610f1e57600080fd5b86602060a083028501011115610e4d57600080fd5b60008060408385031215610f4657600080fd5b50508035926020909101359150565b600060208284031215610f6757600080fd5b5035919050565b60208082526010908201526f39b2b73232b910109e9037b934b3b4b760811b604082015260600190565b634e487b7160e01b600052603260045260246000fd5b600060208284031215610fc057600080fd5b81356001600160a01b0381168114610fd757600080fd5b9392505050565b600060208284031215610ff057600080fd5b813567ffffffffffffffff81168114610fd757600080fd5b634e487b7160e01b600052601160045260246000fd5b600067ffffffffffffffff8083168185168183048111821515161561104557611045611008565b02949350505050565b6000821982111561106157611061611008565b500190565b60006001820161107857611078611008565b5060010190565b6000825160005b818110156110a05760208186018101518583015201611086565b818111156110af576000828501525b509190910192915050565b6000602082840312156110cc57600080fd5b81518015158114610fd757600080fd5b6000602082840312156110ee57600080fd5b505191905056fea2646970667358221220319d89b87a0d5782925310133ed5d12d2ff562b0786611308396804fcad176fc64736f6c634300080f0033", + ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"secretHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"initiator\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"refundTimestamp\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"participant\",\"type\":\"address\"}],\"internalType\":\"structERC20Swap.Vector\",\"name\":\"v\",\"type\":\"tuple\"}],\"name\":\"contractKey\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"secretHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"initiator\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"refundTimestamp\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"participant\",\"type\":\"address\"}],\"internalType\":\"structERC20Swap.Vector[]\",\"name\":\"contracts\",\"type\":\"tuple[]\"}],\"name\":\"initiate\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"secretHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"initiator\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"refundTimestamp\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"participant\",\"type\":\"address\"}],\"internalType\":\"structERC20Swap.Vector\",\"name\":\"v\",\"type\":\"tuple\"}],\"name\":\"isRedeemable\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"secretHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"initiator\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"refundTimestamp\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"participant\",\"type\":\"address\"}],\"internalType\":\"structERC20Swap.Vector\",\"name\":\"v\",\"type\":\"tuple\"},{\"internalType\":\"bytes32\",\"name\":\"secret\",\"type\":\"bytes32\"}],\"internalType\":\"structERC20Swap.Redemption[]\",\"name\":\"redemptions\",\"type\":\"tuple[]\"}],\"name\":\"redeem\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"secretHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"initiator\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"refundTimestamp\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"participant\",\"type\":\"address\"}],\"internalType\":\"structERC20Swap.Vector\",\"name\":\"v\",\"type\":\"tuple\"}],\"name\":\"refund\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"secret\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"secretHash\",\"type\":\"bytes32\"}],\"name\":\"secretValidates\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"secretHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"initiator\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"refundTimestamp\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"participant\",\"type\":\"address\"}],\"internalType\":\"structERC20Swap.Vector\",\"name\":\"v\",\"type\":\"tuple\"}],\"name\":\"status\",\"outputs\":[{\"components\":[{\"internalType\":\"enumERC20Swap.Step\",\"name\":\"step\",\"type\":\"uint8\"},{\"internalType\":\"bytes32\",\"name\":\"secret\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"blockNumber\",\"type\":\"uint256\"}],\"internalType\":\"structERC20Swap.Status\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"swaps\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"token_address\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", + Bin: "0x60a060405234801561001057600080fd5b5060405161110938038061110983398101604081905261002f91610040565b6001600160a01b0316608052610070565b60006020828403121561005257600080fd5b81516001600160a01b038116811461006957600080fd5b9392505050565b6080516110696100a06000396000818161010701528181610430015281816107a00152610a8001526110696000f3fe6080604052600436106100865760003560e01c80638cd8dd97116100595780638cd8dd9714610141578063a76f9f2d14610161578063d5cfd0491461018f578063db3b419c146101af578063eb84e7f2146101dc57600080fd5b806323f0388b1461008b5780633da59631146100ad57806377d7e031146100c05780638c8e8fee146100f5575b600080fd5b34801561009757600080fd5b506100ab6100a6366004610d69565b610209565b005b6100ab6100bb366004610dde565b61050c565b3480156100cc57600080fd5b506100e06100db366004610e41565b61087a565b60405190151581526020015b60405180910390f35b34801561010157600080fd5b506101297f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016100ec565b34801561014d57600080fd5b506100ab61015c366004610e63565b6108f6565b34801561016d57600080fd5b5061018161017c366004610e63565b610b66565b6040519081526020016100ec565b34801561019b57600080fd5b506100e06101aa366004610e63565b610c4e565b3480156101bb57600080fd5b506101cf6101ca366004610e63565b610c82565b6040516100ec9190610e91565b3480156101e857600080fd5b506101816101f7366004610ed4565b60006020819052908152604090205481565b3233146102315760405162461bcd60e51b815260040161022890610eed565b60405180910390fd5b6000805b828110156103e0573684848381811061025057610250610f17565b60c00291909101915033905061026c60a0830160808401610f2d565b6001600160a01b0316146102af5760405162461bcd60e51b815260206004820152600a6024820152691b9bdd08185d5d1a195960b21b6044820152606401610228565b600080806102bc84610d3d565b9250925092506000811180156102d157504381105b61030d5760405162461bcd60e51b815260206004820152600d60248201526c0756e66696c6c6564207377617609c1b6044820152606401610228565b61031882853561087a565b156103585760405162461bcd60e51b815260206004820152601060248201526f185b1c9958591e481c995919595b595960821b6044820152606401610228565b61036760a0850135853561087a565b6103a45760405162461bcd60e51b815260206004820152600e60248201526d1a5b9d985b1a59081cd958dc995d60921b6044820152606401610228565b60008381526020818152604090912060a086013590556103c79085013587610f73565b95505050505080806103d890610f86565b915050610235565b5060408051336024820152604480820184905282518083039091018152606490910182526020810180516001600160e01b031663a9059cbb60e01b17905290516000916060916001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169161045a91610f9f565b6000604051808303816000865af19150503d8060008114610497576040519150601f19603f3d011682016040523d82523d6000602084013e61049c565b606091505b5090925090508180156104c75750805115806104c75750808060200190518101906104c79190610fce565b6105055760405162461bcd60e51b815260206004820152600f60248201526e1d1c985b9cd9995c8819985a5b1959608a1b6044820152606401610228565b5050505050565b32331461052b5760405162461bcd60e51b815260040161022890610eed565b6000805b8281101561074a573684848381811061054a5761054a610f17565b905060a002019050600081602001351161058e5760405162461bcd60e51b81526020600482015260056024820152640c081d985b60da1b6044820152606401610228565b60006105a06080830160608401610ff0565b67ffffffffffffffff16116105eb5760405162461bcd60e51b815260206004820152601160248201527003020726566756e6454696d657374616d7607c1b6044820152606401610228565b7f5069ec89f08d9ca0424bb5a5f59c3c60ed50cf06af5911a368e41e771763bfaf81350161066c5760405162461bcd60e51b815260206004820152602860248201527f696c6c6567616c2073656372657420686173682028726566756e64207265636f604482015267726420686173682960c01b6064820152608401610228565b600061067782610b66565b60008181526020819052604090205490915080156106c85760405162461bcd60e51b815260206004820152600e60248201526d73776170206e6f7420656d70747960901b6044820152606401610228565b50436106d581843561087a565b156107135760405162461bcd60e51b815260206004820152600e60248201526d3430b9b41031b7b63634b9b4b7b760911b6044820152606401610228565b6000828152602081815260409091208290556107329084013586610f73565b9450505050808061074290610f86565b91505061052f565b5060408051336024820152306044820152606480820184905282518083039091018152608490910182526020810180516001600160e01b03166323b872dd60e01b17905290516000916060916001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016916107ca91610f9f565b6000604051808303816000865af19150503d8060008114610807576040519150601f19603f3d011682016040523d82523d6000602084013e61080c565b606091505b5090925090508180156108375750805115806108375750808060200190518101906108379190610fce565b6105055760405162461bcd60e51b81526020600482015260146024820152731d1c985b9cd9995c88199c9bdb4819985a5b195960621b6044820152606401610228565b60008160028460405160200161089291815260200190565b60408051601f19818403018152908290526108ac91610f9f565b602060405180830381855afa1580156108c9573d6000803e3d6000fd5b5050506040513d601f19601f820116820180604052508101906108ec919061101a565b1490505b92915050565b3233146109155760405162461bcd60e51b815260040161022890610eed565b6109256080820160608301610ff0565b67ffffffffffffffff164210156109755760405162461bcd60e51b81526020600482015260146024820152731b1bd8dadd1a5b59481b9bdd08195e1c1a5c995960621b6044820152606401610228565b600080600061098384610d3d565b9250925092506000811180156109995750438111155b6109d75760405162461bcd60e51b815260206004820152600f60248201526e73776170206e6f742061637469766560881b6044820152606401610228565b6109e282853561087a565b15610a275760405162461bcd60e51b81526020600482015260156024820152741cddd85c08185b1c9958591e481c995919595b5959605a1b6044820152606401610228565b6000838152602081815260408083206000199055805133602482015287830135604480830191909152825180830390910181526064909101825291820180516001600160e01b031663a9059cbb60e01b179052516060917f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031691610ab39190610f9f565b6000604051808303816000865af19150503d8060008114610af0576040519150601f19603f3d011682016040523d82523d6000602084013e610af5565b606091505b509092509050818015610b20575080511580610b20575080806020019051810190610b209190610fce565b610b5e5760405162461bcd60e51b815260206004820152600f60248201526e1d1c985b9cd9995c8819985a5b1959608a1b6044820152606401610228565b505050505050565b600060028235610b7c6060850160408601610f2d565b60601b610b8f60a0860160808701610f2d565b60601b856020013560001b866060016020810190610bad9190610ff0565b6040805160208101969096526bffffffffffffffffffffffff199485169086015292909116605484015260688301526001600160c01b031960c09190911b16608882015260900160408051601f1981840301815290829052610c0e91610f9f565b602060405180830381855afa158015610c2b573d6000803e3d6000fd5b5050506040513d601f19601f820116820180604052508101906108f0919061101a565b6000806000610c5c84610d3d565b925092505080600014158015610c7a5750610c7882853561087a565b155b949350505050565b604080516060810182526000808252602082018190529181018290529080610ca984610d3d565b9250925050610cd36040805160608101909152806000815260006020820181905260409091015290565b81600003610cfa578060005b90816003811115610cf257610cf2610e7b565b905250610c7a565b60018301610d0a57806003610cdf565b610d1583863561087a565b15610d2a576002815260208101839052610c7a565b6001815260408101919091529392505050565b600080600080610d4c85610b66565b600081815260208190526040902054909690955085945092505050565b60008060208385031215610d7c57600080fd5b823567ffffffffffffffff80821115610d9457600080fd5b818501915085601f830112610da857600080fd5b813581811115610db757600080fd5b86602060c083028501011115610dcc57600080fd5b60209290920196919550909350505050565b60008060208385031215610df157600080fd5b823567ffffffffffffffff80821115610e0957600080fd5b818501915085601f830112610e1d57600080fd5b813581811115610e2c57600080fd5b86602060a083028501011115610dcc57600080fd5b60008060408385031215610e5457600080fd5b50508035926020909101359150565b600060a08284031215610e7557600080fd5b50919050565b634e487b7160e01b600052602160045260246000fd5b8151606082019060048110610eb657634e487b7160e01b600052602160045260246000fd5b80835250602083015160208301526040830151604083015292915050565b600060208284031215610ee657600080fd5b5035919050565b60208082526010908201526f39b2b73232b910109e9037b934b3b4b760811b604082015260600190565b634e487b7160e01b600052603260045260246000fd5b600060208284031215610f3f57600080fd5b81356001600160a01b0381168114610f5657600080fd5b9392505050565b634e487b7160e01b600052601160045260246000fd5b808201808211156108f0576108f0610f5d565b600060018201610f9857610f98610f5d565b5060010190565b6000825160005b81811015610fc05760208186018101518583015201610fa6565b506000920191825250919050565b600060208284031215610fe057600080fd5b81518015158114610f5657600080fd5b60006020828403121561100257600080fd5b813567ffffffffffffffff81168114610f5657600080fd5b60006020828403121561102c57600080fd5b505191905056fea2646970667358221220acfd007366a18ada193efab33a7139cee67cfd39d1c89075b5534cc57862baa464736f6c63430008120033", } // ERC20SwapABI is the input ABI used to generate the binding from. @@ -202,9 +202,9 @@ func (_ERC20Swap *ERC20SwapTransactorRaw) Transact(opts *bind.TransactOpts, meth return _ERC20Swap.Contract.contract.Transact(opts, method, params...) } -// ContractKey is a free data retrieval call binding the contract method 0xed7cbed7. +// ContractKey is a free data retrieval call binding the contract method 0xa76f9f2d. // -// Solidity: function contractKey((bytes32,address,uint64,address,uint64) v) pure returns(bytes32) +// Solidity: function contractKey((bytes32,uint256,address,uint64,address) v) pure returns(bytes32) func (_ERC20Swap *ERC20SwapCaller) ContractKey(opts *bind.CallOpts, v ethv1.ETHSwapVector) ([32]byte, error) { var out []interface{} err := _ERC20Swap.contract.Call(opts, &out, "contractKey", v) @@ -219,23 +219,23 @@ func (_ERC20Swap *ERC20SwapCaller) ContractKey(opts *bind.CallOpts, v ethv1.ETHS } -// ContractKey is a free data retrieval call binding the contract method 0xed7cbed7. +// ContractKey is a free data retrieval call binding the contract method 0xa76f9f2d. // -// Solidity: function contractKey((bytes32,address,uint64,address,uint64) v) pure returns(bytes32) +// Solidity: function contractKey((bytes32,uint256,address,uint64,address) v) pure returns(bytes32) func (_ERC20Swap *ERC20SwapSession) ContractKey(v ethv1.ETHSwapVector) ([32]byte, error) { return _ERC20Swap.Contract.ContractKey(&_ERC20Swap.CallOpts, v) } -// ContractKey is a free data retrieval call binding the contract method 0xed7cbed7. +// ContractKey is a free data retrieval call binding the contract method 0xa76f9f2d. // -// Solidity: function contractKey((bytes32,address,uint64,address,uint64) v) pure returns(bytes32) +// Solidity: function contractKey((bytes32,uint256,address,uint64,address) v) pure returns(bytes32) func (_ERC20Swap *ERC20SwapCallerSession) ContractKey(v ethv1.ETHSwapVector) ([32]byte, error) { return _ERC20Swap.Contract.ContractKey(&_ERC20Swap.CallOpts, v) } -// IsRedeemable is a free data retrieval call binding the contract method 0x7802689d. +// IsRedeemable is a free data retrieval call binding the contract method 0xd5cfd049. // -// Solidity: function isRedeemable((bytes32,address,uint64,address,uint64) v) view returns(bool) +// Solidity: function isRedeemable((bytes32,uint256,address,uint64,address) v) view returns(bool) func (_ERC20Swap *ERC20SwapCaller) IsRedeemable(opts *bind.CallOpts, v ethv1.ETHSwapVector) (bool, error) { var out []interface{} err := _ERC20Swap.contract.Call(opts, &out, "isRedeemable", v) @@ -250,16 +250,16 @@ func (_ERC20Swap *ERC20SwapCaller) IsRedeemable(opts *bind.CallOpts, v ethv1.ETH } -// IsRedeemable is a free data retrieval call binding the contract method 0x7802689d. +// IsRedeemable is a free data retrieval call binding the contract method 0xd5cfd049. // -// Solidity: function isRedeemable((bytes32,address,uint64,address,uint64) v) view returns(bool) +// Solidity: function isRedeemable((bytes32,uint256,address,uint64,address) v) view returns(bool) func (_ERC20Swap *ERC20SwapSession) IsRedeemable(v ethv1.ETHSwapVector) (bool, error) { return _ERC20Swap.Contract.IsRedeemable(&_ERC20Swap.CallOpts, v) } -// IsRedeemable is a free data retrieval call binding the contract method 0x7802689d. +// IsRedeemable is a free data retrieval call binding the contract method 0xd5cfd049. // -// Solidity: function isRedeemable((bytes32,address,uint64,address,uint64) v) view returns(bool) +// Solidity: function isRedeemable((bytes32,uint256,address,uint64,address) v) view returns(bool) func (_ERC20Swap *ERC20SwapCallerSession) IsRedeemable(v ethv1.ETHSwapVector) (bool, error) { return _ERC20Swap.Contract.IsRedeemable(&_ERC20Swap.CallOpts, v) } @@ -295,9 +295,9 @@ func (_ERC20Swap *ERC20SwapCallerSession) SecretValidates(secret [32]byte, secre return _ERC20Swap.Contract.SecretValidates(&_ERC20Swap.CallOpts, secret, secretHash) } -// Status is a free data retrieval call binding the contract method 0x61a16e33. +// Status is a free data retrieval call binding the contract method 0xdb3b419c. // -// Solidity: function status((bytes32,address,uint64,address,uint64) v) view returns((uint8,bytes32,uint256)) +// Solidity: function status((bytes32,uint256,address,uint64,address) v) view returns((uint8,bytes32,uint256)) func (_ERC20Swap *ERC20SwapCaller) Status(opts *bind.CallOpts, v ethv1.ETHSwapVector) (ethv1.ETHSwapStatus, error) { var out []interface{} err := _ERC20Swap.contract.Call(opts, &out, "status", v) @@ -312,16 +312,16 @@ func (_ERC20Swap *ERC20SwapCaller) Status(opts *bind.CallOpts, v ethv1.ETHSwapVe } -// Status is a free data retrieval call binding the contract method 0x61a16e33. +// Status is a free data retrieval call binding the contract method 0xdb3b419c. // -// Solidity: function status((bytes32,address,uint64,address,uint64) v) view returns((uint8,bytes32,uint256)) +// Solidity: function status((bytes32,uint256,address,uint64,address) v) view returns((uint8,bytes32,uint256)) func (_ERC20Swap *ERC20SwapSession) Status(v ethv1.ETHSwapVector) (ethv1.ETHSwapStatus, error) { return _ERC20Swap.Contract.Status(&_ERC20Swap.CallOpts, v) } -// Status is a free data retrieval call binding the contract method 0x61a16e33. +// Status is a free data retrieval call binding the contract method 0xdb3b419c. // -// Solidity: function status((bytes32,address,uint64,address,uint64) v) view returns((uint8,bytes32,uint256)) +// Solidity: function status((bytes32,uint256,address,uint64,address) v) view returns((uint8,bytes32,uint256)) func (_ERC20Swap *ERC20SwapCallerSession) Status(v ethv1.ETHSwapVector) (ethv1.ETHSwapStatus, error) { return _ERC20Swap.Contract.Status(&_ERC20Swap.CallOpts, v) } @@ -388,65 +388,65 @@ func (_ERC20Swap *ERC20SwapCallerSession) TokenAddress() (common.Address, error) return _ERC20Swap.Contract.TokenAddress(&_ERC20Swap.CallOpts) } -// Initiate is a paid mutator transaction binding the contract method 0x64a97bff. +// Initiate is a paid mutator transaction binding the contract method 0x3da59631. // -// Solidity: function initiate((bytes32,address,uint64,address,uint64)[] contracts) payable returns() +// Solidity: function initiate((bytes32,uint256,address,uint64,address)[] contracts) payable returns() func (_ERC20Swap *ERC20SwapTransactor) Initiate(opts *bind.TransactOpts, contracts []ethv1.ETHSwapVector) (*types.Transaction, error) { return _ERC20Swap.contract.Transact(opts, "initiate", contracts) } -// Initiate is a paid mutator transaction binding the contract method 0x64a97bff. +// Initiate is a paid mutator transaction binding the contract method 0x3da59631. // -// Solidity: function initiate((bytes32,address,uint64,address,uint64)[] contracts) payable returns() +// Solidity: function initiate((bytes32,uint256,address,uint64,address)[] contracts) payable returns() func (_ERC20Swap *ERC20SwapSession) Initiate(contracts []ethv1.ETHSwapVector) (*types.Transaction, error) { return _ERC20Swap.Contract.Initiate(&_ERC20Swap.TransactOpts, contracts) } -// Initiate is a paid mutator transaction binding the contract method 0x64a97bff. +// Initiate is a paid mutator transaction binding the contract method 0x3da59631. // -// Solidity: function initiate((bytes32,address,uint64,address,uint64)[] contracts) payable returns() +// Solidity: function initiate((bytes32,uint256,address,uint64,address)[] contracts) payable returns() func (_ERC20Swap *ERC20SwapTransactorSession) Initiate(contracts []ethv1.ETHSwapVector) (*types.Transaction, error) { return _ERC20Swap.Contract.Initiate(&_ERC20Swap.TransactOpts, contracts) } -// Redeem is a paid mutator transaction binding the contract method 0x428b16e1. +// Redeem is a paid mutator transaction binding the contract method 0x23f0388b. // -// Solidity: function redeem(((bytes32,address,uint64,address,uint64),bytes32)[] redemptions) returns() +// Solidity: function redeem(((bytes32,uint256,address,uint64,address),bytes32)[] redemptions) returns() func (_ERC20Swap *ERC20SwapTransactor) Redeem(opts *bind.TransactOpts, redemptions []ethv1.ETHSwapRedemption) (*types.Transaction, error) { return _ERC20Swap.contract.Transact(opts, "redeem", redemptions) } -// Redeem is a paid mutator transaction binding the contract method 0x428b16e1. +// Redeem is a paid mutator transaction binding the contract method 0x23f0388b. // -// Solidity: function redeem(((bytes32,address,uint64,address,uint64),bytes32)[] redemptions) returns() +// Solidity: function redeem(((bytes32,uint256,address,uint64,address),bytes32)[] redemptions) returns() func (_ERC20Swap *ERC20SwapSession) Redeem(redemptions []ethv1.ETHSwapRedemption) (*types.Transaction, error) { return _ERC20Swap.Contract.Redeem(&_ERC20Swap.TransactOpts, redemptions) } -// Redeem is a paid mutator transaction binding the contract method 0x428b16e1. +// Redeem is a paid mutator transaction binding the contract method 0x23f0388b. // -// Solidity: function redeem(((bytes32,address,uint64,address,uint64),bytes32)[] redemptions) returns() +// Solidity: function redeem(((bytes32,uint256,address,uint64,address),bytes32)[] redemptions) returns() func (_ERC20Swap *ERC20SwapTransactorSession) Redeem(redemptions []ethv1.ETHSwapRedemption) (*types.Transaction, error) { return _ERC20Swap.Contract.Redeem(&_ERC20Swap.TransactOpts, redemptions) } -// Refund is a paid mutator transaction binding the contract method 0xd2544c06. +// Refund is a paid mutator transaction binding the contract method 0x8cd8dd97. // -// Solidity: function refund((bytes32,address,uint64,address,uint64) v) returns() +// Solidity: function refund((bytes32,uint256,address,uint64,address) v) returns() func (_ERC20Swap *ERC20SwapTransactor) Refund(opts *bind.TransactOpts, v ethv1.ETHSwapVector) (*types.Transaction, error) { return _ERC20Swap.contract.Transact(opts, "refund", v) } -// Refund is a paid mutator transaction binding the contract method 0xd2544c06. +// Refund is a paid mutator transaction binding the contract method 0x8cd8dd97. // -// Solidity: function refund((bytes32,address,uint64,address,uint64) v) returns() +// Solidity: function refund((bytes32,uint256,address,uint64,address) v) returns() func (_ERC20Swap *ERC20SwapSession) Refund(v ethv1.ETHSwapVector) (*types.Transaction, error) { return _ERC20Swap.Contract.Refund(&_ERC20Swap.TransactOpts, v) } -// Refund is a paid mutator transaction binding the contract method 0xd2544c06. +// Refund is a paid mutator transaction binding the contract method 0x8cd8dd97. // -// Solidity: function refund((bytes32,address,uint64,address,uint64) v) returns() +// Solidity: function refund((bytes32,uint256,address,uint64,address) v) returns() func (_ERC20Swap *ERC20SwapTransactorSession) Refund(v ethv1.ETHSwapVector) (*types.Transaction, error) { return _ERC20Swap.Contract.Refund(&_ERC20Swap.TransactOpts, v) } diff --git a/dex/networks/eth/contracts/ETHSwapV0.sol b/dex/networks/eth/contracts/ETHSwapV0.sol index e65c188285..ca53936269 100644 --- a/dex/networks/eth/contracts/ETHSwapV0.sol +++ b/dex/networks/eth/contracts/ETHSwapV0.sol @@ -122,7 +122,7 @@ contract ETHSwap { } // redeem redeems a contract. It checks that the sender is not a contract, - // and that the secret hash hashes to secretHash. msg.value is tranfered + // and that the secret hashes to secretHash. msg.value is tranfered // from the contract to the sender. // // It is important to note that this uses call.value which comes with no diff --git a/dex/networks/eth/contracts/ETHSwapV1.sol b/dex/networks/eth/contracts/ETHSwapV1.sol index d1e115b67f..767b39bab8 100644 --- a/dex/networks/eth/contracts/ETHSwapV1.sol +++ b/dex/networks/eth/contracts/ETHSwapV1.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: BlueOak-1.0.0 // pragma should be as specific as possible to allow easier validation. -pragma solidity = 0.8.15; +pragma solidity = 0.8.18; // ETHSwap creates a contract to be deployed on an ethereum network. After // deployed, it keeps a record of the state of a contract and enables @@ -33,6 +33,7 @@ contract ETHSwap { } bytes32 constant RefundRecord = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; + bytes32 constant RefundRecordHash = 0xAF9613760F72635FBDB44A5A0A63C39F12AF30F950A6EE5C971BE188E89C4051; // swaps is a map of contract hashes to the "swap record". The swap record // has the following interpretation. @@ -50,16 +51,16 @@ contract ETHSwap { // the swap record. struct Vector { bytes32 secretHash; + uint256 value; address initiator; uint64 refundTimestamp; address participant; - uint64 value; } // contractKey generates a key hash which commits to the contract data. The // generated hash is used as a key in the swaps map. function contractKey(Vector calldata v) public pure returns (bytes32) { - return sha256(bytes.concat(v.secretHash, bytes20(v.initiator), bytes20(v.participant), bytes8(v.value), bytes8(v.refundTimestamp))); + return sha256(bytes.concat(v.secretHash, bytes20(v.initiator), bytes20(v.participant), bytes32(v.value), bytes8(v.refundTimestamp))); } // Redemption is the information necessary to redeem a Vector. Since we @@ -127,6 +128,7 @@ contract ETHSwap { require(v.value > 0, "0 val"); require(v.refundTimestamp > 0, "0 refundTimestamp"); + require(v.secretHash != RefundRecordHash, "illegal secret hash (refund record hash)"); bytes32 k = contractKey(v); bytes32 record = swaps[k]; @@ -137,13 +139,13 @@ contract ETHSwap { swaps[k] = record; - initVal += v.value * 1 gwei; + initVal += v.value; } require(initVal == msg.value, "bad val"); } - // isRedeemable returns whether or not a swap identified by secretHash + // isRedeemable returns whether or not a swap identified by vector // can be redeemed using secret. isRedeemable DOES NOT check if the caller // is the participant in the vector. function isRedeemable(Vector calldata v) @@ -156,7 +158,7 @@ contract ETHSwap { } // redeem redeems a Vector. It checks that the sender is not a contract, - // and that the secret hash hashes to secretHash. msg.value is tranfered + // and that the secret hashes to secretHash. msg.value is tranfered // from ETHSwap to the sender. // // To prevent reentry attack, it is very important to check the state of the @@ -187,7 +189,7 @@ contract ETHSwap { require(secretValidates(r.secret, r.v.secretHash), "invalid secret"); swaps[k] = r.secret; - amountToRedeem += r.v.value * 1 gwei; + amountToRedeem += r.v.value; } (bool ok, ) = payable(msg.sender).call{value: amountToRedeem}(""); @@ -211,17 +213,17 @@ contract ETHSwap { (bytes32 k, bytes32 record, uint256 blockNum) = retrieveStatus(v); // Is this swap initialized? + // This check also guarantees that the swap has not already been + // refunded i.e. record != RefundRecord, since RefundRecord is certainly + // greater than block.number. require(blockNum > 0 && blockNum <= block.number, "swap not active"); // Is it already redeemed? require(!secretValidates(record, v.secretHash), "swap already redeemed"); - // Is it already refunded? - require(record != RefundRecord, "swap already refunded"); - swaps[k] = RefundRecord; - (bool ok, ) = payable(v.initiator).call{value: v.value * 1 gwei}(""); + (bool ok, ) = payable(v.initiator).call{value: v.value}(""); require(ok == true, "transfer failed"); } } diff --git a/dex/networks/eth/contracts/v1/BinRuntimeV1.go b/dex/networks/eth/contracts/v1/BinRuntimeV1.go index a7a9d0f326..9bd4f954ae 100644 --- a/dex/networks/eth/contracts/v1/BinRuntimeV1.go +++ b/dex/networks/eth/contracts/v1/BinRuntimeV1.go @@ -3,4 +3,4 @@ package v1 -const ETHSwapRuntimeBin = "60806040526004361061007b5760003560e01c80637802689d1161004e5780637802689d1461011b578063d2544c061461013b578063eb84e7f21461015b578063ed7cbed71461019657600080fd5b8063428b16e11461008057806361a16e33146100a257806364a97bff146100d857806377d7e031146100eb575b600080fd5b34801561008c57600080fd5b506100a061009b366004610b6f565b6101b6565b005b3480156100ae57600080fd5b506100c26100bd366004610be4565b610447565b6040516100cf9190610c12565b60405180910390f35b6100a06100e6366004610c55565b610503565b3480156100f757600080fd5b5061010b610106366004610cb8565b61073b565b60405190151581526020016100cf565b34801561012757600080fd5b5061010b610136366004610be4565b6107b5565b34801561014757600080fd5b506100a0610156366004610be4565b6107e8565b34801561016757600080fd5b50610188610176366004610cda565b60006020819052908152604090205481565b6040519081526020016100cf565b3480156101a257600080fd5b506101886101b1366004610be4565b610a4a565b3233146101de5760405162461bcd60e51b81526004016101d590610cf3565b60405180910390fd5b6000805b828110156103b057368484838181106101fd576101fd610d1d565b60c0029190910191503390506102196080830160608401610d33565b6001600160a01b03161461025c5760405162461bcd60e51b815260206004820152600a6024820152691b9bdd08185d5d1a195960b21b60448201526064016101d5565b6000808061026984610b43565b92509250925060008111801561027e57504381105b6102ba5760405162461bcd60e51b815260206004820152600d60248201526c0756e66696c6c6564207377617609c1b60448201526064016101d5565b6102c582853561073b565b156103055760405162461bcd60e51b815260206004820152601060248201526f185b1c9958591e481c995919595b595960821b60448201526064016101d5565b61031460a0850135853561073b565b6103515760405162461bcd60e51b815260206004820152600e60248201526d1a5b9d985b1a59081cd958dc995d60921b60448201526064016101d5565b600083815260208190526040902060a0850180359091556103759060808601610d63565b61038390633b9aca00610da3565b6103979067ffffffffffffffff1687610dd3565b95505050505080806103a890610deb565b9150506101e2565b50604051600090339083908381818185875af1925050503d80600081146103f3576040519150601f19603f3d011682016040523d82523d6000602084013e6103f8565b606091505b50909150506001811515146104415760405162461bcd60e51b815260206004820152600f60248201526e1d1c985b9cd9995c8819985a5b1959608a1b60448201526064016101d5565b50505050565b60408051606081018252600080825260208201819052918101829052908061046e84610b43565b92509250506104986040805160608101909152806000815260006020820181905260409091015290565b816000036104bf578060005b908160038111156104b7576104b7610bfc565b9052506104fb565b600183016104cf578060036104a4565b6104da83863561073b565b156104ef5760028152602081018390526104fb565b60018152604081018290525b949350505050565b3233146105225760405162461bcd60e51b81526004016101d590610cf3565b6000805b828110156106fc573684848381811061054157610541610d1d565b905060a002019050600081608001602081019061055e9190610d63565b67ffffffffffffffff161161059d5760405162461bcd60e51b81526020600482015260056024820152640c081d985b60da1b60448201526064016101d5565b60006105af6060830160408401610d63565b67ffffffffffffffff16116105fa5760405162461bcd60e51b815260206004820152601160248201527003020726566756e6454696d657374616d7607c1b60448201526064016101d5565b600061060582610a4a565b60008181526020819052604090205490915080156106565760405162461bcd60e51b815260206004820152600e60248201526d73776170206e6f7420656d70747960901b60448201526064016101d5565b504361066381843561073b565b156106a15760405162461bcd60e51b815260206004820152600e60248201526d3430b9b41031b7b63634b9b4b7b760911b60448201526064016101d5565b60008281526020819052604090208190556106c260a0840160808501610d63565b6106d090633b9aca00610da3565b6106e49067ffffffffffffffff1686610dd3565b945050505080806106f490610deb565b915050610526565b503481146107365760405162461bcd60e51b8152602060048201526007602482015266189859081d985b60ca1b60448201526064016101d5565b505050565b60008160028460405160200161075391815260200190565b60408051601f198184030181529082905261076d91610e04565b602060405180830381855afa15801561078a573d6000803e3d6000fd5b5050506040513d601f19601f820116820180604052508101906107ad9190610e3f565b149392505050565b60008060006107c384610b43565b9250925050806000141580156104fb57506107df82853561073b565b15949350505050565b3233146108075760405162461bcd60e51b81526004016101d590610cf3565b6108176060820160408301610d63565b67ffffffffffffffff164210156108675760405162461bcd60e51b81526020600482015260146024820152731b1bd8dadd1a5b59481b9bdd08195e1c1a5c995960621b60448201526064016101d5565b600080600061087584610b43565b92509250925060008111801561088b5750438111155b6108c95760405162461bcd60e51b815260206004820152600f60248201526e73776170206e6f742061637469766560881b60448201526064016101d5565b6108d482853561073b565b156109195760405162461bcd60e51b81526020600482015260156024820152741cddd85c08185b1c9958591e481c995919595b5959605a1b60448201526064016101d5565b600182016109615760405162461bcd60e51b81526020600482015260156024820152741cddd85c08185b1c9958591e481c99599d5b991959605a1b60448201526064016101d5565b600083815260208181526040808320600019905561098491908701908701610d33565b6001600160a01b031661099d60a0870160808801610d63565b6109ab90633b9aca00610da3565b67ffffffffffffffff1660405160006040518083038185875af1925050503d80600081146109f5576040519150601f19603f3d011682016040523d82523d6000602084013e6109fa565b606091505b5090915050600181151514610a435760405162461bcd60e51b815260206004820152600f60248201526e1d1c985b9cd9995c8819985a5b1959608a1b60448201526064016101d5565b5050505050565b600060028235610a606040850160208601610d33565b60601b846060016020810190610a769190610d33565b60601b610a8960a0870160808801610d63565b60c01b610a9c6060880160408901610d63565b6040805160208101969096526bffffffffffffffffffffffff19948516908601529190921660548401526001600160c01b0319918216606884015260c01b16607082015260780160408051601f1981840301815290829052610afd91610e04565b602060405180830381855afa158015610b1a573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190610b3d9190610e3f565b92915050565b600080600080610b5285610a4a565b600081815260208190526040902054909690955085945092505050565b60008060208385031215610b8257600080fd5b823567ffffffffffffffff80821115610b9a57600080fd5b818501915085601f830112610bae57600080fd5b813581811115610bbd57600080fd5b86602060c083028501011115610bd257600080fd5b60209290920196919550909350505050565b600060a08284031215610bf657600080fd5b50919050565b634e487b7160e01b600052602160045260246000fd5b8151606082019060048110610c3757634e487b7160e01b600052602160045260246000fd5b80835250602083015160208301526040830151604083015292915050565b60008060208385031215610c6857600080fd5b823567ffffffffffffffff80821115610c8057600080fd5b818501915085601f830112610c9457600080fd5b813581811115610ca357600080fd5b86602060a083028501011115610bd257600080fd5b60008060408385031215610ccb57600080fd5b50508035926020909101359150565b600060208284031215610cec57600080fd5b5035919050565b60208082526010908201526f39b2b73232b910109e9037b934b3b4b760811b604082015260600190565b634e487b7160e01b600052603260045260246000fd5b600060208284031215610d4557600080fd5b81356001600160a01b0381168114610d5c57600080fd5b9392505050565b600060208284031215610d7557600080fd5b813567ffffffffffffffff81168114610d5c57600080fd5b634e487b7160e01b600052601160045260246000fd5b600067ffffffffffffffff80831681851681830481118215151615610dca57610dca610d8d565b02949350505050565b60008219821115610de657610de6610d8d565b500190565b600060018201610dfd57610dfd610d8d565b5060010190565b6000825160005b81811015610e255760208186018101518583015201610e0b565b81811115610e34576000828501525b509190910192915050565b600060208284031215610e5157600080fd5b505191905056fea2646970667358221220d735868682c69bb69ed936f11e2f136e7da44cb347e0b1d67acd1e088ea13dd664736f6c634300080f0033" +const ETHSwapRuntimeBin = "60806040526004361061007b5760003560e01c8063a76f9f2d1161004e578063a76f9f2d1461010a578063d5cfd04914610138578063db3b419c14610158578063eb84e7f21461018557600080fd5b806323f0388b146100805780633da59631146100a257806377d7e031146100b55780638cd8dd97146100ea575b600080fd5b34801561008c57600080fd5b506100a061009b366004610b14565b6101b2565b005b6100a06100b0366004610b89565b610420565b3480156100c157600080fd5b506100d56100d0366004610bec565b61069d565b60405190151581526020015b60405180910390f35b3480156100f657600080fd5b506100a0610105366004610c0e565b610719565b34801561011657600080fd5b5061012a610125366004610c0e565b610911565b6040519081526020016100e1565b34801561014457600080fd5b506100d5610153366004610c0e565b6109f9565b34801561016457600080fd5b50610178610173366004610c0e565b610a2d565b6040516100e19190610c3c565b34801561019157600080fd5b5061012a6101a0366004610c7f565b60006020819052908152604090205481565b3233146101da5760405162461bcd60e51b81526004016101d190610c98565b60405180910390fd5b6000805b8281101561038957368484838181106101f9576101f9610cc2565b60c00291909101915033905061021560a0830160808401610cd8565b6001600160a01b0316146102585760405162461bcd60e51b815260206004820152600a6024820152691b9bdd08185d5d1a195960b21b60448201526064016101d1565b6000808061026584610ae8565b92509250925060008111801561027a57504381105b6102b65760405162461bcd60e51b815260206004820152600d60248201526c0756e66696c6c6564207377617609c1b60448201526064016101d1565b6102c182853561069d565b156103015760405162461bcd60e51b815260206004820152601060248201526f185b1c9958591e481c995919595b595960821b60448201526064016101d1565b61031060a0850135853561069d565b61034d5760405162461bcd60e51b815260206004820152600e60248201526d1a5b9d985b1a59081cd958dc995d60921b60448201526064016101d1565b60008381526020818152604090912060a086013590556103709085013587610d1e565b955050505050808061038190610d31565b9150506101de565b50604051600090339083908381818185875af1925050503d80600081146103cc576040519150601f19603f3d011682016040523d82523d6000602084013e6103d1565b606091505b509091505060018115151461041a5760405162461bcd60e51b815260206004820152600f60248201526e1d1c985b9cd9995c8819985a5b1959608a1b60448201526064016101d1565b50505050565b32331461043f5760405162461bcd60e51b81526004016101d190610c98565b6000805b8281101561065e573684848381811061045e5761045e610cc2565b905060a00201905060008160200135116104a25760405162461bcd60e51b81526020600482015260056024820152640c081d985b60da1b60448201526064016101d1565b60006104b46080830160608401610d4a565b67ffffffffffffffff16116104ff5760405162461bcd60e51b815260206004820152601160248201527003020726566756e6454696d657374616d7607c1b60448201526064016101d1565b7f5069ec89f08d9ca0424bb5a5f59c3c60ed50cf06af5911a368e41e771763bfaf8135016105805760405162461bcd60e51b815260206004820152602860248201527f696c6c6567616c2073656372657420686173682028726566756e64207265636f604482015267726420686173682960c01b60648201526084016101d1565b600061058b82610911565b60008181526020819052604090205490915080156105dc5760405162461bcd60e51b815260206004820152600e60248201526d73776170206e6f7420656d70747960901b60448201526064016101d1565b50436105e981843561069d565b156106275760405162461bcd60e51b815260206004820152600e60248201526d3430b9b41031b7b63634b9b4b7b760911b60448201526064016101d1565b6000828152602081815260409091208290556106469084013586610d1e565b9450505050808061065690610d31565b915050610443565b503481146106985760405162461bcd60e51b8152602060048201526007602482015266189859081d985b60ca1b60448201526064016101d1565b505050565b6000816002846040516020016106b591815260200190565b60408051601f19818403018152908290526106cf91610d74565b602060405180830381855afa1580156106ec573d6000803e3d6000fd5b5050506040513d601f19601f8201168201806040525081019061070f9190610da3565b1490505b92915050565b3233146107385760405162461bcd60e51b81526004016101d190610c98565b6107486080820160608301610d4a565b67ffffffffffffffff164210156107985760405162461bcd60e51b81526020600482015260146024820152731b1bd8dadd1a5b59481b9bdd08195e1c1a5c995960621b60448201526064016101d1565b60008060006107a684610ae8565b9250925092506000811180156107bc5750438111155b6107fa5760405162461bcd60e51b815260206004820152600f60248201526e73776170206e6f742061637469766560881b60448201526064016101d1565b61080582853561069d565b1561084a5760405162461bcd60e51b81526020600482015260156024820152741cddd85c08185b1c9958591e481c995919595b5959605a1b60448201526064016101d1565b600083815260208190526040808220600019905561086e9060608701908701610cd8565b6001600160a01b0316856020013560405160006040518083038185875af1925050503d80600081146108bc576040519150601f19603f3d011682016040523d82523d6000602084013e6108c1565b606091505b509091505060018115151461090a5760405162461bcd60e51b815260206004820152600f60248201526e1d1c985b9cd9995c8819985a5b1959608a1b60448201526064016101d1565b5050505050565b6000600282356109276060850160408601610cd8565b60601b61093a60a0860160808701610cd8565b60601b856020013560001b8660600160208101906109589190610d4a565b6040805160208101969096526bffffffffffffffffffffffff199485169086015292909116605484015260688301526001600160c01b031960c09190911b16608882015260900160408051601f19818403018152908290526109b991610d74565b602060405180830381855afa1580156109d6573d6000803e3d6000fd5b5050506040513d601f19601f820116820180604052508101906107139190610da3565b6000806000610a0784610ae8565b925092505080600014158015610a255750610a2382853561069d565b155b949350505050565b604080516060810182526000808252602082018190529181018290529080610a5484610ae8565b9250925050610a7e6040805160608101909152806000815260006020820181905260409091015290565b81600003610aa5578060005b90816003811115610a9d57610a9d610c26565b905250610a25565b60018301610ab557806003610a8a565b610ac083863561069d565b15610ad5576002815260208101839052610a25565b6001815260408101919091529392505050565b600080600080610af785610911565b600081815260208190526040902054909690955085945092505050565b60008060208385031215610b2757600080fd5b823567ffffffffffffffff80821115610b3f57600080fd5b818501915085601f830112610b5357600080fd5b813581811115610b6257600080fd5b86602060c083028501011115610b7757600080fd5b60209290920196919550909350505050565b60008060208385031215610b9c57600080fd5b823567ffffffffffffffff80821115610bb457600080fd5b818501915085601f830112610bc857600080fd5b813581811115610bd757600080fd5b86602060a083028501011115610b7757600080fd5b60008060408385031215610bff57600080fd5b50508035926020909101359150565b600060a08284031215610c2057600080fd5b50919050565b634e487b7160e01b600052602160045260246000fd5b8151606082019060048110610c6157634e487b7160e01b600052602160045260246000fd5b80835250602083015160208301526040830151604083015292915050565b600060208284031215610c9157600080fd5b5035919050565b60208082526010908201526f39b2b73232b910109e9037b934b3b4b760811b604082015260600190565b634e487b7160e01b600052603260045260246000fd5b600060208284031215610cea57600080fd5b81356001600160a01b0381168114610d0157600080fd5b9392505050565b634e487b7160e01b600052601160045260246000fd5b8082018082111561071357610713610d08565b600060018201610d4357610d43610d08565b5060010190565b600060208284031215610d5c57600080fd5b813567ffffffffffffffff81168114610d0157600080fd5b6000825160005b81811015610d955760208186018101518583015201610d7b565b506000920191825250919050565b600060208284031215610db557600080fd5b505191905056fea2646970667358221220420ce9cfd5a39be8e719369517f704148d580fb33472b5b8947c77d61eee66d664736f6c63430008120033" diff --git a/dex/networks/eth/contracts/v1/contract.go b/dex/networks/eth/contracts/v1/contract.go index 07de2ac59e..61db70d044 100644 --- a/dex/networks/eth/contracts/v1/contract.go +++ b/dex/networks/eth/contracts/v1/contract.go @@ -44,16 +44,16 @@ type ETHSwapStatus struct { // ETHSwapVector is an auto generated low-level Go binding around an user-defined struct. type ETHSwapVector struct { SecretHash [32]byte + Value *big.Int Initiator common.Address RefundTimestamp uint64 Participant common.Address - Value uint64 } // ETHSwapMetaData contains all meta data concerning the ETHSwap contract. var ETHSwapMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"secretHash\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"initiator\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"refundTimestamp\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"participant\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"value\",\"type\":\"uint64\"}],\"internalType\":\"structETHSwap.Vector\",\"name\":\"v\",\"type\":\"tuple\"}],\"name\":\"contractKey\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"secretHash\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"initiator\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"refundTimestamp\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"participant\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"value\",\"type\":\"uint64\"}],\"internalType\":\"structETHSwap.Vector[]\",\"name\":\"contracts\",\"type\":\"tuple[]\"}],\"name\":\"initiate\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"secretHash\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"initiator\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"refundTimestamp\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"participant\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"value\",\"type\":\"uint64\"}],\"internalType\":\"structETHSwap.Vector\",\"name\":\"v\",\"type\":\"tuple\"}],\"name\":\"isRedeemable\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"secretHash\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"initiator\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"refundTimestamp\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"participant\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"value\",\"type\":\"uint64\"}],\"internalType\":\"structETHSwap.Vector\",\"name\":\"v\",\"type\":\"tuple\"},{\"internalType\":\"bytes32\",\"name\":\"secret\",\"type\":\"bytes32\"}],\"internalType\":\"structETHSwap.Redemption[]\",\"name\":\"redemptions\",\"type\":\"tuple[]\"}],\"name\":\"redeem\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"secretHash\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"initiator\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"refundTimestamp\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"participant\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"value\",\"type\":\"uint64\"}],\"internalType\":\"structETHSwap.Vector\",\"name\":\"v\",\"type\":\"tuple\"}],\"name\":\"refund\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"secret\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"secretHash\",\"type\":\"bytes32\"}],\"name\":\"secretValidates\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"secretHash\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"initiator\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"refundTimestamp\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"participant\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"value\",\"type\":\"uint64\"}],\"internalType\":\"structETHSwap.Vector\",\"name\":\"v\",\"type\":\"tuple\"}],\"name\":\"status\",\"outputs\":[{\"components\":[{\"internalType\":\"enumETHSwap.Step\",\"name\":\"step\",\"type\":\"uint8\"},{\"internalType\":\"bytes32\",\"name\":\"secret\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"blockNumber\",\"type\":\"uint256\"}],\"internalType\":\"structETHSwap.Status\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"swaps\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", - Bin: "0x608060405234801561001057600080fd5b50610e8e806100206000396000f3fe60806040526004361061007b5760003560e01c80637802689d1161004e5780637802689d1461011b578063d2544c061461013b578063eb84e7f21461015b578063ed7cbed71461019657600080fd5b8063428b16e11461008057806361a16e33146100a257806364a97bff146100d857806377d7e031146100eb575b600080fd5b34801561008c57600080fd5b506100a061009b366004610b6f565b6101b6565b005b3480156100ae57600080fd5b506100c26100bd366004610be4565b610447565b6040516100cf9190610c12565b60405180910390f35b6100a06100e6366004610c55565b610503565b3480156100f757600080fd5b5061010b610106366004610cb8565b61073b565b60405190151581526020016100cf565b34801561012757600080fd5b5061010b610136366004610be4565b6107b5565b34801561014757600080fd5b506100a0610156366004610be4565b6107e8565b34801561016757600080fd5b50610188610176366004610cda565b60006020819052908152604090205481565b6040519081526020016100cf565b3480156101a257600080fd5b506101886101b1366004610be4565b610a4a565b3233146101de5760405162461bcd60e51b81526004016101d590610cf3565b60405180910390fd5b6000805b828110156103b057368484838181106101fd576101fd610d1d565b60c0029190910191503390506102196080830160608401610d33565b6001600160a01b03161461025c5760405162461bcd60e51b815260206004820152600a6024820152691b9bdd08185d5d1a195960b21b60448201526064016101d5565b6000808061026984610b43565b92509250925060008111801561027e57504381105b6102ba5760405162461bcd60e51b815260206004820152600d60248201526c0756e66696c6c6564207377617609c1b60448201526064016101d5565b6102c582853561073b565b156103055760405162461bcd60e51b815260206004820152601060248201526f185b1c9958591e481c995919595b595960821b60448201526064016101d5565b61031460a0850135853561073b565b6103515760405162461bcd60e51b815260206004820152600e60248201526d1a5b9d985b1a59081cd958dc995d60921b60448201526064016101d5565b600083815260208190526040902060a0850180359091556103759060808601610d63565b61038390633b9aca00610da3565b6103979067ffffffffffffffff1687610dd3565b95505050505080806103a890610deb565b9150506101e2565b50604051600090339083908381818185875af1925050503d80600081146103f3576040519150601f19603f3d011682016040523d82523d6000602084013e6103f8565b606091505b50909150506001811515146104415760405162461bcd60e51b815260206004820152600f60248201526e1d1c985b9cd9995c8819985a5b1959608a1b60448201526064016101d5565b50505050565b60408051606081018252600080825260208201819052918101829052908061046e84610b43565b92509250506104986040805160608101909152806000815260006020820181905260409091015290565b816000036104bf578060005b908160038111156104b7576104b7610bfc565b9052506104fb565b600183016104cf578060036104a4565b6104da83863561073b565b156104ef5760028152602081018390526104fb565b60018152604081018290525b949350505050565b3233146105225760405162461bcd60e51b81526004016101d590610cf3565b6000805b828110156106fc573684848381811061054157610541610d1d565b905060a002019050600081608001602081019061055e9190610d63565b67ffffffffffffffff161161059d5760405162461bcd60e51b81526020600482015260056024820152640c081d985b60da1b60448201526064016101d5565b60006105af6060830160408401610d63565b67ffffffffffffffff16116105fa5760405162461bcd60e51b815260206004820152601160248201527003020726566756e6454696d657374616d7607c1b60448201526064016101d5565b600061060582610a4a565b60008181526020819052604090205490915080156106565760405162461bcd60e51b815260206004820152600e60248201526d73776170206e6f7420656d70747960901b60448201526064016101d5565b504361066381843561073b565b156106a15760405162461bcd60e51b815260206004820152600e60248201526d3430b9b41031b7b63634b9b4b7b760911b60448201526064016101d5565b60008281526020819052604090208190556106c260a0840160808501610d63565b6106d090633b9aca00610da3565b6106e49067ffffffffffffffff1686610dd3565b945050505080806106f490610deb565b915050610526565b503481146107365760405162461bcd60e51b8152602060048201526007602482015266189859081d985b60ca1b60448201526064016101d5565b505050565b60008160028460405160200161075391815260200190565b60408051601f198184030181529082905261076d91610e04565b602060405180830381855afa15801561078a573d6000803e3d6000fd5b5050506040513d601f19601f820116820180604052508101906107ad9190610e3f565b149392505050565b60008060006107c384610b43565b9250925050806000141580156104fb57506107df82853561073b565b15949350505050565b3233146108075760405162461bcd60e51b81526004016101d590610cf3565b6108176060820160408301610d63565b67ffffffffffffffff164210156108675760405162461bcd60e51b81526020600482015260146024820152731b1bd8dadd1a5b59481b9bdd08195e1c1a5c995960621b60448201526064016101d5565b600080600061087584610b43565b92509250925060008111801561088b5750438111155b6108c95760405162461bcd60e51b815260206004820152600f60248201526e73776170206e6f742061637469766560881b60448201526064016101d5565b6108d482853561073b565b156109195760405162461bcd60e51b81526020600482015260156024820152741cddd85c08185b1c9958591e481c995919595b5959605a1b60448201526064016101d5565b600182016109615760405162461bcd60e51b81526020600482015260156024820152741cddd85c08185b1c9958591e481c99599d5b991959605a1b60448201526064016101d5565b600083815260208181526040808320600019905561098491908701908701610d33565b6001600160a01b031661099d60a0870160808801610d63565b6109ab90633b9aca00610da3565b67ffffffffffffffff1660405160006040518083038185875af1925050503d80600081146109f5576040519150601f19603f3d011682016040523d82523d6000602084013e6109fa565b606091505b5090915050600181151514610a435760405162461bcd60e51b815260206004820152600f60248201526e1d1c985b9cd9995c8819985a5b1959608a1b60448201526064016101d5565b5050505050565b600060028235610a606040850160208601610d33565b60601b846060016020810190610a769190610d33565b60601b610a8960a0870160808801610d63565b60c01b610a9c6060880160408901610d63565b6040805160208101969096526bffffffffffffffffffffffff19948516908601529190921660548401526001600160c01b0319918216606884015260c01b16607082015260780160408051601f1981840301815290829052610afd91610e04565b602060405180830381855afa158015610b1a573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190610b3d9190610e3f565b92915050565b600080600080610b5285610a4a565b600081815260208190526040902054909690955085945092505050565b60008060208385031215610b8257600080fd5b823567ffffffffffffffff80821115610b9a57600080fd5b818501915085601f830112610bae57600080fd5b813581811115610bbd57600080fd5b86602060c083028501011115610bd257600080fd5b60209290920196919550909350505050565b600060a08284031215610bf657600080fd5b50919050565b634e487b7160e01b600052602160045260246000fd5b8151606082019060048110610c3757634e487b7160e01b600052602160045260246000fd5b80835250602083015160208301526040830151604083015292915050565b60008060208385031215610c6857600080fd5b823567ffffffffffffffff80821115610c8057600080fd5b818501915085601f830112610c9457600080fd5b813581811115610ca357600080fd5b86602060a083028501011115610bd257600080fd5b60008060408385031215610ccb57600080fd5b50508035926020909101359150565b600060208284031215610cec57600080fd5b5035919050565b60208082526010908201526f39b2b73232b910109e9037b934b3b4b760811b604082015260600190565b634e487b7160e01b600052603260045260246000fd5b600060208284031215610d4557600080fd5b81356001600160a01b0381168114610d5c57600080fd5b9392505050565b600060208284031215610d7557600080fd5b813567ffffffffffffffff81168114610d5c57600080fd5b634e487b7160e01b600052601160045260246000fd5b600067ffffffffffffffff80831681851681830481118215151615610dca57610dca610d8d565b02949350505050565b60008219821115610de657610de6610d8d565b500190565b600060018201610dfd57610dfd610d8d565b5060010190565b6000825160005b81811015610e255760208186018101518583015201610e0b565b81811115610e34576000828501525b509190910192915050565b600060208284031215610e5157600080fd5b505191905056fea2646970667358221220d735868682c69bb69ed936f11e2f136e7da44cb347e0b1d67acd1e088ea13dd664736f6c634300080f0033", + ABI: "[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"secretHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"initiator\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"refundTimestamp\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"participant\",\"type\":\"address\"}],\"internalType\":\"structETHSwap.Vector\",\"name\":\"v\",\"type\":\"tuple\"}],\"name\":\"contractKey\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"secretHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"initiator\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"refundTimestamp\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"participant\",\"type\":\"address\"}],\"internalType\":\"structETHSwap.Vector[]\",\"name\":\"contracts\",\"type\":\"tuple[]\"}],\"name\":\"initiate\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"secretHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"initiator\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"refundTimestamp\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"participant\",\"type\":\"address\"}],\"internalType\":\"structETHSwap.Vector\",\"name\":\"v\",\"type\":\"tuple\"}],\"name\":\"isRedeemable\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"secretHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"initiator\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"refundTimestamp\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"participant\",\"type\":\"address\"}],\"internalType\":\"structETHSwap.Vector\",\"name\":\"v\",\"type\":\"tuple\"},{\"internalType\":\"bytes32\",\"name\":\"secret\",\"type\":\"bytes32\"}],\"internalType\":\"structETHSwap.Redemption[]\",\"name\":\"redemptions\",\"type\":\"tuple[]\"}],\"name\":\"redeem\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"secretHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"initiator\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"refundTimestamp\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"participant\",\"type\":\"address\"}],\"internalType\":\"structETHSwap.Vector\",\"name\":\"v\",\"type\":\"tuple\"}],\"name\":\"refund\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"secret\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"secretHash\",\"type\":\"bytes32\"}],\"name\":\"secretValidates\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"secretHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"initiator\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"refundTimestamp\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"participant\",\"type\":\"address\"}],\"internalType\":\"structETHSwap.Vector\",\"name\":\"v\",\"type\":\"tuple\"}],\"name\":\"status\",\"outputs\":[{\"components\":[{\"internalType\":\"enumETHSwap.Step\",\"name\":\"step\",\"type\":\"uint8\"},{\"internalType\":\"bytes32\",\"name\":\"secret\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"blockNumber\",\"type\":\"uint256\"}],\"internalType\":\"structETHSwap.Status\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"swaps\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", + Bin: "0x608060405234801561001057600080fd5b50610df2806100206000396000f3fe60806040526004361061007b5760003560e01c8063a76f9f2d1161004e578063a76f9f2d1461010a578063d5cfd04914610138578063db3b419c14610158578063eb84e7f21461018557600080fd5b806323f0388b146100805780633da59631146100a257806377d7e031146100b55780638cd8dd97146100ea575b600080fd5b34801561008c57600080fd5b506100a061009b366004610b14565b6101b2565b005b6100a06100b0366004610b89565b610420565b3480156100c157600080fd5b506100d56100d0366004610bec565b61069d565b60405190151581526020015b60405180910390f35b3480156100f657600080fd5b506100a0610105366004610c0e565b610719565b34801561011657600080fd5b5061012a610125366004610c0e565b610911565b6040519081526020016100e1565b34801561014457600080fd5b506100d5610153366004610c0e565b6109f9565b34801561016457600080fd5b50610178610173366004610c0e565b610a2d565b6040516100e19190610c3c565b34801561019157600080fd5b5061012a6101a0366004610c7f565b60006020819052908152604090205481565b3233146101da5760405162461bcd60e51b81526004016101d190610c98565b60405180910390fd5b6000805b8281101561038957368484838181106101f9576101f9610cc2565b60c00291909101915033905061021560a0830160808401610cd8565b6001600160a01b0316146102585760405162461bcd60e51b815260206004820152600a6024820152691b9bdd08185d5d1a195960b21b60448201526064016101d1565b6000808061026584610ae8565b92509250925060008111801561027a57504381105b6102b65760405162461bcd60e51b815260206004820152600d60248201526c0756e66696c6c6564207377617609c1b60448201526064016101d1565b6102c182853561069d565b156103015760405162461bcd60e51b815260206004820152601060248201526f185b1c9958591e481c995919595b595960821b60448201526064016101d1565b61031060a0850135853561069d565b61034d5760405162461bcd60e51b815260206004820152600e60248201526d1a5b9d985b1a59081cd958dc995d60921b60448201526064016101d1565b60008381526020818152604090912060a086013590556103709085013587610d1e565b955050505050808061038190610d31565b9150506101de565b50604051600090339083908381818185875af1925050503d80600081146103cc576040519150601f19603f3d011682016040523d82523d6000602084013e6103d1565b606091505b509091505060018115151461041a5760405162461bcd60e51b815260206004820152600f60248201526e1d1c985b9cd9995c8819985a5b1959608a1b60448201526064016101d1565b50505050565b32331461043f5760405162461bcd60e51b81526004016101d190610c98565b6000805b8281101561065e573684848381811061045e5761045e610cc2565b905060a00201905060008160200135116104a25760405162461bcd60e51b81526020600482015260056024820152640c081d985b60da1b60448201526064016101d1565b60006104b46080830160608401610d4a565b67ffffffffffffffff16116104ff5760405162461bcd60e51b815260206004820152601160248201527003020726566756e6454696d657374616d7607c1b60448201526064016101d1565b7f5069ec89f08d9ca0424bb5a5f59c3c60ed50cf06af5911a368e41e771763bfaf8135016105805760405162461bcd60e51b815260206004820152602860248201527f696c6c6567616c2073656372657420686173682028726566756e64207265636f604482015267726420686173682960c01b60648201526084016101d1565b600061058b82610911565b60008181526020819052604090205490915080156105dc5760405162461bcd60e51b815260206004820152600e60248201526d73776170206e6f7420656d70747960901b60448201526064016101d1565b50436105e981843561069d565b156106275760405162461bcd60e51b815260206004820152600e60248201526d3430b9b41031b7b63634b9b4b7b760911b60448201526064016101d1565b6000828152602081815260409091208290556106469084013586610d1e565b9450505050808061065690610d31565b915050610443565b503481146106985760405162461bcd60e51b8152602060048201526007602482015266189859081d985b60ca1b60448201526064016101d1565b505050565b6000816002846040516020016106b591815260200190565b60408051601f19818403018152908290526106cf91610d74565b602060405180830381855afa1580156106ec573d6000803e3d6000fd5b5050506040513d601f19601f8201168201806040525081019061070f9190610da3565b1490505b92915050565b3233146107385760405162461bcd60e51b81526004016101d190610c98565b6107486080820160608301610d4a565b67ffffffffffffffff164210156107985760405162461bcd60e51b81526020600482015260146024820152731b1bd8dadd1a5b59481b9bdd08195e1c1a5c995960621b60448201526064016101d1565b60008060006107a684610ae8565b9250925092506000811180156107bc5750438111155b6107fa5760405162461bcd60e51b815260206004820152600f60248201526e73776170206e6f742061637469766560881b60448201526064016101d1565b61080582853561069d565b1561084a5760405162461bcd60e51b81526020600482015260156024820152741cddd85c08185b1c9958591e481c995919595b5959605a1b60448201526064016101d1565b600083815260208190526040808220600019905561086e9060608701908701610cd8565b6001600160a01b0316856020013560405160006040518083038185875af1925050503d80600081146108bc576040519150601f19603f3d011682016040523d82523d6000602084013e6108c1565b606091505b509091505060018115151461090a5760405162461bcd60e51b815260206004820152600f60248201526e1d1c985b9cd9995c8819985a5b1959608a1b60448201526064016101d1565b5050505050565b6000600282356109276060850160408601610cd8565b60601b61093a60a0860160808701610cd8565b60601b856020013560001b8660600160208101906109589190610d4a565b6040805160208101969096526bffffffffffffffffffffffff199485169086015292909116605484015260688301526001600160c01b031960c09190911b16608882015260900160408051601f19818403018152908290526109b991610d74565b602060405180830381855afa1580156109d6573d6000803e3d6000fd5b5050506040513d601f19601f820116820180604052508101906107139190610da3565b6000806000610a0784610ae8565b925092505080600014158015610a255750610a2382853561069d565b155b949350505050565b604080516060810182526000808252602082018190529181018290529080610a5484610ae8565b9250925050610a7e6040805160608101909152806000815260006020820181905260409091015290565b81600003610aa5578060005b90816003811115610a9d57610a9d610c26565b905250610a25565b60018301610ab557806003610a8a565b610ac083863561069d565b15610ad5576002815260208101839052610a25565b6001815260408101919091529392505050565b600080600080610af785610911565b600081815260208190526040902054909690955085945092505050565b60008060208385031215610b2757600080fd5b823567ffffffffffffffff80821115610b3f57600080fd5b818501915085601f830112610b5357600080fd5b813581811115610b6257600080fd5b86602060c083028501011115610b7757600080fd5b60209290920196919550909350505050565b60008060208385031215610b9c57600080fd5b823567ffffffffffffffff80821115610bb457600080fd5b818501915085601f830112610bc857600080fd5b813581811115610bd757600080fd5b86602060a083028501011115610b7757600080fd5b60008060408385031215610bff57600080fd5b50508035926020909101359150565b600060a08284031215610c2057600080fd5b50919050565b634e487b7160e01b600052602160045260246000fd5b8151606082019060048110610c6157634e487b7160e01b600052602160045260246000fd5b80835250602083015160208301526040830151604083015292915050565b600060208284031215610c9157600080fd5b5035919050565b60208082526010908201526f39b2b73232b910109e9037b934b3b4b760811b604082015260600190565b634e487b7160e01b600052603260045260246000fd5b600060208284031215610cea57600080fd5b81356001600160a01b0381168114610d0157600080fd5b9392505050565b634e487b7160e01b600052601160045260246000fd5b8082018082111561071357610713610d08565b600060018201610d4357610d43610d08565b5060010190565b600060208284031215610d5c57600080fd5b813567ffffffffffffffff81168114610d0157600080fd5b6000825160005b81811015610d955760208186018101518583015201610d7b565b506000920191825250919050565b600060208284031215610db557600080fd5b505191905056fea2646970667358221220420ce9cfd5a39be8e719369517f704148d580fb33472b5b8947c77d61eee66d664736f6c63430008120033", } // ETHSwapABI is the input ABI used to generate the binding from. @@ -223,9 +223,9 @@ func (_ETHSwap *ETHSwapTransactorRaw) Transact(opts *bind.TransactOpts, method s return _ETHSwap.Contract.contract.Transact(opts, method, params...) } -// ContractKey is a free data retrieval call binding the contract method 0xed7cbed7. +// ContractKey is a free data retrieval call binding the contract method 0xa76f9f2d. // -// Solidity: function contractKey((bytes32,address,uint64,address,uint64) v) pure returns(bytes32) +// Solidity: function contractKey((bytes32,uint256,address,uint64,address) v) pure returns(bytes32) func (_ETHSwap *ETHSwapCaller) ContractKey(opts *bind.CallOpts, v ETHSwapVector) ([32]byte, error) { var out []interface{} err := _ETHSwap.contract.Call(opts, &out, "contractKey", v) @@ -240,23 +240,23 @@ func (_ETHSwap *ETHSwapCaller) ContractKey(opts *bind.CallOpts, v ETHSwapVector) } -// ContractKey is a free data retrieval call binding the contract method 0xed7cbed7. +// ContractKey is a free data retrieval call binding the contract method 0xa76f9f2d. // -// Solidity: function contractKey((bytes32,address,uint64,address,uint64) v) pure returns(bytes32) +// Solidity: function contractKey((bytes32,uint256,address,uint64,address) v) pure returns(bytes32) func (_ETHSwap *ETHSwapSession) ContractKey(v ETHSwapVector) ([32]byte, error) { return _ETHSwap.Contract.ContractKey(&_ETHSwap.CallOpts, v) } -// ContractKey is a free data retrieval call binding the contract method 0xed7cbed7. +// ContractKey is a free data retrieval call binding the contract method 0xa76f9f2d. // -// Solidity: function contractKey((bytes32,address,uint64,address,uint64) v) pure returns(bytes32) +// Solidity: function contractKey((bytes32,uint256,address,uint64,address) v) pure returns(bytes32) func (_ETHSwap *ETHSwapCallerSession) ContractKey(v ETHSwapVector) ([32]byte, error) { return _ETHSwap.Contract.ContractKey(&_ETHSwap.CallOpts, v) } -// IsRedeemable is a free data retrieval call binding the contract method 0x7802689d. +// IsRedeemable is a free data retrieval call binding the contract method 0xd5cfd049. // -// Solidity: function isRedeemable((bytes32,address,uint64,address,uint64) v) view returns(bool) +// Solidity: function isRedeemable((bytes32,uint256,address,uint64,address) v) view returns(bool) func (_ETHSwap *ETHSwapCaller) IsRedeemable(opts *bind.CallOpts, v ETHSwapVector) (bool, error) { var out []interface{} err := _ETHSwap.contract.Call(opts, &out, "isRedeemable", v) @@ -271,16 +271,16 @@ func (_ETHSwap *ETHSwapCaller) IsRedeemable(opts *bind.CallOpts, v ETHSwapVector } -// IsRedeemable is a free data retrieval call binding the contract method 0x7802689d. +// IsRedeemable is a free data retrieval call binding the contract method 0xd5cfd049. // -// Solidity: function isRedeemable((bytes32,address,uint64,address,uint64) v) view returns(bool) +// Solidity: function isRedeemable((bytes32,uint256,address,uint64,address) v) view returns(bool) func (_ETHSwap *ETHSwapSession) IsRedeemable(v ETHSwapVector) (bool, error) { return _ETHSwap.Contract.IsRedeemable(&_ETHSwap.CallOpts, v) } -// IsRedeemable is a free data retrieval call binding the contract method 0x7802689d. +// IsRedeemable is a free data retrieval call binding the contract method 0xd5cfd049. // -// Solidity: function isRedeemable((bytes32,address,uint64,address,uint64) v) view returns(bool) +// Solidity: function isRedeemable((bytes32,uint256,address,uint64,address) v) view returns(bool) func (_ETHSwap *ETHSwapCallerSession) IsRedeemable(v ETHSwapVector) (bool, error) { return _ETHSwap.Contract.IsRedeemable(&_ETHSwap.CallOpts, v) } @@ -316,9 +316,9 @@ func (_ETHSwap *ETHSwapCallerSession) SecretValidates(secret [32]byte, secretHas return _ETHSwap.Contract.SecretValidates(&_ETHSwap.CallOpts, secret, secretHash) } -// Status is a free data retrieval call binding the contract method 0x61a16e33. +// Status is a free data retrieval call binding the contract method 0xdb3b419c. // -// Solidity: function status((bytes32,address,uint64,address,uint64) v) view returns((uint8,bytes32,uint256)) +// Solidity: function status((bytes32,uint256,address,uint64,address) v) view returns((uint8,bytes32,uint256)) func (_ETHSwap *ETHSwapCaller) Status(opts *bind.CallOpts, v ETHSwapVector) (ETHSwapStatus, error) { var out []interface{} err := _ETHSwap.contract.Call(opts, &out, "status", v) @@ -333,16 +333,16 @@ func (_ETHSwap *ETHSwapCaller) Status(opts *bind.CallOpts, v ETHSwapVector) (ETH } -// Status is a free data retrieval call binding the contract method 0x61a16e33. +// Status is a free data retrieval call binding the contract method 0xdb3b419c. // -// Solidity: function status((bytes32,address,uint64,address,uint64) v) view returns((uint8,bytes32,uint256)) +// Solidity: function status((bytes32,uint256,address,uint64,address) v) view returns((uint8,bytes32,uint256)) func (_ETHSwap *ETHSwapSession) Status(v ETHSwapVector) (ETHSwapStatus, error) { return _ETHSwap.Contract.Status(&_ETHSwap.CallOpts, v) } -// Status is a free data retrieval call binding the contract method 0x61a16e33. +// Status is a free data retrieval call binding the contract method 0xdb3b419c. // -// Solidity: function status((bytes32,address,uint64,address,uint64) v) view returns((uint8,bytes32,uint256)) +// Solidity: function status((bytes32,uint256,address,uint64,address) v) view returns((uint8,bytes32,uint256)) func (_ETHSwap *ETHSwapCallerSession) Status(v ETHSwapVector) (ETHSwapStatus, error) { return _ETHSwap.Contract.Status(&_ETHSwap.CallOpts, v) } @@ -378,65 +378,65 @@ func (_ETHSwap *ETHSwapCallerSession) Swaps(arg0 [32]byte) ([32]byte, error) { return _ETHSwap.Contract.Swaps(&_ETHSwap.CallOpts, arg0) } -// Initiate is a paid mutator transaction binding the contract method 0x64a97bff. +// Initiate is a paid mutator transaction binding the contract method 0x3da59631. // -// Solidity: function initiate((bytes32,address,uint64,address,uint64)[] contracts) payable returns() +// Solidity: function initiate((bytes32,uint256,address,uint64,address)[] contracts) payable returns() func (_ETHSwap *ETHSwapTransactor) Initiate(opts *bind.TransactOpts, contracts []ETHSwapVector) (*types.Transaction, error) { return _ETHSwap.contract.Transact(opts, "initiate", contracts) } -// Initiate is a paid mutator transaction binding the contract method 0x64a97bff. +// Initiate is a paid mutator transaction binding the contract method 0x3da59631. // -// Solidity: function initiate((bytes32,address,uint64,address,uint64)[] contracts) payable returns() +// Solidity: function initiate((bytes32,uint256,address,uint64,address)[] contracts) payable returns() func (_ETHSwap *ETHSwapSession) Initiate(contracts []ETHSwapVector) (*types.Transaction, error) { return _ETHSwap.Contract.Initiate(&_ETHSwap.TransactOpts, contracts) } -// Initiate is a paid mutator transaction binding the contract method 0x64a97bff. +// Initiate is a paid mutator transaction binding the contract method 0x3da59631. // -// Solidity: function initiate((bytes32,address,uint64,address,uint64)[] contracts) payable returns() +// Solidity: function initiate((bytes32,uint256,address,uint64,address)[] contracts) payable returns() func (_ETHSwap *ETHSwapTransactorSession) Initiate(contracts []ETHSwapVector) (*types.Transaction, error) { return _ETHSwap.Contract.Initiate(&_ETHSwap.TransactOpts, contracts) } -// Redeem is a paid mutator transaction binding the contract method 0x428b16e1. +// Redeem is a paid mutator transaction binding the contract method 0x23f0388b. // -// Solidity: function redeem(((bytes32,address,uint64,address,uint64),bytes32)[] redemptions) returns() +// Solidity: function redeem(((bytes32,uint256,address,uint64,address),bytes32)[] redemptions) returns() func (_ETHSwap *ETHSwapTransactor) Redeem(opts *bind.TransactOpts, redemptions []ETHSwapRedemption) (*types.Transaction, error) { return _ETHSwap.contract.Transact(opts, "redeem", redemptions) } -// Redeem is a paid mutator transaction binding the contract method 0x428b16e1. +// Redeem is a paid mutator transaction binding the contract method 0x23f0388b. // -// Solidity: function redeem(((bytes32,address,uint64,address,uint64),bytes32)[] redemptions) returns() +// Solidity: function redeem(((bytes32,uint256,address,uint64,address),bytes32)[] redemptions) returns() func (_ETHSwap *ETHSwapSession) Redeem(redemptions []ETHSwapRedemption) (*types.Transaction, error) { return _ETHSwap.Contract.Redeem(&_ETHSwap.TransactOpts, redemptions) } -// Redeem is a paid mutator transaction binding the contract method 0x428b16e1. +// Redeem is a paid mutator transaction binding the contract method 0x23f0388b. // -// Solidity: function redeem(((bytes32,address,uint64,address,uint64),bytes32)[] redemptions) returns() +// Solidity: function redeem(((bytes32,uint256,address,uint64,address),bytes32)[] redemptions) returns() func (_ETHSwap *ETHSwapTransactorSession) Redeem(redemptions []ETHSwapRedemption) (*types.Transaction, error) { return _ETHSwap.Contract.Redeem(&_ETHSwap.TransactOpts, redemptions) } -// Refund is a paid mutator transaction binding the contract method 0xd2544c06. +// Refund is a paid mutator transaction binding the contract method 0x8cd8dd97. // -// Solidity: function refund((bytes32,address,uint64,address,uint64) v) returns() +// Solidity: function refund((bytes32,uint256,address,uint64,address) v) returns() func (_ETHSwap *ETHSwapTransactor) Refund(opts *bind.TransactOpts, v ETHSwapVector) (*types.Transaction, error) { return _ETHSwap.contract.Transact(opts, "refund", v) } -// Refund is a paid mutator transaction binding the contract method 0xd2544c06. +// Refund is a paid mutator transaction binding the contract method 0x8cd8dd97. // -// Solidity: function refund((bytes32,address,uint64,address,uint64) v) returns() +// Solidity: function refund((bytes32,uint256,address,uint64,address) v) returns() func (_ETHSwap *ETHSwapSession) Refund(v ETHSwapVector) (*types.Transaction, error) { return _ETHSwap.Contract.Refund(&_ETHSwap.TransactOpts, v) } -// Refund is a paid mutator transaction binding the contract method 0xd2544c06. +// Refund is a paid mutator transaction binding the contract method 0x8cd8dd97. // -// Solidity: function refund((bytes32,address,uint64,address,uint64) v) returns() +// Solidity: function refund((bytes32,uint256,address,uint64,address) v) returns() func (_ETHSwap *ETHSwapTransactorSession) Refund(v ETHSwapVector) (*types.Transaction, error) { return _ETHSwap.Contract.Refund(&_ETHSwap.TransactOpts, v) } diff --git a/dex/networks/eth/params.go b/dex/networks/eth/params.go index 43f065db04..bb50dc5de9 100644 --- a/dex/networks/eth/params.go +++ b/dex/networks/eth/params.go @@ -5,6 +5,7 @@ package eth import ( "encoding/binary" + "encoding/hex" "encoding/json" "errors" "fmt" @@ -67,7 +68,7 @@ var ( 1: { dex.Mainnet: common.Address{}, dex.Testnet: common.Address{}, - dex.Simnet: common.Address{}, + dex.Simnet: common.HexToAddress("0x2f68e723b8989ba1c6a9f03e42f33cb7dc9d606f"), }, } @@ -86,22 +87,21 @@ var v0Gases = &Gases{ } var v1Gases = &Gases{ - // First swap used 48769 gas Recommended Gases.Swap = 63399 - // 4 additional swaps averaged 26904 gas each. Recommended Gases.SwapAdd = 34975 - // [48769 75679 102590 129486 156385] - Swap: 63_399, - SwapAdd: 34_975, - // First redeem used 39792 gas. Recommended Gases.Redeem = 51729 - // 4 additional redeems averaged 11037 gas each. recommended Gases.RedeemAdd = 14348 - // [39792 50836 61880 72898 83943] - - // Compare expected Swap + Redeem = 88k with UniSwap v2: 102k, v3: 127k - // A 1-match order is cheaper that UniSwap with v1 gases. - Redeem: 51_729, - RedeemAdd: 14_348, - // Average of 5 refunds: 40155. Recommended Gases.Refund = 52201 - // [40158 40158 40158 40158 40146] - Refund: 52_201, + // First swap used 48340 gas Recommended Gases.Swap = 62842 + Swap: 62_842, + // 4 additional swaps averaged 26499 gas each. Recommended Gases.SwapAdd = 34448 + // [48340 74837 101338 127836 154338] + SwapAdd: 34_448, + // First redeem used 39496 gas. Recommended Gases.Redeem = 51344 + Redeem: 51_344, + // 4 additional redeems averaged 10744 gas each. recommended Gases.RedeemAdd = 13967 + // [39496 50238 60984 71727 82473] + RedeemAdd: 13_967, + // *** Compare expected Swap + Redeem = 88k with UniSwap v2: 102k, v3: 127k + // *** A 1-match order is cheaper than UniSwap. + // Average of 5 refunds: 39918. Recommended Gases.Refund = 51893 + // [39918 39918 39918 39918 39918] + Refund: 51_893, } // LoadGenesisFile loads a Genesis config from a json file. @@ -129,8 +129,20 @@ func EncodeContractData(contractVersion uint32, locator []byte) []byte { return b } -// DecodeLocator unpacks the contract version and secret hash. -func DecodeLocator(data []byte) (contractVersion uint32, locator []byte, err error) { +func DecodeContractDataV0(data []byte) (secretHash [32]byte, err error) { + contractVer, secretHashB, err := DecodeContractData(data) + if err != nil { + return secretHash, err + } + if contractVer != 0 { + return secretHash, errors.New("not contract version 0") + } + copy(secretHash[:], secretHashB) + return +} + +// DecodeContractData unpacks the contract version and the locator. +func DecodeContractData(data []byte) (contractVersion uint32, locator []byte, err error) { if len(data) < 4 { err = errors.New("invalid short encoding") return @@ -278,9 +290,9 @@ func (ss SwapStep) String() string { type SwapVector struct { From common.Address To common.Address - Value uint64 + Value *big.Int SecretHash [32]byte - LockTime uint64 + LockTime uint64 // seconds } // Locator encodes a version 1 locator for the SwapVector. @@ -288,12 +300,23 @@ func (v *SwapVector) Locator() []byte { locator := make([]byte, LocatorV1Length) copy(locator[0:20], v.From[:]) copy(locator[20:40], v.To[:]) - binary.BigEndian.PutUint64(locator[40:48], v.Value) - copy(locator[48:80], v.SecretHash[:]) - binary.BigEndian.PutUint64(locator[80:88], v.LockTime) + v.Value.FillBytes(locator[40:72]) + copy(locator[72:104], v.SecretHash[:]) + binary.BigEndian.PutUint64(locator[104:112], v.LockTime) return locator } +func (v *SwapVector) String() string { + return fmt.Sprintf("{ from = %s, to = %s, value = %d, secret hash = %s, locktime = %s }", + v.From, v.To, v.Value, hex.EncodeToString(v.SecretHash[:]), time.UnixMilli(int64(v.LockTime))) +} + +func CompareVectors(v1, v2 *SwapVector) bool { + // Check vector equivalence. + return v1.Value.Cmp(v2.Value) == 0 && v1.To == v2.To && v1.From == v2.From && + v1.LockTime == v2.LockTime && v1.SecretHash == v2.SecretHash +} + // SwapStatus is the contract data that specifies the current contract state. type SwapStatus struct { BlockHeight uint64 @@ -393,9 +416,9 @@ func ParseV0Locator(locator []byte) (secretHash [32]byte, err error) { return } -// LocatorV1Length = from 20 + to 20 + value 8 + secretHash 32 + -// lockTime 8 = 88 bytes -const LocatorV1Length = 88 +// LocatorV1Length = from 20 + to 20 + value 32 + secretHash 32 + +// lockTime 8 = 112 bytes +const LocatorV1Length = 112 func ParseV1Locator(locator []byte) (v *SwapVector, err error) { // from 20 + to 20 + value 8 + secretHash 32 + lockTime 8 @@ -403,22 +426,49 @@ func ParseV1Locator(locator []byte) (v *SwapVector, err error) { v = &SwapVector{ From: common.BytesToAddress(locator[:20]), To: common.BytesToAddress(locator[20:40]), - Value: binary.BigEndian.Uint64(locator[40:48]), - LockTime: binary.BigEndian.Uint64(locator[80:88]), + Value: new(big.Int).SetBytes(locator[40:72]), + LockTime: binary.BigEndian.Uint64(locator[104:112]), } - copy(v.SecretHash[:], locator[48:80]) + copy(v.SecretHash[:], locator[72:104]) } else { err = fmt.Errorf("wrong v1 locator length. wanted %d, got %d", LocatorV1Length, len(locator)) } return } -func SwapVectorToAbigen(c *SwapVector) swapv1.ETHSwapVector { +func SwapVectorToAbigen(v *SwapVector) swapv1.ETHSwapVector { return swapv1.ETHSwapVector{ - SecretHash: c.SecretHash, - Initiator: c.From, - RefundTimestamp: c.LockTime, - Participant: c.To, - Value: c.Value, + SecretHash: v.SecretHash, + Initiator: v.From, + RefundTimestamp: v.LockTime, + Participant: v.To, + Value: v.Value, } } + +// ProtocolVersion assists in mapping the dex.Asset.Version to a contract +// version. +type ProtocolVersion uint32 + +const ( + ProtocolVersionZero ProtocolVersion = iota + ProtocolVersionV1Contracts +) + +func (v ProtocolVersion) ContractVersion() uint32 { + switch v { + case ProtocolVersionZero: + return 0 + case ProtocolVersionV1Contracts: + return 1 + default: + return ContractVersionUnknown + } +} + +var ( + // ContractVersionERC20 is passed as the contract version when calling + // ERC20 contract methods. + ContractVersionERC20 = ^uint32(0) + ContractVersionUnknown = ContractVersionERC20 - 1 +) diff --git a/dex/networks/eth/tokens.go b/dex/networks/eth/tokens.go index 815b8dd4a3..050e2c02c9 100644 --- a/dex/networks/eth/tokens.go +++ b/dex/networks/eth/tokens.go @@ -316,7 +316,7 @@ func MaybeReadSimnetAddrs() { func MaybeReadSimnetAddrsDir( dir string, - contractsAddrs map[uint32]map[dex.Network]common.Address, + contractAddrs map[uint32]map[dex.Network]common.Address, multiBalandAddresses map[dex.Network]common.Address, usdcToken *NetToken, usdtToken *NetToken, @@ -345,8 +345,8 @@ func MaybeReadSimnetAddrsDir( testUSDCSwapContractAddrFileV1 := filepath.Join(harnessDir, "usdc_swap_contract_address_v1.txt") multiBalanceContractAddrFile := filepath.Join(harnessDir, "multibalance_address.txt") - contractsAddrs[0][dex.Simnet] = maybeGetContractAddrFromFile(ethSwapContractAddrFileV0) - contractsAddrs[1][dex.Simnet] = maybeGetContractAddrFromFile(ethSwapContractAddrFileV1) + contractAddrs[0][dex.Simnet] = maybeGetContractAddrFromFile(ethSwapContractAddrFileV0) + contractAddrs[1][dex.Simnet] = maybeGetContractAddrFromFile(ethSwapContractAddrFileV1) multiBalandAddresses[dex.Simnet] = maybeGetContractAddrFromFile(multiBalanceContractAddrFile) usdcToken.SwapContracts[0].Address = maybeGetContractAddrFromFile(testUSDCSwapContractAddrFileV0) diff --git a/dex/networks/eth/txdata.go b/dex/networks/eth/txdata.go index e89db1e066..f80b8e0067 100644 --- a/dex/networks/eth/txdata.go +++ b/dex/networks/eth/txdata.go @@ -91,7 +91,7 @@ func ParseInitiateDataV0(calldata []byte) (map[[SecretHashSize]byte]*Initiation, return toReturn, nil } -// ParseRedeemData parses the calldata used to call the redeem function of a +// ParseRedeemDataV0 parses the calldata used to call the redeem function of a // specific version of the swap contract. It returns the the list of redemptions // done in the call and errors if the call data does not call redeem with expected // argument types. @@ -137,7 +137,7 @@ func ParseRedeemDataV0(calldata []byte) (map[[SecretHashSize]byte]*Redemption, e return toReturn, nil } -// ParseRefundData parses the calldata used to call the refund function of a +// ParseRefundDataV0 parses the calldata used to call the refund function of a // specific version of the swap contract. It returns the secret hash and errors // if the call data does not call refund with expected argument types. func ParseRefundDataV0(calldata []byte) ([32]byte, error) { @@ -191,17 +191,17 @@ func ParseInitiateDataV1(calldata []byte) (map[[SecretHashSize]byte]*SwapVector, } initiations, ok := args[0].value.([]struct { SecretHash [32]byte `json:"secretHash"` + Value *big.Int `json:"value"` Initiator common.Address `json:"initiator"` RefundTimestamp uint64 `json:"refundTimestamp"` Participant common.Address `json:"participant"` - Value uint64 `json:"value"` }) if !ok { return nil, fmt.Errorf("expected first arg of type []swapv1.ETHSwapContract but got %T", args[0].value) } // This is done for the compiler to ensure that the type defined above and - // swapv0.ETHSwapInitiation are the same, other than the tags. + // swapv1.ETHSwapVector are the same, other than the tags. if len(initiations) > 0 { _ = swapv1.ETHSwapVector(initiations[0]) } @@ -241,10 +241,10 @@ func ParseRedeemDataV1(calldata []byte) (map[[SecretHashSize]byte]*RedemptionV1, redemptions, ok := args[0].value.([]struct { V struct { SecretHash [32]uint8 `json:"secretHash"` + Value *big.Int `json:"value"` Initiator common.Address `json:"initiator"` RefundTimestamp uint64 `json:"refundTimestamp"` Participant common.Address `json:"participant"` - Value uint64 `json:"value"` } `json:"v"` Secret [32]uint8 `json:"secret"` }) @@ -253,9 +253,8 @@ func ParseRedeemDataV1(calldata []byte) (map[[SecretHashSize]byte]*RedemptionV1, } // This is done for the compiler to ensure that the type defined above and - // swapv0.ETHSwapRedemption are the same, other than the tags. + // swapv1.ETHSwapVector are the same, other than the tags. if len(redemptions) > 0 { - // Why can't I do ETHSwapRedemption directly? _ = swapv1.ETHSwapVector(redemptions[0].V) } toReturn := make(map[[SecretHashSize]byte]*RedemptionV1, len(redemptions)) @@ -294,10 +293,10 @@ func ParseRefundDataV1(calldata []byte) (*SwapVector, error) { } contract, ok := args[0].value.(struct { SecretHash [32]byte `json:"secretHash"` + Value *big.Int `json:"value"` Initiator common.Address `json:"initiator"` RefundTimestamp uint64 `json:"refundTimestamp"` Participant common.Address `json:"participant"` - Value uint64 `json:"value"` }) if !ok { return nil, fmt.Errorf("expected first arg of type [32]byte but got %T", args[0].value) diff --git a/dex/testing/eth/harness.sh b/dex/testing/eth/harness.sh index 1d2b24f5ea..02d33c2b79 100755 --- a/dex/testing/eth/harness.sh +++ b/dex/testing/eth/harness.sh @@ -45,8 +45,8 @@ ERC20_SWAP_V0="60a060405234801561001057600080fd5b50604051610e92380380610e9283398 TEST_TOKEN="608060405234801561001057600080fd5b50604051610a9d380380610a9d83398101604081905261002f91610140565b6040805180820190915260098152682a32b9ba2a37b5b2b760b91b602082015260039061005c9082610209565b506040805180820190915260038152621514d560ea1b60208201526004906100849082610209565b506005805460ff191660ff929092169190911790556909513ea9de0243800000600255600060208190526902544faa778090e000007f7d4921c2bc32c0110a31d16f4efb43c7a1228f1df7af765f608241dee5c62ebc8190557f59603491850c7d11499afe95b334ccfd92b48b36a15df31ef59ff5813fe3708281905573d12ab7cf72ccf1f3882ec99ddc53cd415635c3be9091527f5bd8dfce2dbb581d0922a094c40bab2f7d2f0ea9aaf275bf0fcc0f027a2ff91d556102c8565b60006020828403121561015257600080fd5b815160ff8116811461016357600080fd5b9392505050565b634e487b7160e01b600052604160045260246000fd5b600181811c9082168061019457607f821691505b6020821081036101b457634e487b7160e01b600052602260045260246000fd5b50919050565b601f82111561020457600081815260208120601f850160051c810160208610156101e15750805b601f850160051c820191505b81811015610200578281556001016101ed565b5050505b505050565b81516001600160401b038111156102225761022261016a565b610236816102308454610180565b846101ba565b602080601f83116001811461026b57600084156102535750858301515b600019600386901b1c1916600185901b178555610200565b600085815260208120601f198616915b8281101561029a5788860151825594840194600190910190840161027b565b50858210156102b85787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b6107c6806102d76000396000f3fe608060405234801561001057600080fd5b50600436106100a95760003560e01c806370a082311161007157806370a08231146101295780638ba4cc3c1461015257806395d89b4114610167578063a9059cbb1461016f578063ce714b5114610182578063dd62ed3e1461019557600080fd5b806306fdde03146100ae578063095ea7b3146100cc57806318160ddd146100ef57806323b872dd14610101578063313ce56714610114575b600080fd5b6100b66101ce565b6040516100c39190610610565b60405180910390f35b6100df6100da36600461067a565b610260565b60405190151581526020016100c3565b6002545b6040519081526020016100c3565b6100df61010f3660046106a4565b610277565b60055460405160ff90911681526020016100c3565b6100f36101373660046106e0565b6001600160a01b031660009081526020819052604090205490565b61016561016036600461067a565b610326565b005b6100b661036e565b6100df61017d36600461067a565b61037d565b6100df6101903660046106a4565b61038a565b6100f36101a3366004610702565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b6060600380546101dd90610735565b80601f016020809104026020016040519081016040528092919081815260200182805461020990610735565b80156102565780601f1061022b57610100808354040283529160200191610256565b820191906000526020600020905b81548152906001019060200180831161023957829003601f168201915b5050505050905090565b600061026d3384846103a1565b5060015b92915050565b6000610284848484610490565b6001600160a01b03841660009081526001602090815260408083203384529091529020548281101561030e5760405162461bcd60e51b815260206004820152602860248201527f45524332303a207472616e7366657220616d6f756e74206578636565647320616044820152676c6c6f77616e636560c01b60648201526084015b60405180910390fd5b61031b85338584036103a1565b506001949350505050565b8060026000828254610338919061076f565b90915550506001600160a01b0382166000908152602081905260408120805483929061036590849061076f565b90915550505050565b6060600480546101dd90610735565b600061026d338484610490565b60006103978484846103a1565b5060019392505050565b6001600160a01b0383166104035760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b6064820152608401610305565b6001600160a01b0382166104645760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b6064820152608401610305565b6001600160a01b0392831660009081526001602090815260408083209490951682529290925291902055565b6001600160a01b0383166104f45760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b6064820152608401610305565b6001600160a01b0382166105565760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b6064820152608401610305565b6001600160a01b038316600090815260208190526040902054818110156105ce5760405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b6064820152608401610305565b6001600160a01b0380851660009081526020819052604080822085850390559185168152908120805484929061060590849061076f565b909155505050505050565b600060208083528351808285015260005b8181101561063d57858101830151858201604001528201610621565b506000604082860101526040601f19601f8301168501019250505092915050565b80356001600160a01b038116811461067557600080fd5b919050565b6000806040838503121561068d57600080fd5b6106968361065e565b946020939093013593505050565b6000806000606084860312156106b957600080fd5b6106c28461065e565b92506106d06020850161065e565b9150604084013590509250925092565b6000602082840312156106f257600080fd5b6106fb8261065e565b9392505050565b6000806040838503121561071557600080fd5b61071e8361065e565b915061072c6020840161065e565b90509250929050565b600181811c9082168061074957607f821691505b60208210810361076957634e487b7160e01b600052602260045260246000fd5b50919050565b8082018082111561027157634e487b7160e01b600052601160045260246000fdfea2646970667358221220ae95c054153b06a9cd2a617063821b9c7e1348edfb08580448597f439ce7b63b64736f6c63430008120033" MULTIBALANCE_BIN="608060405234801561001057600080fd5b50610483806100206000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c8063d3e5ca8714610030575b600080fd5b61004361003e3660046102a5565b610059565b604051610050919061032b565b60405180910390f35b60606000610068836001610385565b67ffffffffffffffff8111156100805761008061039e565b6040519080825280602002602001820160405280156100a9578160200160208202803683370190505b509050846001600160a01b031631816000815181106100ca576100ca6103b4565b60200260200101818152505060005b838110156102805760008585838181106100f5576100f56103b4565b905060200201602081019061010a91906103ca565b905060006060826001600160a01b03167f70a08231b98ef4ca268c9cc3f6b4590e4bfec28280db06bb5d45e689f2a360be8a60405160240161015b91906001600160a01b0391909116815260200190565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b031990941693909317909252905161019991906103ec565b600060405180830381855afa9150503d80600081146101d4576040519150601f19603f3d011682016040523d82523d6000602084013e6101d9565b606091505b509092509050816102285760405162461bcd60e51b815260206004820152601560248201527418985b185b98d953d98818d85b1b0819985a5b1959605a1b604482015260640160405180910390fd5b60008180602001905181019061023e919061041b565b9050808661024d876001610385565b8151811061025d5761025d6103b4565b60200260200101818152505050505050808061027890610434565b9150506100d9565b50949350505050565b80356001600160a01b03811681146102a057600080fd5b919050565b6000806000604084860312156102ba57600080fd5b6102c384610289565b9250602084013567ffffffffffffffff808211156102e057600080fd5b818601915086601f8301126102f457600080fd5b81358181111561030357600080fd5b8760208260051b850101111561031857600080fd5b6020830194508093505050509250925092565b6020808252825182820181905260009190848201906040850190845b8181101561036357835183529284019291840191600101610347565b50909695505050505050565b634e487b7160e01b600052601160045260246000fd5b808201808211156103985761039861036f565b92915050565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b6000602082840312156103dc57600080fd5b6103e582610289565b9392505050565b6000825160005b8181101561040d57602081860181015185830152016103f3565b506000920191825250919050565b60006020828403121561042d57600080fd5b5051919050565b6000600182016104465761044661036f565b506001019056fea26469706673582212207074e3e189a2692e2841f7943a9de24bcdbca943ee6bfcbd83a2fa6e43ec497b64736f6c63430008120033" -ETH_SWAP_V1="608060405234801561001057600080fd5b50610e8e806100206000396000f3fe60806040526004361061007b5760003560e01c80637802689d1161004e5780637802689d1461011b578063d2544c061461013b578063eb84e7f21461015b578063ed7cbed71461019657600080fd5b8063428b16e11461008057806361a16e33146100a257806364a97bff146100d857806377d7e031146100eb575b600080fd5b34801561008c57600080fd5b506100a061009b366004610b6f565b6101b6565b005b3480156100ae57600080fd5b506100c26100bd366004610be4565b610447565b6040516100cf9190610c12565b60405180910390f35b6100a06100e6366004610c55565b610503565b3480156100f757600080fd5b5061010b610106366004610cb8565b61073b565b60405190151581526020016100cf565b34801561012757600080fd5b5061010b610136366004610be4565b6107b5565b34801561014757600080fd5b506100a0610156366004610be4565b6107e8565b34801561016757600080fd5b50610188610176366004610cda565b60006020819052908152604090205481565b6040519081526020016100cf565b3480156101a257600080fd5b506101886101b1366004610be4565b610a4a565b3233146101de5760405162461bcd60e51b81526004016101d590610cf3565b60405180910390fd5b6000805b828110156103b057368484838181106101fd576101fd610d1d565b60c0029190910191503390506102196080830160608401610d33565b6001600160a01b03161461025c5760405162461bcd60e51b815260206004820152600a6024820152691b9bdd08185d5d1a195960b21b60448201526064016101d5565b6000808061026984610b43565b92509250925060008111801561027e57504381105b6102ba5760405162461bcd60e51b815260206004820152600d60248201526c0756e66696c6c6564207377617609c1b60448201526064016101d5565b6102c582853561073b565b156103055760405162461bcd60e51b815260206004820152601060248201526f185b1c9958591e481c995919595b595960821b60448201526064016101d5565b61031460a0850135853561073b565b6103515760405162461bcd60e51b815260206004820152600e60248201526d1a5b9d985b1a59081cd958dc995d60921b60448201526064016101d5565b600083815260208190526040902060a0850180359091556103759060808601610d63565b61038390633b9aca00610da3565b6103979067ffffffffffffffff1687610dd3565b95505050505080806103a890610deb565b9150506101e2565b50604051600090339083908381818185875af1925050503d80600081146103f3576040519150601f19603f3d011682016040523d82523d6000602084013e6103f8565b606091505b50909150506001811515146104415760405162461bcd60e51b815260206004820152600f60248201526e1d1c985b9cd9995c8819985a5b1959608a1b60448201526064016101d5565b50505050565b60408051606081018252600080825260208201819052918101829052908061046e84610b43565b92509250506104986040805160608101909152806000815260006020820181905260409091015290565b816000036104bf578060005b908160038111156104b7576104b7610bfc565b9052506104fb565b600183016104cf578060036104a4565b6104da83863561073b565b156104ef5760028152602081018390526104fb565b60018152604081018290525b949350505050565b3233146105225760405162461bcd60e51b81526004016101d590610cf3565b6000805b828110156106fc573684848381811061054157610541610d1d565b905060a002019050600081608001602081019061055e9190610d63565b67ffffffffffffffff161161059d5760405162461bcd60e51b81526020600482015260056024820152640c081d985b60da1b60448201526064016101d5565b60006105af6060830160408401610d63565b67ffffffffffffffff16116105fa5760405162461bcd60e51b815260206004820152601160248201527003020726566756e6454696d657374616d7607c1b60448201526064016101d5565b600061060582610a4a565b60008181526020819052604090205490915080156106565760405162461bcd60e51b815260206004820152600e60248201526d73776170206e6f7420656d70747960901b60448201526064016101d5565b504361066381843561073b565b156106a15760405162461bcd60e51b815260206004820152600e60248201526d3430b9b41031b7b63634b9b4b7b760911b60448201526064016101d5565b60008281526020819052604090208190556106c260a0840160808501610d63565b6106d090633b9aca00610da3565b6106e49067ffffffffffffffff1686610dd3565b945050505080806106f490610deb565b915050610526565b503481146107365760405162461bcd60e51b8152602060048201526007602482015266189859081d985b60ca1b60448201526064016101d5565b505050565b60008160028460405160200161075391815260200190565b60408051601f198184030181529082905261076d91610e04565b602060405180830381855afa15801561078a573d6000803e3d6000fd5b5050506040513d601f19601f820116820180604052508101906107ad9190610e3f565b149392505050565b60008060006107c384610b43565b9250925050806000141580156104fb57506107df82853561073b565b15949350505050565b3233146108075760405162461bcd60e51b81526004016101d590610cf3565b6108176060820160408301610d63565b67ffffffffffffffff164210156108675760405162461bcd60e51b81526020600482015260146024820152731b1bd8dadd1a5b59481b9bdd08195e1c1a5c995960621b60448201526064016101d5565b600080600061087584610b43565b92509250925060008111801561088b5750438111155b6108c95760405162461bcd60e51b815260206004820152600f60248201526e73776170206e6f742061637469766560881b60448201526064016101d5565b6108d482853561073b565b156109195760405162461bcd60e51b81526020600482015260156024820152741cddd85c08185b1c9958591e481c995919595b5959605a1b60448201526064016101d5565b600182016109615760405162461bcd60e51b81526020600482015260156024820152741cddd85c08185b1c9958591e481c99599d5b991959605a1b60448201526064016101d5565b600083815260208181526040808320600019905561098491908701908701610d33565b6001600160a01b031661099d60a0870160808801610d63565b6109ab90633b9aca00610da3565b67ffffffffffffffff1660405160006040518083038185875af1925050503d80600081146109f5576040519150601f19603f3d011682016040523d82523d6000602084013e6109fa565b606091505b5090915050600181151514610a435760405162461bcd60e51b815260206004820152600f60248201526e1d1c985b9cd9995c8819985a5b1959608a1b60448201526064016101d5565b5050505050565b600060028235610a606040850160208601610d33565b60601b846060016020810190610a769190610d33565b60601b610a8960a0870160808801610d63565b60c01b610a9c6060880160408901610d63565b6040805160208101969096526bffffffffffffffffffffffff19948516908601529190921660548401526001600160c01b0319918216606884015260c01b16607082015260780160408051601f1981840301815290829052610afd91610e04565b602060405180830381855afa158015610b1a573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190610b3d9190610e3f565b92915050565b600080600080610b5285610a4a565b600081815260208190526040902054909690955085945092505050565b60008060208385031215610b8257600080fd5b823567ffffffffffffffff80821115610b9a57600080fd5b818501915085601f830112610bae57600080fd5b813581811115610bbd57600080fd5b86602060c083028501011115610bd257600080fd5b60209290920196919550909350505050565b600060a08284031215610bf657600080fd5b50919050565b634e487b7160e01b600052602160045260246000fd5b8151606082019060048110610c3757634e487b7160e01b600052602160045260246000fd5b80835250602083015160208301526040830151604083015292915050565b60008060208385031215610c6857600080fd5b823567ffffffffffffffff80821115610c8057600080fd5b818501915085601f830112610c9457600080fd5b813581811115610ca357600080fd5b86602060a083028501011115610bd257600080fd5b60008060408385031215610ccb57600080fd5b50508035926020909101359150565b600060208284031215610cec57600080fd5b5035919050565b60208082526010908201526f39b2b73232b910109e9037b934b3b4b760811b604082015260600190565b634e487b7160e01b600052603260045260246000fd5b600060208284031215610d4557600080fd5b81356001600160a01b0381168114610d5c57600080fd5b9392505050565b600060208284031215610d7557600080fd5b813567ffffffffffffffff81168114610d5c57600080fd5b634e487b7160e01b600052601160045260246000fd5b600067ffffffffffffffff80831681851681830481118215151615610dca57610dca610d8d565b02949350505050565b60008219821115610de657610de6610d8d565b500190565b600060018201610dfd57610dfd610d8d565b5060010190565b6000825160005b81811015610e255760208186018101518583015201610e0b565b81811115610e34576000828501525b509190910192915050565b600060208284031215610e5157600080fd5b505191905056fea2646970667358221220d735868682c69bb69ed936f11e2f136e7da44cb347e0b1d67acd1e088ea13dd664736f6c634300080f0033" -ERC20_SWAP_V1="60a060405234801561001057600080fd5b506040516111cb3803806111cb83398101604081905261002f91610040565b6001600160a01b0316608052610070565b60006020828403121561005257600080fd5b81516001600160a01b038116811461006957600080fd5b9392505050565b60805161112b6100a060003960008181610158015281816104570152818161083e0152610b5d015261112b6000f3fe6080604052600436106100865760003560e01c80637802689d116100595780637802689d146101265780638c8e8fee14610146578063d2544c0614610192578063eb84e7f2146101b2578063ed7cbed7146101ed57600080fd5b8063428b16e11461008b57806361a16e33146100ad57806364a97bff146100e357806377d7e031146100f6575b600080fd5b34801561009757600080fd5b506100ab6100a6366004610dea565b61020d565b005b3480156100b957600080fd5b506100cd6100c8366004610e5f565b610533565b6040516100da9190610e8d565b60405180910390f35b6100ab6100f1366004610ed0565b6105ef565b34801561010257600080fd5b50610116610111366004610f33565b610918565b60405190151581526020016100da565b34801561013257600080fd5b50610116610141366004610e5f565b610992565b34801561015257600080fd5b5061017a7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016100da565b34801561019e57600080fd5b506100ab6101ad366004610e5f565b6109c5565b3480156101be57600080fd5b506101df6101cd366004610f55565b60006020819052908152604090205481565b6040519081526020016100da565b3480156101f957600080fd5b506101df610208366004610e5f565b610cc5565b3233146102355760405162461bcd60e51b815260040161022c90610f6e565b60405180910390fd5b6000805b82811015610407573684848381811061025457610254610f98565b60c0029190910191503390506102706080830160608401610fae565b6001600160a01b0316146102b35760405162461bcd60e51b815260206004820152600a6024820152691b9bdd08185d5d1a195960b21b604482015260640161022c565b600080806102c084610dbe565b9250925092506000811180156102d557504381105b6103115760405162461bcd60e51b815260206004820152600d60248201526c0756e66696c6c6564207377617609c1b604482015260640161022c565b61031c828535610918565b1561035c5760405162461bcd60e51b815260206004820152601060248201526f185b1c9958591e481c995919595b595960821b604482015260640161022c565b61036b60a08501358535610918565b6103a85760405162461bcd60e51b815260206004820152600e60248201526d1a5b9d985b1a59081cd958dc995d60921b604482015260640161022c565b600083815260208190526040902060a0850180359091556103cc9060808601610fde565b6103da90633b9aca0061101e565b6103ee9067ffffffffffffffff168761104e565b95505050505080806103ff90611066565b915050610239565b5060408051336024820152604480820184905282518083039091018152606490910182526020810180516001600160e01b031663a9059cbb60e01b17905290516000916060916001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016916104819161107f565b6000604051808303816000865af19150503d80600081146104be576040519150601f19603f3d011682016040523d82523d6000602084013e6104c3565b606091505b5090925090508180156104ee5750805115806104ee5750808060200190518101906104ee91906110ba565b61052c5760405162461bcd60e51b815260206004820152600f60248201526e1d1c985b9cd9995c8819985a5b1959608a1b604482015260640161022c565b5050505050565b60408051606081018252600080825260208201819052918101829052908061055a84610dbe565b92509250506105846040805160608101909152806000815260006020820181905260409091015290565b816000036105ab578060005b908160038111156105a3576105a3610e77565b9052506105e7565b600183016105bb57806003610590565b6105c6838635610918565b156105db5760028152602081018390526105e7565b60018152604081018290525b949350505050565b32331461060e5760405162461bcd60e51b815260040161022c90610f6e565b6000805b828110156107e8573684848381811061062d5761062d610f98565b905060a002019050600081608001602081019061064a9190610fde565b67ffffffffffffffff16116106895760405162461bcd60e51b81526020600482015260056024820152640c081d985b60da1b604482015260640161022c565b600061069b6060830160408401610fde565b67ffffffffffffffff16116106e65760405162461bcd60e51b815260206004820152601160248201527003020726566756e6454696d657374616d7607c1b604482015260640161022c565b60006106f182610cc5565b60008181526020819052604090205490915080156107425760405162461bcd60e51b815260206004820152600e60248201526d73776170206e6f7420656d70747960901b604482015260640161022c565b504361074f818435610918565b1561078d5760405162461bcd60e51b815260206004820152600e60248201526d3430b9b41031b7b63634b9b4b7b760911b604482015260640161022c565b60008281526020819052604090208190556107ae60a0840160808501610fde565b6107bc90633b9aca0061101e565b6107d09067ffffffffffffffff168661104e565b945050505080806107e090611066565b915050610612565b5060408051336024820152306044820152606480820184905282518083039091018152608490910182526020810180516001600160e01b03166323b872dd60e01b17905290516000916060916001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016916108689161107f565b6000604051808303816000865af19150503d80600081146108a5576040519150601f19603f3d011682016040523d82523d6000602084013e6108aa565b606091505b5090925090508180156108d55750805115806108d55750808060200190518101906108d591906110ba565b61052c5760405162461bcd60e51b81526020600482015260146024820152731d1c985b9cd9995c88199c9bdb4819985a5b195960621b604482015260640161022c565b60008160028460405160200161093091815260200190565b60408051601f198184030181529082905261094a9161107f565b602060405180830381855afa158015610967573d6000803e3d6000fd5b5050506040513d601f19601f8201168201806040525081019061098a91906110dc565b149392505050565b60008060006109a084610dbe565b9250925050806000141580156105e757506109bc828535610918565b15949350505050565b3233146109e45760405162461bcd60e51b815260040161022c90610f6e565b6109f46060820160408301610fde565b67ffffffffffffffff16421015610a445760405162461bcd60e51b81526020600482015260146024820152731b1bd8dadd1a5b59481b9bdd08195e1c1a5c995960621b604482015260640161022c565b6000806000610a5284610dbe565b925092509250600081118015610a685750438111155b610aa65760405162461bcd60e51b815260206004820152600f60248201526e73776170206e6f742061637469766560881b604482015260640161022c565b610ab1828535610918565b15610af65760405162461bcd60e51b81526020600482015260156024820152741cddd85c08185b1c9958591e481c995919595b5959605a1b604482015260640161022c565b60018201610b3e5760405162461bcd60e51b81526020600482015260156024820152741cddd85c08185b1c9958591e481c99599d5b991959605a1b604482015260640161022c565b6000838152602081905260408120600019905560606001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000167fa9059cbb2ab09eb219583f4a59a5d0623ade346d962bcd4e46b11da047c9049b33610baf60a08a0160808b01610fde565b6040516001600160a01b03909216602483015267ffffffffffffffff16604482015260640160408051601f198184030181529181526020820180516001600160e01b03166001600160e01b0319909416939093179092529051610c12919061107f565b6000604051808303816000865af19150503d8060008114610c4f576040519150601f19603f3d011682016040523d82523d6000602084013e610c54565b606091505b509092509050818015610c7f575080511580610c7f575080806020019051810190610c7f91906110ba565b610cbd5760405162461bcd60e51b815260206004820152600f60248201526e1d1c985b9cd9995c8819985a5b1959608a1b604482015260640161022c565b505050505050565b600060028235610cdb6040850160208601610fae565b60601b846060016020810190610cf19190610fae565b60601b610d0460a0870160808801610fde565b60c01b610d176060880160408901610fde565b6040805160208101969096526bffffffffffffffffffffffff19948516908601529190921660548401526001600160c01b0319918216606884015260c01b16607082015260780160408051601f1981840301815290829052610d789161107f565b602060405180830381855afa158015610d95573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190610db891906110dc565b92915050565b600080600080610dcd85610cc5565b600081815260208190526040902054909690955085945092505050565b60008060208385031215610dfd57600080fd5b823567ffffffffffffffff80821115610e1557600080fd5b818501915085601f830112610e2957600080fd5b813581811115610e3857600080fd5b86602060c083028501011115610e4d57600080fd5b60209290920196919550909350505050565b600060a08284031215610e7157600080fd5b50919050565b634e487b7160e01b600052602160045260246000fd5b8151606082019060048110610eb257634e487b7160e01b600052602160045260246000fd5b80835250602083015160208301526040830151604083015292915050565b60008060208385031215610ee357600080fd5b823567ffffffffffffffff80821115610efb57600080fd5b818501915085601f830112610f0f57600080fd5b813581811115610f1e57600080fd5b86602060a083028501011115610e4d57600080fd5b60008060408385031215610f4657600080fd5b50508035926020909101359150565b600060208284031215610f6757600080fd5b5035919050565b60208082526010908201526f39b2b73232b910109e9037b934b3b4b760811b604082015260600190565b634e487b7160e01b600052603260045260246000fd5b600060208284031215610fc057600080fd5b81356001600160a01b0381168114610fd757600080fd5b9392505050565b600060208284031215610ff057600080fd5b813567ffffffffffffffff81168114610fd757600080fd5b634e487b7160e01b600052601160045260246000fd5b600067ffffffffffffffff8083168185168183048111821515161561104557611045611008565b02949350505050565b6000821982111561106157611061611008565b500190565b60006001820161107857611078611008565b5060010190565b6000825160005b818110156110a05760208186018101518583015201611086565b818111156110af576000828501525b509190910192915050565b6000602082840312156110cc57600080fd5b81518015158114610fd757600080fd5b6000602082840312156110ee57600080fd5b505191905056fea2646970667358221220319d89b87a0d5782925310133ed5d12d2ff562b0786611308396804fcad176fc64736f6c634300080f0033" +ETH_SWAP_V1="608060405234801561001057600080fd5b50610df2806100206000396000f3fe60806040526004361061007b5760003560e01c8063a76f9f2d1161004e578063a76f9f2d1461010a578063d5cfd04914610138578063db3b419c14610158578063eb84e7f21461018557600080fd5b806323f0388b146100805780633da59631146100a257806377d7e031146100b55780638cd8dd97146100ea575b600080fd5b34801561008c57600080fd5b506100a061009b366004610b14565b6101b2565b005b6100a06100b0366004610b89565b610420565b3480156100c157600080fd5b506100d56100d0366004610bec565b61069d565b60405190151581526020015b60405180910390f35b3480156100f657600080fd5b506100a0610105366004610c0e565b610719565b34801561011657600080fd5b5061012a610125366004610c0e565b610911565b6040519081526020016100e1565b34801561014457600080fd5b506100d5610153366004610c0e565b6109f9565b34801561016457600080fd5b50610178610173366004610c0e565b610a2d565b6040516100e19190610c3c565b34801561019157600080fd5b5061012a6101a0366004610c7f565b60006020819052908152604090205481565b3233146101da5760405162461bcd60e51b81526004016101d190610c98565b60405180910390fd5b6000805b8281101561038957368484838181106101f9576101f9610cc2565b60c00291909101915033905061021560a0830160808401610cd8565b6001600160a01b0316146102585760405162461bcd60e51b815260206004820152600a6024820152691b9bdd08185d5d1a195960b21b60448201526064016101d1565b6000808061026584610ae8565b92509250925060008111801561027a57504381105b6102b65760405162461bcd60e51b815260206004820152600d60248201526c0756e66696c6c6564207377617609c1b60448201526064016101d1565b6102c182853561069d565b156103015760405162461bcd60e51b815260206004820152601060248201526f185b1c9958591e481c995919595b595960821b60448201526064016101d1565b61031060a0850135853561069d565b61034d5760405162461bcd60e51b815260206004820152600e60248201526d1a5b9d985b1a59081cd958dc995d60921b60448201526064016101d1565b60008381526020818152604090912060a086013590556103709085013587610d1e565b955050505050808061038190610d31565b9150506101de565b50604051600090339083908381818185875af1925050503d80600081146103cc576040519150601f19603f3d011682016040523d82523d6000602084013e6103d1565b606091505b509091505060018115151461041a5760405162461bcd60e51b815260206004820152600f60248201526e1d1c985b9cd9995c8819985a5b1959608a1b60448201526064016101d1565b50505050565b32331461043f5760405162461bcd60e51b81526004016101d190610c98565b6000805b8281101561065e573684848381811061045e5761045e610cc2565b905060a00201905060008160200135116104a25760405162461bcd60e51b81526020600482015260056024820152640c081d985b60da1b60448201526064016101d1565b60006104b46080830160608401610d4a565b67ffffffffffffffff16116104ff5760405162461bcd60e51b815260206004820152601160248201527003020726566756e6454696d657374616d7607c1b60448201526064016101d1565b7f5069ec89f08d9ca0424bb5a5f59c3c60ed50cf06af5911a368e41e771763bfaf8135016105805760405162461bcd60e51b815260206004820152602860248201527f696c6c6567616c2073656372657420686173682028726566756e64207265636f604482015267726420686173682960c01b60648201526084016101d1565b600061058b82610911565b60008181526020819052604090205490915080156105dc5760405162461bcd60e51b815260206004820152600e60248201526d73776170206e6f7420656d70747960901b60448201526064016101d1565b50436105e981843561069d565b156106275760405162461bcd60e51b815260206004820152600e60248201526d3430b9b41031b7b63634b9b4b7b760911b60448201526064016101d1565b6000828152602081815260409091208290556106469084013586610d1e565b9450505050808061065690610d31565b915050610443565b503481146106985760405162461bcd60e51b8152602060048201526007602482015266189859081d985b60ca1b60448201526064016101d1565b505050565b6000816002846040516020016106b591815260200190565b60408051601f19818403018152908290526106cf91610d74565b602060405180830381855afa1580156106ec573d6000803e3d6000fd5b5050506040513d601f19601f8201168201806040525081019061070f9190610da3565b1490505b92915050565b3233146107385760405162461bcd60e51b81526004016101d190610c98565b6107486080820160608301610d4a565b67ffffffffffffffff164210156107985760405162461bcd60e51b81526020600482015260146024820152731b1bd8dadd1a5b59481b9bdd08195e1c1a5c995960621b60448201526064016101d1565b60008060006107a684610ae8565b9250925092506000811180156107bc5750438111155b6107fa5760405162461bcd60e51b815260206004820152600f60248201526e73776170206e6f742061637469766560881b60448201526064016101d1565b61080582853561069d565b1561084a5760405162461bcd60e51b81526020600482015260156024820152741cddd85c08185b1c9958591e481c995919595b5959605a1b60448201526064016101d1565b600083815260208190526040808220600019905561086e9060608701908701610cd8565b6001600160a01b0316856020013560405160006040518083038185875af1925050503d80600081146108bc576040519150601f19603f3d011682016040523d82523d6000602084013e6108c1565b606091505b509091505060018115151461090a5760405162461bcd60e51b815260206004820152600f60248201526e1d1c985b9cd9995c8819985a5b1959608a1b60448201526064016101d1565b5050505050565b6000600282356109276060850160408601610cd8565b60601b61093a60a0860160808701610cd8565b60601b856020013560001b8660600160208101906109589190610d4a565b6040805160208101969096526bffffffffffffffffffffffff199485169086015292909116605484015260688301526001600160c01b031960c09190911b16608882015260900160408051601f19818403018152908290526109b991610d74565b602060405180830381855afa1580156109d6573d6000803e3d6000fd5b5050506040513d601f19601f820116820180604052508101906107139190610da3565b6000806000610a0784610ae8565b925092505080600014158015610a255750610a2382853561069d565b155b949350505050565b604080516060810182526000808252602082018190529181018290529080610a5484610ae8565b9250925050610a7e6040805160608101909152806000815260006020820181905260409091015290565b81600003610aa5578060005b90816003811115610a9d57610a9d610c26565b905250610a25565b60018301610ab557806003610a8a565b610ac083863561069d565b15610ad5576002815260208101839052610a25565b6001815260408101919091529392505050565b600080600080610af785610911565b600081815260208190526040902054909690955085945092505050565b60008060208385031215610b2757600080fd5b823567ffffffffffffffff80821115610b3f57600080fd5b818501915085601f830112610b5357600080fd5b813581811115610b6257600080fd5b86602060c083028501011115610b7757600080fd5b60209290920196919550909350505050565b60008060208385031215610b9c57600080fd5b823567ffffffffffffffff80821115610bb457600080fd5b818501915085601f830112610bc857600080fd5b813581811115610bd757600080fd5b86602060a083028501011115610b7757600080fd5b60008060408385031215610bff57600080fd5b50508035926020909101359150565b600060a08284031215610c2057600080fd5b50919050565b634e487b7160e01b600052602160045260246000fd5b8151606082019060048110610c6157634e487b7160e01b600052602160045260246000fd5b80835250602083015160208301526040830151604083015292915050565b600060208284031215610c9157600080fd5b5035919050565b60208082526010908201526f39b2b73232b910109e9037b934b3b4b760811b604082015260600190565b634e487b7160e01b600052603260045260246000fd5b600060208284031215610cea57600080fd5b81356001600160a01b0381168114610d0157600080fd5b9392505050565b634e487b7160e01b600052601160045260246000fd5b8082018082111561071357610713610d08565b600060018201610d4357610d43610d08565b5060010190565b600060208284031215610d5c57600080fd5b813567ffffffffffffffff81168114610d0157600080fd5b6000825160005b81811015610d955760208186018101518583015201610d7b565b506000920191825250919050565b600060208284031215610db557600080fd5b505191905056fea2646970667358221220420ce9cfd5a39be8e719369517f704148d580fb33472b5b8947c77d61eee66d664736f6c63430008120033" +ERC20_SWAP_V1="60a060405234801561001057600080fd5b5060405161110938038061110983398101604081905261002f91610040565b6001600160a01b0316608052610070565b60006020828403121561005257600080fd5b81516001600160a01b038116811461006957600080fd5b9392505050565b6080516110696100a06000396000818161010701528181610430015281816107a00152610a8001526110696000f3fe6080604052600436106100865760003560e01c80638cd8dd97116100595780638cd8dd9714610141578063a76f9f2d14610161578063d5cfd0491461018f578063db3b419c146101af578063eb84e7f2146101dc57600080fd5b806323f0388b1461008b5780633da59631146100ad57806377d7e031146100c05780638c8e8fee146100f5575b600080fd5b34801561009757600080fd5b506100ab6100a6366004610d69565b610209565b005b6100ab6100bb366004610dde565b61050c565b3480156100cc57600080fd5b506100e06100db366004610e41565b61087a565b60405190151581526020015b60405180910390f35b34801561010157600080fd5b506101297f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016100ec565b34801561014d57600080fd5b506100ab61015c366004610e63565b6108f6565b34801561016d57600080fd5b5061018161017c366004610e63565b610b66565b6040519081526020016100ec565b34801561019b57600080fd5b506100e06101aa366004610e63565b610c4e565b3480156101bb57600080fd5b506101cf6101ca366004610e63565b610c82565b6040516100ec9190610e91565b3480156101e857600080fd5b506101816101f7366004610ed4565b60006020819052908152604090205481565b3233146102315760405162461bcd60e51b815260040161022890610eed565b60405180910390fd5b6000805b828110156103e0573684848381811061025057610250610f17565b60c00291909101915033905061026c60a0830160808401610f2d565b6001600160a01b0316146102af5760405162461bcd60e51b815260206004820152600a6024820152691b9bdd08185d5d1a195960b21b6044820152606401610228565b600080806102bc84610d3d565b9250925092506000811180156102d157504381105b61030d5760405162461bcd60e51b815260206004820152600d60248201526c0756e66696c6c6564207377617609c1b6044820152606401610228565b61031882853561087a565b156103585760405162461bcd60e51b815260206004820152601060248201526f185b1c9958591e481c995919595b595960821b6044820152606401610228565b61036760a0850135853561087a565b6103a45760405162461bcd60e51b815260206004820152600e60248201526d1a5b9d985b1a59081cd958dc995d60921b6044820152606401610228565b60008381526020818152604090912060a086013590556103c79085013587610f73565b95505050505080806103d890610f86565b915050610235565b5060408051336024820152604480820184905282518083039091018152606490910182526020810180516001600160e01b031663a9059cbb60e01b17905290516000916060916001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169161045a91610f9f565b6000604051808303816000865af19150503d8060008114610497576040519150601f19603f3d011682016040523d82523d6000602084013e61049c565b606091505b5090925090508180156104c75750805115806104c75750808060200190518101906104c79190610fce565b6105055760405162461bcd60e51b815260206004820152600f60248201526e1d1c985b9cd9995c8819985a5b1959608a1b6044820152606401610228565b5050505050565b32331461052b5760405162461bcd60e51b815260040161022890610eed565b6000805b8281101561074a573684848381811061054a5761054a610f17565b905060a002019050600081602001351161058e5760405162461bcd60e51b81526020600482015260056024820152640c081d985b60da1b6044820152606401610228565b60006105a06080830160608401610ff0565b67ffffffffffffffff16116105eb5760405162461bcd60e51b815260206004820152601160248201527003020726566756e6454696d657374616d7607c1b6044820152606401610228565b7f5069ec89f08d9ca0424bb5a5f59c3c60ed50cf06af5911a368e41e771763bfaf81350161066c5760405162461bcd60e51b815260206004820152602860248201527f696c6c6567616c2073656372657420686173682028726566756e64207265636f604482015267726420686173682960c01b6064820152608401610228565b600061067782610b66565b60008181526020819052604090205490915080156106c85760405162461bcd60e51b815260206004820152600e60248201526d73776170206e6f7420656d70747960901b6044820152606401610228565b50436106d581843561087a565b156107135760405162461bcd60e51b815260206004820152600e60248201526d3430b9b41031b7b63634b9b4b7b760911b6044820152606401610228565b6000828152602081815260409091208290556107329084013586610f73565b9450505050808061074290610f86565b91505061052f565b5060408051336024820152306044820152606480820184905282518083039091018152608490910182526020810180516001600160e01b03166323b872dd60e01b17905290516000916060916001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016916107ca91610f9f565b6000604051808303816000865af19150503d8060008114610807576040519150601f19603f3d011682016040523d82523d6000602084013e61080c565b606091505b5090925090508180156108375750805115806108375750808060200190518101906108379190610fce565b6105055760405162461bcd60e51b81526020600482015260146024820152731d1c985b9cd9995c88199c9bdb4819985a5b195960621b6044820152606401610228565b60008160028460405160200161089291815260200190565b60408051601f19818403018152908290526108ac91610f9f565b602060405180830381855afa1580156108c9573d6000803e3d6000fd5b5050506040513d601f19601f820116820180604052508101906108ec919061101a565b1490505b92915050565b3233146109155760405162461bcd60e51b815260040161022890610eed565b6109256080820160608301610ff0565b67ffffffffffffffff164210156109755760405162461bcd60e51b81526020600482015260146024820152731b1bd8dadd1a5b59481b9bdd08195e1c1a5c995960621b6044820152606401610228565b600080600061098384610d3d565b9250925092506000811180156109995750438111155b6109d75760405162461bcd60e51b815260206004820152600f60248201526e73776170206e6f742061637469766560881b6044820152606401610228565b6109e282853561087a565b15610a275760405162461bcd60e51b81526020600482015260156024820152741cddd85c08185b1c9958591e481c995919595b5959605a1b6044820152606401610228565b6000838152602081815260408083206000199055805133602482015287830135604480830191909152825180830390910181526064909101825291820180516001600160e01b031663a9059cbb60e01b179052516060917f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031691610ab39190610f9f565b6000604051808303816000865af19150503d8060008114610af0576040519150601f19603f3d011682016040523d82523d6000602084013e610af5565b606091505b509092509050818015610b20575080511580610b20575080806020019051810190610b209190610fce565b610b5e5760405162461bcd60e51b815260206004820152600f60248201526e1d1c985b9cd9995c8819985a5b1959608a1b6044820152606401610228565b505050505050565b600060028235610b7c6060850160408601610f2d565b60601b610b8f60a0860160808701610f2d565b60601b856020013560001b866060016020810190610bad9190610ff0565b6040805160208101969096526bffffffffffffffffffffffff199485169086015292909116605484015260688301526001600160c01b031960c09190911b16608882015260900160408051601f1981840301815290829052610c0e91610f9f565b602060405180830381855afa158015610c2b573d6000803e3d6000fd5b5050506040513d601f19601f820116820180604052508101906108f0919061101a565b6000806000610c5c84610d3d565b925092505080600014158015610c7a5750610c7882853561087a565b155b949350505050565b604080516060810182526000808252602082018190529181018290529080610ca984610d3d565b9250925050610cd36040805160608101909152806000815260006020820181905260409091015290565b81600003610cfa578060005b90816003811115610cf257610cf2610e7b565b905250610c7a565b60018301610d0a57806003610cdf565b610d1583863561087a565b15610d2a576002815260208101839052610c7a565b6001815260408101919091529392505050565b600080600080610d4c85610b66565b600081815260208190526040902054909690955085945092505050565b60008060208385031215610d7c57600080fd5b823567ffffffffffffffff80821115610d9457600080fd5b818501915085601f830112610da857600080fd5b813581811115610db757600080fd5b86602060c083028501011115610dcc57600080fd5b60209290920196919550909350505050565b60008060208385031215610df157600080fd5b823567ffffffffffffffff80821115610e0957600080fd5b818501915085601f830112610e1d57600080fd5b813581811115610e2c57600080fd5b86602060a083028501011115610dcc57600080fd5b60008060408385031215610e5457600080fd5b50508035926020909101359150565b600060a08284031215610e7557600080fd5b50919050565b634e487b7160e01b600052602160045260246000fd5b8151606082019060048110610eb657634e487b7160e01b600052602160045260246000fd5b80835250602083015160208301526040830151604083015292915050565b600060208284031215610ee657600080fd5b5035919050565b60208082526010908201526f39b2b73232b910109e9037b934b3b4b760811b604082015260600190565b634e487b7160e01b600052603260045260246000fd5b600060208284031215610f3f57600080fd5b81356001600160a01b0381168114610f5657600080fd5b9392505050565b634e487b7160e01b600052601160045260246000fd5b808201808211156108f0576108f0610f5d565b600060018201610f9857610f98610f5d565b5060010190565b6000825160005b81811015610fc05760208186018101518583015201610fa6565b506000920191825250919050565b600060208284031215610fe057600080fd5b81518015158114610f5657600080fd5b60006020828403121561100257600080fd5b813567ffffffffffffffff81168114610f5657600080fd5b60006020828403121561102c57600080fd5b505191905056fea2646970667358221220acfd007366a18ada193efab33a7139cee67cfd39d1c89075b5534cc57862baa464736f6c63430008120033" # PASSWORD is the password used to unlock all accounts/wallets/addresses. PASSWORD="abc" diff --git a/server/asset/eth/coiner.go b/server/asset/eth/coiner.go index d6c3814672..e16883bc73 100644 --- a/server/asset/eth/coiner.go +++ b/server/asset/eth/coiner.go @@ -9,6 +9,7 @@ import ( "fmt" "math/big" + "decred.org/dcrdex/dex" dexeth "decred.org/dcrdex/dex/networks/eth" "decred.org/dcrdex/server/asset" "github.com/ethereum/go-ethereum" @@ -20,11 +21,11 @@ var _ asset.Coin = (*redeemCoin)(nil) type baseCoin struct { backend *AssetBackend - vector *dexeth.SwapVector + locator []byte gasFeeCap *big.Int gasTipCap *big.Int txHash common.Hash - value *big.Int + value uint64 txData []byte serializedTx []byte contractVer uint32 @@ -38,6 +39,7 @@ type swapCoin struct { type redeemCoin struct { *baseCoin secret [32]byte + // vector *dexeth.SwapVector } // newSwapCoin creates a new swapCoin that stores and retrieves info about a @@ -54,29 +56,65 @@ func (be *AssetBackend) newSwapCoin(coinID []byte, contractData []byte) (*swapCo return nil, err } - vectors, err := dexeth.ParseInitiateDataV1(bc.txData) - if err != nil { - return nil, fmt.Errorf("unable to parse initiate call data: %v", err) - } + var sum uint64 + var vector *dexeth.SwapVector + switch bc.contractVer { + case 0: + var secretHash [32]byte + copy(secretHash[:], bc.locator) - vec, ok := vectors[bc.vector.SecretHash] - if !ok { - return nil, fmt.Errorf("tx %v does not contain initiation with locator %x", bc.txHash, bc.vector.Locator()) - } + inits, err := dexeth.ParseInitiateDataV0(bc.txData) + if err != nil { + return nil, fmt.Errorf("unable to parse v0 initiate call data: %v", err) + } - if be.assetID == be.baseChainID { - var sum uint64 - for _, in := range vectors { - sum += in.Value + init, ok := inits[secretHash] + if !ok { + return nil, fmt.Errorf("tx %v does not contain v0 initiation with secret hash %x", bc.txHash, secretHash[:]) + } + for _, in := range inits { + sum = +be.atomize(in.Value) + } + + vector = &dexeth.SwapVector{ + // From: , + To: init.Participant, + Value: init.Value, + SecretHash: secretHash, + LockTime: uint64(init.LockTime.Unix()), + } + case 1: + contractVector, err := dexeth.ParseV1Locator(bc.locator) + if err != nil { + return nil, fmt.Errorf("contract data vector decoding error: %w", err) + } + txVectors, err := dexeth.ParseInitiateDataV1(bc.txData) + if err != nil { + return nil, fmt.Errorf("unable to parse v1 initiate call data: %v", err) + } + txVector, ok := txVectors[contractVector.SecretHash] + if !ok { + return nil, fmt.Errorf("tx %v does not contain v1 initiation with vector %s", bc.txHash, contractVector) + } + if !dexeth.CompareVectors(contractVector, txVector) { + return nil, fmt.Errorf("contract and transaction vectors do not match. %+v != %+v", contractVector, txVector) } - if sum != dexeth.WeiToGwei(bc.value) { - return nil, fmt.Errorf("tx %s value < sum of inits. %d < %d", bc.txHash, bc.value, sum) + vector = txVector + if vector.SecretHash == refundRecordHash { + return nil, errors.New("illegal secret hash (refund record hash)") } + for _, v := range txVectors { + sum += be.atomize(v.Value) + } + } + + if be.assetID == BipID && bc.value < sum { + return nil, fmt.Errorf("tx %s value < sum of inits. %d < %d", bc.txHash, bc.value, sum) } return &swapCoin{ baseCoin: bc, - vector: vec, + vector: vector, }, nil } @@ -90,51 +128,81 @@ func (be *AssetBackend) newRedeemCoin(coinID []byte, contractData []byte) (*rede if err == asset.CoinNotFoundError { // If the coin is not found, check to see if the swap has been // redeemed by another transaction. - contractVer, locator, err := dexeth.DecodeLocator(contractData) + contractVer, locator, err := dexeth.DecodeContractData(contractData) if err != nil { return nil, err } - vector, err := dexeth.ParseV1Locator(locator) - if err != nil { - return nil, err + if be.contractVer != contractVer { + return nil, fmt.Errorf("wrong contract version for %s. wanted %d, got %d", dex.BipIDSymbol(be.assetID), be.contractVer, contractVer) } - be.log.Warnf("redeem coin with ID %x for locator %x was not found", coinID, locator) - status, err := be.node.status(be.ctx, be.assetID, vector) + + status, vec, err := be.node.statusAndVector(be.ctx, be.assetID, locator) if err != nil { return nil, err } + be.log.Warnf("redeem coin with ID %x for %s was not found", coinID, vec) if status.Step != dexeth.SSRedeemed { return nil, asset.CoinNotFoundError } + bc = &baseCoin{ backend: be, - vector: vector, + locator: locator, contractVer: contractVer, } return &redeemCoin{ baseCoin: bc, secret: status.Secret, + // vector: vec, }, nil } else if err != nil { return nil, err } - if bc.value.Cmp(new(big.Int)) != 0 { + if bc.value != 0 { return nil, fmt.Errorf("expected tx value of zero for redeem but got: %d", bc.value) } - redemptions, err := dexeth.ParseRedeemDataV1(bc.txData) - if err != nil { - return nil, fmt.Errorf("unable to parse redemption call data: %v", err) - } - redemption, ok := redemptions[bc.vector.SecretHash] - if !ok { - return nil, fmt.Errorf("tx %v does not contain redemption with locator %x", bc.txHash, bc.vector) + var secret [32]byte + switch bc.contractVer { + case 0: + secretHash, err := dexeth.ParseV0Locator(bc.locator) + if err != nil { + return nil, fmt.Errorf("error parsing vector from v0 locator '%x': %w", bc.locator, err) + } + redemptions, err := dexeth.ParseRedeemDataV0(bc.txData) + if err != nil { + return nil, fmt.Errorf("unable to parse v0 redemption call data: %v", err) + } + redemption, ok := redemptions[secretHash] + if !ok { + return nil, fmt.Errorf("tx %v does not contain redemption for v0 secret hash %x", bc.txHash, secretHash[:]) + } + secret = redemption.Secret + case 1: + vector, err := dexeth.ParseV1Locator(bc.locator) + if err != nil { + return nil, fmt.Errorf("error parsing vector from v1 locator '%x': %w", bc.locator, err) + } + redemptions, err := dexeth.ParseRedeemDataV1(bc.txData) + if err != nil { + return nil, fmt.Errorf("unable to parse v1 redemption call data: %v", err) + } + redemption, ok := redemptions[vector.SecretHash] + if !ok { + return nil, fmt.Errorf("tx %v does not contain redemption for v1 vector %s", bc.txHash, vector) + } + if !dexeth.CompareVectors(redemption.Contract, vector) { + return nil, fmt.Errorf("encoded vector %q doesn't match expected vector %q", redemption.Contract, vector) + } + secret = redemption.Secret + default: + return nil, fmt.Errorf("version %d redeem coin not supported", bc.contractVer) } return &redeemCoin{ baseCoin: bc, - secret: redemption.Secret, + secret: secret, }, nil } @@ -151,26 +219,20 @@ func (be *AssetBackend) baseCoin(coinID []byte, contractData []byte) (*baseCoin, } return nil, fmt.Errorf("unable to fetch transaction: %v", err) } - contractAddr := tx.To() - if *contractAddr != be.contractAddr { - return nil, fmt.Errorf("contract address is not supported: %v", contractAddr) - } serializedTx, err := tx.MarshalBinary() if err != nil { return nil, err } - contractVer, locator, err := dexeth.DecodeLocator(contractData) + contractVer, locator, err := dexeth.DecodeContractData(contractData) if err != nil { return nil, err } - if contractVer != ethContractVersion { - return nil, fmt.Errorf("contract version %d not supported, only %d", contractVer, ethContractVersion) - } - vector, err := dexeth.ParseV1Locator(locator) - if err != nil { - return nil, err + + contractAddr := tx.To() + if *contractAddr != be.contractAddr { + return nil, fmt.Errorf("contract address is not supported: %v", contractAddr) } // Gas price is not stored in the swap, and is used to determine if the @@ -186,21 +248,21 @@ func (be *AssetBackend) baseCoin(coinID []byte, contractData []byte) (*baseCoin, zero := new(big.Int) gasFeeCap := tx.GasFeeCap() if gasFeeCap == nil || gasFeeCap.Cmp(zero) <= 0 { - return nil, fmt.Errorf("Failed to parse gas fee cap from tx %s", txHash) + return nil, fmt.Errorf("failed to parse gas fee cap from tx %s", txHash) } gasTipCap := tx.GasTipCap() if gasTipCap == nil || gasTipCap.Cmp(zero) <= 0 { - return nil, fmt.Errorf("Failed to parse gas tip cap from tx %s", txHash) + return nil, fmt.Errorf("failed to parse gas tip cap from tx %s", txHash) } return &baseCoin{ backend: be, - vector: vector, + locator: locator, gasFeeCap: gasFeeCap, gasTipCap: gasTipCap, txHash: txHash, - value: tx.Value(), + value: be.atomize(tx.Value()), txData: tx.Data(), serializedTx: serializedTx, contractVer: contractVer, @@ -217,7 +279,7 @@ func (be *AssetBackend) baseCoin(coinID []byte, contractData []byte) (*baseCoin, // and the same account and nonce, effectively voiding the transaction we // expected to be mined. func (c *swapCoin) Confirmations(ctx context.Context) (int64, error) { - status, err := c.backend.node.status(ctx, c.backend.assetID, c.vector) + status, checkVec, err := c.backend.node.statusAndVector(ctx, c.backend.assetID, c.locator) if err != nil { return -1, err } @@ -230,39 +292,90 @@ func (c *swapCoin) Confirmations(ctx context.Context) (int64, error) { // Assume the tx still has a chance of being mined. return 0, nil } + // Any other swap state is ok. We are sure that initialization + // happened. + + // The swap initiation transaction has some number of + // confirmations, and we are sure the secret hash belongs to + // this swap. Assert that the value, receiver, and locktime are + // as expected. + switch c.contractVer { + case 0: + if checkVec.Value.Cmp(c.vector.Value) != 0 { + return -1, fmt.Errorf("tx data swap val (%d) does not match contract value (%d)", + c.vector.Value, checkVec.Value) + } + if checkVec.To != c.vector.To { + return -1, fmt.Errorf("tx data participant %q does not match contract value %q", + c.vector.To, checkVec.To) + } + if checkVec.LockTime != c.vector.LockTime { + return -1, fmt.Errorf("expected swap locktime (%d) does not match expected (%d)", + c.vector.LockTime, checkVec.LockTime) + } + case 1: + if err := setV1StatusBlockHeight(ctx, c.backend.node, status, c.baseCoin); err != nil { + return 0, err + } + } + bn, err := c.backend.node.blockNumber(ctx) if err != nil { return 0, fmt.Errorf("unable to fetch block number: %v", err) } - if status.Step == dexeth.SSInitiated { - return int64(bn - status.BlockHeight + 1), nil + return int64(bn - status.BlockHeight + 1), nil +} + +func setV1StatusBlockHeight(ctx context.Context, node ethFetcher, status *dexeth.SwapStatus, bc *baseCoin) error { + switch status.Step { + case dexeth.SSNone, dexeth.SSInitiated: + case dexeth.SSRedeemed, dexeth.SSRefunded: + // No block height for redeemed or refunded version 1 contracts, + // only SSInitiated. + r, err := node.transactionReceipt(ctx, bc.txHash) + if err != nil { + return err + } + status.BlockHeight = r.BlockNumber.Uint64() } - // Redeemed or refunded. Have to check the tx. - return c.backend.txConfirmations(ctx, c.txHash) + return nil } func (c *redeemCoin) Confirmations(ctx context.Context) (int64, error) { - status, err := c.backend.node.status(ctx, c.backend.assetID, c.vector) + status, err := c.backend.node.status(ctx, c.backend.assetID, c.locator) if err != nil { return -1, err } - // If swap is in None state, then the redemption can't possibly - // succeed as the swap must already be in the Initialized state - // to redeem. If the swap is in the Refunded state, then the - // redemption either failed or never happened. - if status.Step == dexeth.SSNone || status.Step == dexeth.SSRefunded { - return -1, fmt.Errorf("redemption in failed state with swap at %s state", status.Step) + // There should be no need to check the counter party, or value + // as a swap with a specific secret hash that has been redeemed + // wouldn't have been redeemed without ensuring the initiator + // is the expected address and value was also as expected. Also + // not validating the locktime, as the swap is redeemed and + // locktime no longer relevant. + if status.Step == dexeth.SSRedeemed { + if c.contractVer == 1 { + if err := setV1StatusBlockHeight(ctx, c.backend.node, status, c.baseCoin); err != nil { + return -1, err + } + } + bn, err := c.backend.node.blockNumber(ctx) + if err != nil { + return 0, fmt.Errorf("unable to fetch block number: %v", err) + } + return int64(bn - status.BlockHeight + 1), nil } - // If swap is in the Initiated state, the redemption may be // unmined. if status.Step == dexeth.SSInitiated { // Assume the tx still has a chance of being mined. return 0, nil } - - return c.backend.txConfirmations(ctx, c.txHash) + // If swap is in None state, then the redemption can't possibly + // succeed as the swap must already be in the Initialized state + // to redeem. If the swap is in the Refunded state, then the + // redemption either failed or never happened. + return -1, fmt.Errorf("redemption in failed state with swap at %s state", status.Step) } func (c *redeemCoin) Value() uint64 { return 0 } @@ -290,5 +403,5 @@ func (c *baseCoin) FeeRate() uint64 { // Value returns the value of one swap in order to validate during processing. func (c *swapCoin) Value() uint64 { - return c.vector.Value + return c.backend.atomize(c.vector.Value) } diff --git a/server/asset/eth/coiner_test.go b/server/asset/eth/coiner_test.go index 95d5a18ef8..1b658b989a 100644 --- a/server/asset/eth/coiner_test.go +++ b/server/asset/eth/coiner_test.go @@ -5,8 +5,11 @@ package eth import ( + "bytes" "context" + "encoding/hex" "errors" + "fmt" "math/big" "testing" @@ -15,6 +18,8 @@ import ( "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" + // swapv0 "decred.org/dcrdex/dex/networks/eth/contracts/v0" + // swapv1 "decred.org/dcrdex/dex/networks/eth/contracts/v1" ) func randomAddress() *common.Address { @@ -25,11 +30,9 @@ func randomAddress() *common.Address { func TestNewRedeemCoin(t *testing.T) { contractAddr := randomAddress() + secret, secretHash, locator := secretB, secretHashB, locatorB var txHash [32]byte copy(txHash[:], encode.RandomBytes(32)) - contract := dexeth.EncodeContractData(1, vector2.Locator()) - vector3 := *vector2 - contract3 := dexeth.EncodeContractData(1, vector3.Locator()) const gasPrice = 30 const gasTipCap = 2 const value = 5e9 @@ -37,54 +40,66 @@ func TestNewRedeemCoin(t *testing.T) { const wantGasTipCap = 2 tests := []struct { name string - contract []byte + ver uint32 + locator []byte tx *types.Transaction swpErr, txErr error swap *dexeth.SwapState wantErr bool - }{{ - name: "ok redeem", - tx: tTx(gasPrice, gasTipCap, 0, contractAddr, redeemCalldata), - contract: contract, - }, { - name: "non zero value with redeem", - tx: tTx(gasPrice, gasTipCap, value, contractAddr, redeemCalldata), - contract: contract, - wantErr: true, - }, { - name: "unable to decode redeem data, must be redeem for redeem coin type", - tx: tTx(gasPrice, gasTipCap, 0, contractAddr, initCalldata), - contract: contract, - wantErr: true, - }, { - name: "tx coin id for redeem - contract not in tx", - tx: tTx(gasPrice, gasTipCap, value, contractAddr, redeemCalldata), - contract: contract3, - wantErr: true, - }, { - name: "tx not found, redeemed", - txErr: ethereum.NotFound, - swap: tSwap(97, initLocktime, 1000, tRedeem2.Secret, dexeth.SSRedeemed, &initParticipantAddr), - contract: contract, - }, { - name: "tx not found, not redeemed", - txErr: ethereum.NotFound, - swap: tSwap(97, initLocktime, 1000, tRedeem2.Secret, dexeth.SSInitiated, &initParticipantAddr), - contract: contract, - wantErr: true, - }, { - name: "tx not found, swap err", - txErr: ethereum.NotFound, - swpErr: errors.New("swap not found"), - contract: contract, - wantErr: true, - }} + }{ + { + name: "ok redeem v0", + tx: tTx(gasPrice, gasTipCap, 0, contractAddr, redeemCalldataV0), + swap: tSwap(97, initLocktime, 1000, secret, dexeth.SSRedeemed, &participantAddr), + locator: secretHash[:], + }, { + name: "ok redeem v1", + ver: 1, + tx: tTx(gasPrice, gasTipCap, 0, contractAddr, redeemCalldataV1), + swap: tSwap(97, initLocktime, 1000, secret, dexeth.SSRedeemed, &participantAddr), + locator: locator, + }, { + name: "non zero value with redeem", + tx: tTx(gasPrice, gasTipCap, value, contractAddr, redeemCalldataV0), + locator: secretHash[:], + wantErr: true, + }, { + name: "unable to decode redeem data, must be redeem for redeem coin type", + tx: tTx(gasPrice, gasTipCap, 0, contractAddr, initCalldataV0), + locator: secretHash[:], + wantErr: true, + }, { + name: "tx coin id for redeem - contract not in tx", + tx: tTx(gasPrice, gasTipCap, value, contractAddr, redeemCalldataV0), + locator: encode.RandomBytes(32), + wantErr: true, + }, { + name: "tx not found, redeemed", + txErr: ethereum.NotFound, + swap: tSwap(97, initLocktime, 1000, secret, dexeth.SSRedeemed, &participantAddr), + locator: secretHash[:], + }, { + name: "tx not found, not redeemed", + txErr: ethereum.NotFound, + swap: tSwap(97, initLocktime, 1000, secret, dexeth.SSInitiated, &participantAddr), + locator: secretHash[:], + wantErr: true, + }, { + name: "tx not found, swap err", + txErr: ethereum.NotFound, + swpErr: errors.New("swap not found"), + locator: secretHash[:], + wantErr: true, + }, + } for _, test := range tests { node := &testNode{ tx: test.tx, txErr: test.txErr, - swp: test.swap, swpErr: test.swpErr, + swp: map[string]*dexeth.SwapState{ + string(secretHash[:]): test.swap, + }, } eth := &AssetBackend{ baseBackend: &baseBackend{ @@ -95,8 +110,9 @@ func TestNewRedeemCoin(t *testing.T) { initTxSize: uint32(dexeth.InitGas(1, 0)), assetID: BipID, log: tLogger, + atomize: dexeth.WeiToGwei, } - rc, err := eth.newRedeemCoin(txHash[:], test.contract) + rc, err := eth.newRedeemCoin(txHash[:], dexeth.EncodeContractData(test.ver, test.locator)) if test.wantErr { if err == nil { t.Fatalf("expected error for test %q", test.name) @@ -107,9 +123,11 @@ func TestNewRedeemCoin(t *testing.T) { t.Fatalf("unexpected error for test %q: %v", test.name, err) } - if test.txErr == nil && (rc.vector.SecretHash != tRedeem2.V.SecretHash || - rc.secret != tRedeem2.Secret || - rc.value.Uint64() != 0 || + fmt.Println("--", rc.gasFeeCap == nil, rc.gasTipCap == nil) + + if test.txErr == nil && (!bytes.Equal(rc.locator, test.locator) || + rc.secret != secretB || + rc.value != 0 || dexeth.WeiToGwei(rc.gasFeeCap) != wantGas || dexeth.WeiToGwei(rc.gasTipCap) != wantGasTipCap) { t.Fatalf("returns do not match expected for test %q / %v", test.name, rc) @@ -130,90 +148,99 @@ func TestNewSwapCoin(t *testing.T) { if err != nil { t.Fatal(err) } - wantVal, err := dexeth.WeiToGweiSafe(big.NewInt(5e18)) - if err != nil { - t.Fatal(err) - } + wantVal := dexeth.GweiToWei(initValue) wantGasTipCap, err := dexeth.WeiToGweiSafe(big.NewInt(2e9)) if err != nil { t.Fatal(err) } - goodContract := dexeth.EncodeContractData(1, vector2.Locator()) - vector3 := *vector2 - vector3.SecretHash = [32]byte{3} - badContract := dexeth.EncodeContractData(1, vector3.Locator()) + tests := []struct { - name string - coinID []byte - contract []byte - tx *types.Transaction - swpErr, txErr error - wantErr bool - }{{ - name: "ok init", - tx: tTx(gasPrice, gasTipCap, value, contractAddr, initCalldata), - coinID: txCoinIDBytes, - contract: goodContract, - }, { - name: "contract incorrect length", - tx: tTx(gasPrice, gasTipCap, value, contractAddr, initCalldata), - coinID: txCoinIDBytes, - contract: goodContract[:len(goodContract)-1], - wantErr: true, - }, { - name: "tx has no data", - tx: tTx(gasPrice, gasTipCap, value, contractAddr, nil), - coinID: txCoinIDBytes, - contract: goodContract, - wantErr: true, - }, { - name: "unable to decode init data, must be init for init coin type", - tx: tTx(gasPrice, gasTipCap, value, contractAddr, redeemCalldata), - coinID: txCoinIDBytes, - contract: goodContract, - wantErr: true, - }, { - name: "unable to decode CoinID", - tx: tTx(gasPrice, gasTipCap, value, contractAddr, initCalldata), - contract: goodContract, - wantErr: true, - }, { - name: "invalid coinID", - tx: tTx(gasPrice, gasTipCap, value, contractAddr, initCalldata), - coinID: badCoinIDBytes, - contract: goodContract, - wantErr: true, - }, { - name: "transaction error", - tx: tTx(gasPrice, gasTipCap, value, contractAddr, initCalldata), - coinID: txCoinIDBytes, - contract: goodContract, - txErr: errors.New(""), - wantErr: true, - }, { - name: "transaction not found error", - tx: tTx(gasPrice, gasTipCap, value, contractAddr, initCalldata), - coinID: txCoinIDBytes, - contract: goodContract, - txErr: ethereum.NotFound, - wantErr: true, - }, { - name: "wrong contract", - tx: tTx(gasPrice, gasTipCap, value, randomAddr, initCalldata), - coinID: txCoinIDBytes, - contract: badContract, - wantErr: true, - // }, { TODO: This test was doing nothing, I think. - // name: "tx coin id for swap - contract not in tx", - // tx: tTx(gasPrice, gasTipCap, value, contractAddr, initCalldata), - // coinID: txCoinIDBytes, - // contract: encode.RandomBytes(32), - // wantErr: true, - }} + name string + ver uint32 + coinID []byte + locator []byte + tx *types.Transaction + swap *dexeth.SwapState + txErr error + wantErr bool + }{ + { + name: "ok init v0", + tx: tTx(gasPrice, gasTipCap, value, contractAddr, initCalldataV0), + coinID: txCoinIDBytes, + locator: secretHashA[:], + swap: tSwap(97, initLocktime, initValue, secretA, dexeth.SSRedeemed, &participantAddr), + }, { + name: "ok init v1", + ver: 1, + tx: tTx(gasPrice, gasTipCap, value, contractAddr, initCalldataV1), + coinID: txCoinIDBytes, + locator: locatorA, + swap: tSwap(97, initLocktime, initValue, secretA, dexeth.SSRedeemed, &participantAddr), + }, { + name: "contract incorrect length", + tx: tTx(gasPrice, gasTipCap, value, contractAddr, initCalldataV0), + coinID: txCoinIDBytes, + locator: secretHashA[:31], + wantErr: true, + }, { + name: "tx has no data", + tx: tTx(gasPrice, gasTipCap, value, contractAddr, nil), + coinID: txCoinIDBytes, + locator: secretHashA[:], + wantErr: true, + }, { + name: "unable to decode init data, must be init for init coin type", + tx: tTx(gasPrice, gasTipCap, value, contractAddr, redeemCalldataV0), + coinID: txCoinIDBytes, + locator: secretHashA[:], + wantErr: true, + }, { + name: "unable to decode CoinID", + tx: tTx(gasPrice, gasTipCap, value, contractAddr, initCalldataV0), + locator: secretHashA[:], + wantErr: true, + }, { + name: "invalid coinID", + tx: tTx(gasPrice, gasTipCap, value, contractAddr, initCalldataV0), + coinID: badCoinIDBytes, + locator: secretHashA[:], + wantErr: true, + }, { + name: "transaction error", + tx: tTx(gasPrice, gasTipCap, value, contractAddr, initCalldataV0), + coinID: txCoinIDBytes, + locator: secretHashA[:], + txErr: errors.New(""), + wantErr: true, + }, { + name: "transaction not found error", + tx: tTx(gasPrice, gasTipCap, value, contractAddr, initCalldataV0), + coinID: txCoinIDBytes, + locator: secretHashA[:], + txErr: ethereum.NotFound, + wantErr: true, + }, { + name: "wrong contract", + tx: tTx(gasPrice, gasTipCap, value, randomAddr, initCalldataV0), + coinID: txCoinIDBytes, + locator: secretHashA[:], + wantErr: true, + }, { + name: "tx coin id for swap - contract not in tx", + tx: tTx(gasPrice, gasTipCap, value, contractAddr, initCalldataV0), + coinID: txCoinIDBytes, + locator: encode.RandomBytes(32), + wantErr: true, + }, + } for _, test := range tests { node := &testNode{ tx: test.tx, txErr: test.txErr, + swp: map[string]*dexeth.SwapState{ + string(secretHashA[:]): test.swap, + }, } eth := &AssetBackend{ baseBackend: &baseBackend{ @@ -224,7 +251,7 @@ func TestNewSwapCoin(t *testing.T) { initTxSize: uint32(dexeth.InitGas(1, 0)), atomize: dexeth.WeiToGwei, } - sc, err := eth.newSwapCoin(test.coinID, test.contract) + sc, err := eth.newSwapCoin(test.coinID, dexeth.EncodeContractData(test.ver, test.locator)) if test.wantErr { if err == nil { t.Fatalf("expected error for test %q", test.name) @@ -235,12 +262,21 @@ func TestNewSwapCoin(t *testing.T) { t.Fatalf("unexpected error for test %q: %v", test.name, err) } - if sc.vector.To != tSwap2.Participant || - sc.vector.SecretHash != tRedeem2.V.SecretHash || - dexeth.WeiToGwei(sc.value) != wantVal || + if sc.vector.To != participantAddr || + sc.vector.SecretHash != secretHashA || + sc.vector.Value.Cmp(wantVal) != 0 || dexeth.WeiToGwei(sc.gasFeeCap) != wantGas || dexeth.WeiToGwei(sc.gasTipCap) != wantGasTipCap || sc.vector.LockTime != initLocktime { + + fmt.Println("to:", sc.vector.To, participantAddr) + fmt.Println("secret hash:", hex.EncodeToString(sc.vector.SecretHash[:]), hex.EncodeToString(secretHashA[:])) + fmt.Println("value:", sc.value, value) + fmt.Println("swap value:", sc.vector.Value, wantVal) + fmt.Println("gas fee cap:", sc.gasFeeCap, wantGas) + fmt.Println("gas tip cap:", sc.gasTipCap, wantGasTipCap) + fmt.Println("lock time:", sc.vector.LockTime, initLocktime) + t.Fatalf("returns do not match expected for test %q / %v", test.name, sc) } } @@ -254,68 +290,113 @@ type Confirmer interface { func TestConfirmations(t *testing.T) { contractAddr, nullAddr := new(common.Address), new(common.Address) copy(contractAddr[:], encode.RandomBytes(20)) - txHash := bytesToArray(encode.RandomBytes(32)) - secret := tRedeem2.Secret + secret, secretHash := secretB, secretHashB + var txHash [32]byte + copy(txHash[:], encode.RandomBytes(32)) const gasPrice = 30 const gasTipCap = 2 - swapVal := tSwap2.Value - txVal := tSwap1.Value + tSwap2.Value - const blockNumber = 100 + txVal := initValue * 2 + oneGweiMore := initValue + 1 tests := []struct { name string + ver uint32 swap *dexeth.SwapState + receipt *types.Receipt // v1 only bn uint64 value uint64 wantConfs int64 swapErr, bnErr error wantErr, redeem bool - }{{ - name: "ok has confs value not verified", - bn: 100, - swap: tSwap(97, initLocktime, swapVal, secret, dexeth.SSInitiated, &initParticipantAddr), - value: txVal, - wantConfs: 4, - }, { - name: "ok no confs", - swap: tSwap(0, 0, 0, secret, dexeth.SSNone, nullAddr), - value: txVal, - }, { - name: "ok redeem swap status redeemed", - bn: 97, - swap: tSwap(97, initLocktime, swapVal, secret, dexeth.SSRedeemed, &initParticipantAddr), - value: 0, - wantConfs: 4, - redeem: true, - }, { - name: "ok redeem swap status initiated", - swap: tSwap(blockNumber, initLocktime, swapVal, secret, dexeth.SSInitiated, &initParticipantAddr), - value: 0, - redeem: true, - wantConfs: 0, // SSInitiated is always zero confs for redeems. - }, { - name: "redeem bad swap state None", - swap: tSwap(0, 0, 0, secret, dexeth.SSNone, nullAddr), - value: 0, - wantErr: true, - redeem: true, - }, { - name: "error getting swap", - swapErr: errors.New(""), - value: txVal, - wantErr: true, - }, { - name: "block number error", - swap: tSwap(97, initLocktime, swapVal, secret, dexeth.SSInitiated, &initParticipantAddr), - value: txVal, - bnErr: errors.New(""), - wantErr: true, - }} + }{ + { + name: "ok has confs value not verified v0", + bn: 100, + swap: tSwap(97, initLocktime, initValue, secret, dexeth.SSInitiated, &participantAddr), + value: txVal, + wantConfs: 4, + }, { + name: "ok has confs value not verified v1", + ver: 1, + bn: 100, + swap: tSwap(97, initLocktime, initValue, secret, dexeth.SSInitiated, &participantAddr), + value: txVal, + wantConfs: 4, + }, { + name: "ok no confs", + swap: tSwap(0, 0, 0, secret, dexeth.SSNone, nullAddr), + value: txVal, + }, { + name: "ok 1 conf v1", + ver: 1, + bn: 97, + receipt: &types.Receipt{ + BlockNumber: big.NewInt(97), + }, + swap: tSwap(0, 0, 0, secret, dexeth.SSRedeemed, nullAddr), + value: txVal, + wantConfs: 1, + }, { + name: "ok redeem swap status redeemed", + bn: 97, + swap: tSwap(97, initLocktime, initValue, secret, dexeth.SSRedeemed, &participantAddr), + value: 0, + wantConfs: 1, + redeem: true, + }, { + name: "ok redeem swap status initiated", + swap: tSwap(97, initLocktime, initValue, secret, dexeth.SSInitiated, &participantAddr), + value: 0, + redeem: true, + }, { + name: "redeem bad swap state None", + swap: tSwap(0, 0, 0, secret, dexeth.SSNone, nullAddr), + value: 0, + wantErr: true, + redeem: true, + }, { + name: "error getting swap", + swapErr: errors.New("test error"), + value: txVal, + wantErr: true, + }, { + name: "value differs from initial transaction", + swap: tSwap(99, initLocktime, oneGweiMore, secret, dexeth.SSInitiated, &participantAddr), + value: txVal, + wantErr: true, + }, { + name: "participant differs from initial transaction", + swap: tSwap(99, initLocktime, initValue, secret, dexeth.SSInitiated, nullAddr), + value: txVal, + wantErr: true, + // }, { + // name: "locktime not an int64", + // swap: tSwap(99, new(big.Int).SetUint64(^uint64(0)), value, secret, dexeth.SSInitiated, &participantAddr), + // value: value, + // ct: sctInit, + // wantErr: true, + }, { + name: "locktime differs from initial transaction", + swap: tSwap(99, 0, initValue, secret, dexeth.SSInitiated, &participantAddr), + value: txVal, + wantErr: true, + }, { + name: "block number error", + swap: tSwap(97, initLocktime, initValue, secret, dexeth.SSInitiated, &participantAddr), + value: txVal, + bnErr: errors.New("test error"), + wantErr: true, + }, + } for _, test := range tests { node := &testNode{ - swp: test.swap, swpErr: test.swapErr, - blkNum: blockNumber, + blkNum: test.bn, blkNumErr: test.bnErr, + swp: map[string]*dexeth.SwapState{ + string(secretHash[:]): test.swap, + string(locatorA): test.swap, + }, + receipt: test.receipt, } eth := &AssetBackend{ baseBackend: &baseBackend{ @@ -327,11 +408,15 @@ func TestConfirmations(t *testing.T) { atomize: dexeth.WeiToGwei, } - swapData := dexeth.EncodeContractData(1, vector2.Locator()) - - if test.swap != nil { - node.rcpt = &types.Receipt{BlockNumber: big.NewInt(int64(test.swap.BlockHeight))} + locator := secretHash[:] + redeemCalldata := redeemCalldataV0 + initCalldata := initCalldataV0 + if test.ver == 1 { + redeemCalldata = redeemCalldataV1 + initCalldata = initCalldataV1 + locator = locatorA } + swapData := dexeth.EncodeContractData(test.ver, locator) var confirmer Confirmer var err error @@ -348,7 +433,7 @@ func TestConfirmations(t *testing.T) { _ = confirmer.String() // unrelated panic test - confs, err := confirmer.Confirmations(nil) + confs, err := confirmer.Confirmations(context.TODO()) if test.wantErr { if err == nil { t.Fatalf("expected error for test %q", test.name) @@ -356,7 +441,7 @@ func TestConfirmations(t *testing.T) { continue } if err != nil { - t.Fatalf("unexpected error for test %q: %v", test.name, err) + t.Fatalf("unexpected error for test %q (redeem = %t): %v", test.name, test.redeem, err) } if confs != test.wantConfs { t.Fatalf("want %d but got %d confs for test: %v", test.wantConfs, confs, test.name) @@ -365,29 +450,67 @@ func TestConfirmations(t *testing.T) { } // func TestGeneratePackedInits(t *testing.T) { -// hexToHash := func(s string) (h [32]byte) { -// b, _ := hex.DecodeString(s) -// copy(h[:], b) -// return +// initsV0 := []swapv0.ETHSwapInitiation{ +// { +// RefundTimestamp: big.NewInt(int64(swapVectorA.LockTime)), +// SecretHash: swapVectorA.SecretHash, +// Value: swapVectorA.Value, +// Participant: swapVectorA.To, +// }, +// { +// RefundTimestamp: big.NewInt(int64(swapVectorB.LockTime)), +// SecretHash: swapVectorB.SecretHash, +// Value: swapVectorB.Value, +// Participant: swapVectorB.To, +// }, +// } +// dataV0, err := dexeth.ABIs[0].Pack("initiate", initsV0) +// if err != nil { +// t.Fatalf("Pack error: %v", err) +// } +// fmt.Println("V0 Init Data", hex.EncodeToString(dataV0)) + +// init0 := dexeth.SwapVectorToAbigen(swapVectorA) +// init1 := dexeth.SwapVectorToAbigen(swapVectorB) +// inits := []swapv1.ETHSwapVector{init0, init1} + +// dataV1, err := dexeth.ABIs[1].Pack("initiate", inits) +// if err != nil { +// t.Fatalf("Pack error: %v", err) // } -// inits := []swapv0.ETHSwapInitiation{ +// fmt.Println("V1 Init Data:", hex.EncodeToString(dataV1)) + +// redeemsV0 := []swapv0.ETHSwapRedemption{ +// { +// SecretHash: secretHashA, +// Secret: secretA, +// }, +// { +// SecretHash: secretHashB, +// Secret: secretB, +// }, +// } +// dataV0, err = dexeth.ABIs[0].Pack("redeem", redeemsV0) +// if err != nil { +// t.Fatalf("Pack error: %v", err) +// } +// fmt.Println("V0 Redeem Data", hex.EncodeToString(dataV0)) + +// redeemsV1 := []swapv1.ETHSwapRedemption{ // { -// RefundTimestamp: big.NewInt(1632112916), -// SecretHash: hexToHash("8b3e4acc53b664f9cf6fcac0adcd328e95d62ba1f4379650ae3e1460a0f9d1a1"), -// Value: dexeth.GweiToWei(25e8), -// Participant: common.HexToAddress("0x345853e21b1d475582e71cc269124ed5e2dd3422"), +// V: init0, +// Secret: secretA, // }, // { -// RefundTimestamp: big.NewInt(1632112916), -// SecretHash: hexToHash("ebdc4c31b88d0c8f4d644591a8e00e92b607f920ad8050deb7c7469767d9c561"), -// Value: dexeth.GweiToWei(25e8), -// Participant: common.HexToAddress("0x345853e21b1d475582e71cc269124ed5e2dd3422"), +// V: init1, +// Secret: secretB, // }, // } -// data, err := dexeth.ABIs[0].Pack("initiate", inits) +// dataV1, err = dexeth.ABIs[1].Pack("redeem", redeemsV1) // if err != nil { // t.Fatalf("Pack error: %v", err) // } +// fmt.Println("V1 Redeem Data", hex.EncodeToString(dataV1)) -// fmt.Printf("tx data: %x \n", data) +// t.Fatal("ok") // } diff --git a/server/asset/eth/eth.go b/server/asset/eth/eth.go index 0f4cb8619f..c298663dc4 100644 --- a/server/asset/eth/eth.go +++ b/server/asset/eth/eth.go @@ -8,6 +8,7 @@ import ( "bytes" "context" "crypto/sha256" + "encoding/json" "errors" "fmt" "math/big" @@ -23,91 +24,127 @@ import ( "decred.org/dcrdex/server/asset" "github.com/decred/dcrd/dcrec/secp256k1/v4" "github.com/decred/dcrd/dcrec/secp256k1/v4/ecdsa" - "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/params" ) -type VersionedToken struct { - *dexeth.Token - Ver uint32 -} +const ( + BipID = 60 +) -var registeredTokens = make(map[uint32]*VersionedToken) +var ( + _ asset.Driver = (*Driver)(nil) + _ asset.TokenBacker = (*ETHBackend)(nil) -func registerToken(assetID uint32, ver uint32) { - token, exists := dexeth.Tokens[assetID] - if !exists { - panic(fmt.Sprintf("no token constructor for asset ID %d", assetID)) + defaultProtocolVersion = dexeth.ProtocolVersionV1Contracts + protocolVersionsFilePath = "evm-protocol-overrides.json" + protocolVersionsOverrides = make(map[uint32]dexeth.ProtocolVersion) + + backendInfo = &asset.BackendInfo{ + SupportsDynamicTxFee: true, } - asset.RegisterToken(assetID, &TokenDriver{ - DriverBase: DriverBase{ - Ver: ver, - UI: token.UnitInfo, - Nam: token.Name, - }, - Token: token.Token, - }) - registeredTokens[assetID] = &VersionedToken{ - Token: token, - Ver: ver, + + refundRecordHash = [32]byte{ + 0xaf, 0x96, 0x13, 0x76, 0x0f, 0x72, 0x63, 0x5f, 0xbd, 0xb4, 0x4a, 0x5a, + 0x0a, 0x63, 0xc3, 0x9f, 0x12, 0xaf, 0x30, 0xf9, 0x50, 0xa6, 0xee, 0x5c, + 0x97, 0x1b, 0xe1, 0x88, 0xe8, 0x9c, 0x40, 0x51, } -} +) func init() { + // Load any legacy/reverted protocol versions. + if b, err := os.ReadFile(protocolVersionsFilePath); err == nil && len(b) > 0 { + var symbolVers map[string]uint32 + if err := json.Unmarshal(b, &symbolVers); err != nil { + panic(fmt.Sprintf("provided protocol override file at %q did not parse: %v", protocolVersionsFilePath, err)) + } + for symbol, v := range symbolVers { + assetID, found := dex.BipSymbolID(symbol) + if !found { + panic(fmt.Sprintf("asset %s specified in protocol override file is not known", symbol)) + } + protocolVersionsOverrides[assetID] = dexeth.ProtocolVersion(v) + } + } + asset.Register(BipID, &Driver{ DriverBase: DriverBase{ - Ver: version, - UI: dexeth.UnitInfo, - Nam: "Ethereum", + ProtocolVersion: ProtocolVersion(BipID), + UI: dexeth.UnitInfo, + Nam: "Ethereum", }, }) - registerToken(usdcID, ethContractVersion) - registerToken(usdtID, ethContractVersion) + registerToken(usdcID, ProtocolVersion(usdcID)) + registerToken(usdtID, ProtocolVersion(usdtID)) } -const ( - BipID = 60 - ethContractVersion = 1 - version = 1 -) +// ProtocolVersion returns the default protocol version unless a reversion is +// specified in the file at protocolVersionsFilePath. +func ProtocolVersion(assetID uint32) dexeth.ProtocolVersion { + v, found := protocolVersionsOverrides[assetID] + if found { + return v + } + return defaultProtocolVersion +} -var ( - _ asset.Driver = (*Driver)(nil) - _ asset.TokenBacker = (*ETHBackend)(nil) +type VersionedToken struct { + *dexeth.Token + ContractVersion uint32 +} - backendInfo = &asset.BackendInfo{ - SupportsDynamicTxFee: true, - } +var ( + registeredTokens = make(map[uint32]*VersionedToken) usdcID, _ = dex.BipSymbolID("usdc.eth") usdtID, _ = dex.BipSymbolID("usdt.eth") ) +func registerToken(assetID uint32, protocolVer dexeth.ProtocolVersion) { + token, exists := dexeth.Tokens[assetID] + if !exists { + panic(fmt.Sprintf("no token constructor for asset ID %d", assetID)) + } + drv := &TokenDriver{ + DriverBase: DriverBase{ + ProtocolVersion: protocolVer, + UI: token.UnitInfo, + Nam: token.Name, + }, + Token: token.Token, + } + asset.RegisterToken(assetID, drv) + registeredTokens[assetID] = &VersionedToken{ + Token: token, + ContractVersion: protocolVer.ContractVersion(), + } +} + func networkToken(vToken *VersionedToken, net dex.Network) (netToken *dexeth.NetToken, contract *dexeth.SwapContract, err error) { netToken, found := vToken.NetTokens[net] if !found { return nil, nil, fmt.Errorf("no addresses for %s on %s", vToken.Name, net) } - contract, found = netToken.SwapContracts[vToken.Ver] + + contract, found = netToken.SwapContracts[vToken.ContractVersion] if !found || contract.Address == (common.Address{}) { - return nil, nil, fmt.Errorf("no version %d address for %s on %s", vToken.Ver, vToken.Name, net) + return nil, nil, fmt.Errorf("no version %d address for %s on %s", vToken.ContractVersion, vToken.Name, net) } return } type DriverBase struct { - UI dex.UnitInfo - Ver uint32 - Nam string + UI dex.UnitInfo + ProtocolVersion dexeth.ProtocolVersion + Nam string } // Version returns the Backend implementation's version number. func (d *DriverBase) Version() uint32 { - return d.Ver + return uint32(d.ProtocolVersion) } // DecodeCoinID creates a human-readable representation of a coin ID for @@ -174,11 +211,13 @@ type ethFetcher interface { connect(ctx context.Context) error suggestGasTipCap(ctx context.Context) (*big.Int, error) transaction(ctx context.Context, hash common.Hash) (tx *types.Transaction, isMempool bool, err error) + transactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) // token- and asset-specific methods loadToken(ctx context.Context, assetID uint32, vToken *VersionedToken) error - status(ctx context.Context, assetID uint32, vector *dexeth.SwapVector) (*dexeth.SwapStatus, error) + status(ctx context.Context, assetID uint32, locator []byte) (*dexeth.SwapStatus, error) + vector(ctx context.Context, assetID uint32, locator []byte) (*dexeth.SwapVector, error) + statusAndVector(ctx context.Context, assetID uint32, locator []byte) (*dexeth.SwapStatus, *dexeth.SwapVector, error) accountBalance(ctx context.Context, assetID uint32, addr common.Address) (*big.Int, error) - receipt(context.Context, common.Hash) (*types.Receipt, error) } type baseBackend struct { @@ -225,6 +264,7 @@ type AssetBackend struct { redeemSize uint64 contractAddr common.Address + contractVer uint32 } // ETHBackend implements some Ethereum-specific methods. @@ -248,14 +288,15 @@ var _ asset.AccountBalancer = (*ETHBackend)(nil) // unconnectedETH returns a Backend without a node. The node should be set // before use. -func unconnectedETH(bipID uint32, contractAddr common.Address, vTokens map[uint32]*VersionedToken, logger dex.Logger, net dex.Network) (*ETHBackend, error) { +func unconnectedETH(bipID uint32, vTokens map[uint32]*VersionedToken, logger dex.Logger, net dex.Network) (*ETHBackend, error) { // TODO: At some point multiple contracts will need to be used, at // least for transitory periods when updating the contract, and // possibly a random contract setup, and so this section will need to // change to support multiple contracts. - contractAddr, exists := dexeth.ContractAddresses[ethContractVersion][net] + contractVer := ProtocolVersion(bipID).ContractVersion() + contractAddr, exists := dexeth.ContractAddresses[contractVer][net] if !exists || contractAddr == (common.Address{}) { - return nil, fmt.Errorf("no eth contract for version %d, net %s", ethContractVersion, net) + return nil, fmt.Errorf("no eth contract for version %d, net %s", contractVer, net) } return ÐBackend{&AssetBackend{ baseBackend: &baseBackend{ @@ -269,10 +310,11 @@ func unconnectedETH(bipID uint32, contractAddr common.Address, vTokens map[uint3 log: logger, contractAddr: contractAddr, blockChans: make(map[chan *asset.BlockUpdate]struct{}), - initTxSize: uint32(dexeth.InitGas(1, ethContractVersion)), - redeemSize: dexeth.RedeemGas(1, ethContractVersion), + initTxSize: uint32(dexeth.InitGas(1, contractVer)), + redeemSize: dexeth.RedeemGas(1, contractVer), assetID: bipID, atomize: dexeth.WeiToGwei, + contractVer: contractVer, }}, nil } @@ -354,17 +396,18 @@ func NewEVMBackend( baseChainID, net, log := cfg.AssetID, cfg.Net, cfg.Logger assetName := strings.ToUpper(dex.BipIDSymbol(baseChainID)) + contractVer := ProtocolVersion(baseChainID).ContractVersion() - netAddrs, found := contractAddrs[ethContractVersion] + netAddrs, found := contractAddrs[contractVer] if !found { - return nil, fmt.Errorf("no contract address for %s version %d", assetName, ethContractVersion) + return nil, fmt.Errorf("no contract address for %s version %d", assetName, contractVer) } contractAddr, found := netAddrs[net] if !found { - return nil, fmt.Errorf("no contract address for %s version %d on %s", assetName, ethContractVersion, net) + return nil, fmt.Errorf("no contract address for %s version %d on %s", assetName, contractVer, net) } - eth, err := unconnectedETH(baseChainID, contractAddr, vTokens, log, net) + eth, err := unconnectedETH(baseChainID, vTokens, log, net) if err != nil { return nil, err } @@ -591,21 +634,17 @@ func (be *AssetBackend) sendBlockUpdate(u *asset.BlockUpdate) { // ValidateContract ensures that contractData encodes both the expected contract // version and a secret hash. func (eth *ETHBackend) ValidateContract(contractData []byte) error { - ver, _, err := dexeth.DecodeLocator(contractData) + _, _, err := dexeth.DecodeContractData(contractData) if err != nil { // ensures secretHash is proper length return err } - - if ver != ethContractVersion { - return fmt.Errorf("incorrect swap contract version %d, wanted %d", ver, ethContractVersion) - } return nil } // ValidateContract ensures that contractData encodes both the expected swap // contract version and a secret hash. func (eth *TokenBackend) ValidateContract(contractData []byte) error { - ver, _, err := dexeth.DecodeLocator(contractData) + contractVer, _, err := dexeth.DecodeContractData(contractData) if err != nil { // ensures secretHash is proper length return err } @@ -613,8 +652,8 @@ func (eth *TokenBackend) ValidateContract(contractData []byte) error { if err != nil { return fmt.Errorf("error locating token: %v", err) } - if ver != eth.VersionedToken.Ver { - return fmt.Errorf("incorrect token swap contract version %d, wanted %d", ver, eth.VersionedToken.Ver) + if contractVer != eth.VersionedToken.ContractVersion { + return fmt.Errorf("incorrect token swap contract version %d, wanted %d", contractVer, eth.VersionedToken.ContractVersion) } return nil @@ -648,22 +687,30 @@ func (be *AssetBackend) Contract(coinID, contractData []byte) (*asset.Contract, } // ValidateSecret checks that the secret satisfies the secret hash. -func (eth *baseBackend) ValidateSecret(secret, contractData []byte) bool { - contractVer, locator, err := dexeth.DecodeLocator(contractData) +func (eth *AssetBackend) ValidateSecret(secret, contractData []byte) bool { + contractVer, locator, err := dexeth.DecodeContractData(contractData) if err != nil { - eth.baseLogger.Errorf("unable to decode contract data: %w", err) + eth.log.Errorf("Error decoding contract data for validation: %v", err) return false } - if contractVer != ethContractVersion { - return false - } - vec, err := dexeth.ParseV1Locator(locator) - if err != nil { - eth.baseLogger.Errorf("unable to parse v1 locator: %w", err) + var secretHash [32]byte + switch contractVer { + case 0: + copy(secretHash[:], locator) + case 1: + v, err := dexeth.ParseV1Locator(locator) + if err != nil { + eth.log.Errorf("ValidateSecret locator parsing error: %v", err) + return false + } + secretHash = v.SecretHash + default: + eth.log.Errorf("ValidateSecret received unknown contract version: %d", contractVer) return false } + sh := sha256.Sum256(secret) - return bytes.Equal(sh[:], vec.SecretHash[:]) + return bytes.Equal(sh[:], secretHash[:]) } // Synced is true if the blockchain is ready for action. @@ -811,29 +858,3 @@ func (eth *ETHBackend) run(ctx context.Context) { } } } - -func (eth *baseBackend) txConfirmations(ctx context.Context, txHash common.Hash) (int64, error) { - r, err := eth.node.receipt(ctx, txHash) - if err != nil { - // Could be mempool. - if _, isMempool, err2 := eth.node.transaction(ctx, txHash); err2 != nil { - if errors.Is(err2, ethereum.NotFound) { - return 0, asset.CoinNotFoundError - } - return 0, fmt.Errorf("errors encountered searching for transaction: %v, %v", err, err2) - } else if isMempool { - return 0, nil - } - return 0, err - } - - if r.BlockNumber == nil || r.BlockNumber.Int64() <= 0 { - return 0, nil - } - - bn, err := eth.node.blockNumber(ctx) - if err != nil { - return 0, fmt.Errorf("unable to fetch block number: %v", err) - } - return int64(bn) - r.BlockNumber.Int64() + 1, nil -} diff --git a/server/asset/eth/eth_test.go b/server/asset/eth/eth_test.go index d00ced1ef3..26e1a17073 100644 --- a/server/asset/eth/eth_test.go +++ b/server/asset/eth/eth_test.go @@ -21,7 +21,6 @@ import ( "decred.org/dcrdex/dex/calc" "decred.org/dcrdex/dex/encode" dexeth "decred.org/dcrdex/dex/networks/eth" - swapv1 "decred.org/dcrdex/dex/networks/eth/contracts/v1" "decred.org/dcrdex/server/asset" "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" @@ -34,66 +33,100 @@ var ( _ ethFetcher = (*testNode)(nil) tLogger = dex.StdOutLogger("ETHTEST", dex.LevelTrace) tCtx context.Context - tSwap1 = &swapv1.ETHSwapVector{ - SecretHash: bytesToArray([]byte("1")), - Initiator: common.BytesToAddress([]byte("initiator1")), - RefundTimestamp: 1, - Participant: common.BytesToAddress([]byte("participant1")), - Value: 1, - } - tSwap2 = &swapv1.ETHSwapVector{ - SecretHash: bytesToArray([]byte("2")), - Initiator: common.BytesToAddress([]byte("initiator2")), - RefundTimestamp: 2, - Participant: common.BytesToAddress([]byte("participant2")), - Value: 2, - } - // redeemCalldata encodes [tSwap1, tSwap2] - initCalldata = mustParseHex("64a97bff00000000000000000000000000000000000000" + - "00000000000000000000000020000000000000000000000000000000000000000000000000" + - "00000000000000023100000000000000000000000000000000000000000000000000000000" + - "00000000000000000000000000000000000000000000000000696e69746961746f72310000" + - "00000000000000000000000000000000000000000000000000000000000100000000000000" + - "000000000000000000000000007061727469636970616e7431000000000000000000000000" + - "00000000000000000000000000000000000000013200000000000000000000000000000000" + - "00000000000000000000000000000000000000000000000000000000000000000000000000" + - "696e69746961746f7232000000000000000000000000000000000000000000000000000000" + - "000000000200000000000000000000000000000000000000007061727469636970616e7432" + - "0000000000000000000000000000000000000000000000000000000000000002") - initSecretHashA = mustParseHex("8b3e4acc53b664f9cf6fcac0adcd328e95d62ba1f4379650ae3e1460a0f9d1a1") - initSecretHashB = mustParseHex("ebdc4c31b88d0c8f4d644591a8e00e92b607f920ad8050deb7c7469767d9c561") - initParticipantAddr = common.HexToAddress("345853e21b1d475582E71cC269124eD5e2dD3422") - - tRedeem1 = &swapv1.ETHSwapRedemption{ - V: *tSwap1, - Secret: bytesToArray([]byte("1")), - } - tRedeem2 = &swapv1.ETHSwapRedemption{ - V: *tSwap2, - Secret: bytesToArray([]byte("2")), - } - vector2 = &dexeth.SwapVector{ - From: tRedeem2.V.Initiator, - To: tRedeem2.V.Participant, - Value: tRedeem2.V.Value, - SecretHash: tRedeem2.V.SecretHash, - LockTime: tRedeem2.V.RefundTimestamp, - } - // redeemCalldata encodes [tRedeem1, tRedeem2] - redeemCalldata = mustParseHex("428b16e100000000000000000000000000000000000" + - "0000000000000000000000000002000000000000000000000000000000000000000000" + - "0000000000000000000000231000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000696e69746" + - "961746f723100000000000000000000000000000000000000000000000000000000000" + - "0000100000000000000000000000000000000000000007061727469636970616e74310" + - "0000000000000000000000000000000000000000000000000000000000000013100000" + - "0000000000000000000000000000000000000000000000000000000003200000000000" + - "0000000000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000696e69746961746f72320000000000000000000000000" + - "0000000000000000000000000000000000000020000000000000000000000000000000" + - "0000000007061727469636970616e74320000000000000000000000000000000000000" + - "0000000000000000000000000023200000000000000000000000000000000000000000" + - "000000000000000000000") + + initiatorAddr = common.HexToAddress("0x2b84C791b79Ee37De042AD2ffF1A253c3ce9bc27") + participantAddr = common.HexToAddress("345853e21b1d475582E71cC269124eD5e2dD3422") + secretA = mustArray32("557cf82e5e72e8ba23963a5e2832767a7e2a3e0a58ac00d319605f4b34b46de2") + secretHashA = mustArray32("09439d8fdc46a777590a5345704042c2774061d5322c6a94352c98a6f6a3630a") + secretB = mustArray32("87eac09638c0c38b4e735b79f053cb869167ee770640ac5df5c4ab030813122a") + secretHashB = mustArray32("ebdc4c31b88d0c8f4d644591a8e00e92b607f920ad8050deb7c7469767d9c561") + initValue uint64 = 2_500_000_000 + swapVectorA = &dexeth.SwapVector{ + From: initiatorAddr, + To: participantAddr, + Value: dexeth.GweiToWei(initValue), + SecretHash: secretHashA, + LockTime: initLocktime, + } + locatorA = swapVectorA.Locator() + swapVectorB = &dexeth.SwapVector{ + From: initiatorAddr, + To: participantAddr, + Value: dexeth.GweiToWei(initValue), + SecretHash: secretHashB, + LockTime: initLocktime, + } + locatorB = swapVectorB.Locator() + + initCalldataV0 = mustParseHex("a8793f94000000000000000000000000000000000000" + + "0000000000000000000000000020000000000000000000000000000000000000000000" + + "0000000000000000000002000000000000000000000000000000000000000000000000" + + "000000006148111409439d8fdc46a777590a5345704042c2774061d5322c6a94352c98" + + "a6f6a3630a000000000000000000000000345853e21b1d475582e71cc269124ed5e2dd" + + "342200000000000000000000000000000000000000000000000022b1c8c1227a000000" + + "00000000000000000000000000000000000000000000000000000061481114ebdc4c31" + + "b88d0c8f4d644591a8e00e92b607f920ad8050deb7c7469767d9c56100000000000000" + + "0000000000345853e21b1d475582e71cc269124ed5e2dd342200000000000000000000" + + "000000000000000000000000000022b1c8c1227a0000") + /* initCallData parses to: + [ETHSwapInitiation { + RefundTimestamp: 1632112916 + SecretHash: 8b3e4acc53b664f9cf6fcac0adcd328e95d62ba1f4379650ae3e1460a0f9d1a1 + Value: 2.5e9 gwei + Participant: 0x345853e21b1d475582e71cc269124ed5e2dd3422 + }, + ETHSwapInitiation { + RefundTimestamp: 1632112916 + SecretHash: ebdc4c31b88d0c8f4d644591a8e00e92b607f920ad8050deb7c7469767d9c561 + Value: 2.5e9 gwei + Participant: 0x345853e21b1d475582e71cc269124ed5e2dd3422 + }] + */ + initCalldataV1 = mustParseHex("3da59631000000000000000000000000000000000000" + + "0000000000000000000000000020000000000000000000000000000000000000000000" + + "000000000000000000000209439d8fdc46a777590a5345704042c2774061d5322c6a94" + + "352c98a6f6a3630a00000000000000000000000000000000000000000000000022b1c8" + + "c1227a00000000000000000000000000002b84c791b79ee37de042ad2fff1a253c3ce9" + + "bc27000000000000000000000000000000000000000000000000000000006148111400" + + "0000000000000000000000345853e21b1d475582e71cc269124ed5e2dd3422ebdc4c31" + + "b88d0c8f4d644591a8e00e92b607f920ad8050deb7c7469767d9c56100000000000000" + + "000000000000000000000000000000000022b1c8c1227a000000000000000000000000" + + "00002b84c791b79ee37de042ad2fff1a253c3ce9bc2700000000000000000000000000" + + "00000000000000000000000000000061481114000000000000000000000000345853e2" + + "1b1d475582e71cc269124ed5e2dd3422") + + redeemCalldataV0 = mustParseHex("f4fd17f90000000000000000000000000000000000" + + "0000000000000000000000000000200000000000000000000000000000000000000000" + + "000000000000000000000002557cf82e5e72e8ba23963a5e2832767a7e2a3e0a58ac00" + + "d319605f4b34b46de209439d8fdc46a777590a5345704042c2774061d5322c6a94352c" + + "98a6f6a3630a87eac09638c0c38b4e735b79f053cb869167ee770640ac5df5c4ab0308" + + "13122aebdc4c31b88d0c8f4d644591a8e00e92b607f920ad8050deb7c7469767d9c561") + + redeemCalldataV1 = mustParseHex("23f0388b0000000000000000000000000000000000" + + "0000000000000000000000000000200000000000000000000000000000000000000000" + + "00000000000000000000000209439d8fdc46a777590a5345704042c2774061d5322c6a" + + "94352c98a6f6a3630a00000000000000000000000000000000000000000000000022b1" + + "c8c1227a00000000000000000000000000002b84c791b79ee37de042ad2fff1a253c3c" + + "e9bc270000000000000000000000000000000000000000000000000000000061481114" + + "000000000000000000000000345853e21b1d475582e71cc269124ed5e2dd3422557cf8" + + "2e5e72e8ba23963a5e2832767a7e2a3e0a58ac00d319605f4b34b46de2ebdc4c31b88d" + + "0c8f4d644591a8e00e92b607f920ad8050deb7c7469767d9c561000000000000000000" + + "00000000000000000000000000000022b1c8c1227a0000000000000000000000000000" + + "2b84c791b79ee37de042ad2fff1a253c3ce9bc27000000000000000000000000000000" + + "0000000000000000000000000061481114000000000000000000000000345853e21b1d" + + "475582e71cc269124ed5e2dd342287eac09638c0c38b4e735b79f053cb869167ee7706" + + "40ac5df5c4ab030813122a") + /* + redeemCallData parses to: + [ETHSwapRedemption { + SecretHash: 99d971975c09331eb00f5e0dc1eaeca9bf4ee2d086d3fe1de489f920007d6546 + Secret: 2c0a304c9321402dc11cbb5898b9f2af3029ce1c76ec6702c4cd5bb965fd3e73 + } + ETHSwapRedemption { + SecretHash: ebdc4c31b88d0c8f4d644591a8e00e92b607f920ad8050deb7c7469767d9c561 + Secret: 87eac09638c0c38b4e735b79f053cb869167ee770640ac5df5c4ab030813122a + }] + */ ) func mustParseHex(s string) []byte { @@ -104,8 +137,8 @@ func mustParseHex(s string) []byte { return b } -func bytesToArray(b []byte) (a [32]byte) { - copy(a[:], b) +func mustArray32(s string) (b [32]byte) { + copy(b[:], mustParseHex(s)) return } @@ -121,13 +154,12 @@ type testNode struct { syncProgErr error suggGasTipCap *big.Int suggGasTipCapErr error - swp *dexeth.SwapState + swp map[string]*dexeth.SwapState swpErr error tx *types.Transaction txIsMempool bool txErr error - rcpt *types.Receipt - rcptErr error + receipt *types.Receipt acctBal *big.Int acctBalErr error } @@ -162,24 +194,51 @@ func (n *testNode) suggestGasTipCap(ctx context.Context) (*big.Int, error) { return n.suggGasTipCap, n.suggGasTipCapErr } -func (n *testNode) status(ctx context.Context, assetID uint32, vector *dexeth.SwapVector) (*dexeth.SwapStatus, error) { - // return n.swp.State, n.swp.Secret, uint32(n.swp.BlockHeight), n.swpErr - if n.swpErr != nil { - return nil, n.swpErr +func (n *testNode) status(ctx context.Context, assetID uint32, locator []byte) (*dexeth.SwapStatus, error) { + if s := n.swp[string(locator)]; s != nil { + return &dexeth.SwapStatus{ + BlockHeight: s.BlockHeight, + Secret: s.Secret, + Step: s.State, + }, n.swpErr + } + return nil, n.swpErr +} + +func (n *testNode) vector(ctx context.Context, assetID uint32, locator []byte) (*dexeth.SwapVector, error) { + var secretHash [32]byte + switch len(locator) { + case dexeth.LocatorV1Length: + vec, _ := dexeth.ParseV1Locator(locator) + secretHash = vec.SecretHash + default: + copy(secretHash[:], locator) + } + + if s := n.swp[string(locator)]; s != nil { + return &dexeth.SwapVector{ + From: s.Initiator, + To: s.Participant, + Value: s.Value, + SecretHash: secretHash, + LockTime: uint64(s.LockTime.Unix()), + }, n.swpErr } - return &dexeth.SwapStatus{ - BlockHeight: n.swp.BlockHeight, - Secret: n.swp.Secret, - Step: n.swp.State, - }, nil + return nil, n.swpErr } -func (n *testNode) transaction(ctx context.Context, hash common.Hash) (tx *types.Transaction, isMempool bool, err error) { +func (n *testNode) statusAndVector(ctx context.Context, assetID uint32, locator []byte) (*dexeth.SwapStatus, *dexeth.SwapVector, error) { + status, _ := n.status(ctx, assetID, locator) + vec, _ := n.vector(ctx, assetID, locator) + return status, vec, n.swpErr +} + +func (n *testNode) transaction(ctx context.Context, txHash common.Hash) (tx *types.Transaction, isMempool bool, err error) { return n.tx, n.txIsMempool, n.txErr } -func (n *testNode) receipt(context.Context, common.Hash) (*types.Receipt, error) { - return n.rcpt, n.rcptErr +func (n *testNode) transactionReceipt(ctx context.Context, txHash common.Hash) (tx *types.Receipt, err error) { + return n.receipt, nil } func (n *testNode) accountBalance(ctx context.Context, assetID uint32, addr common.Address) (*big.Int, error) { @@ -198,7 +257,9 @@ func tSwap(bn, locktime int64, value uint64, secret [32]byte, state dexeth.SwapS } func tNewBackend(assetID uint32) (*AssetBackend, *testNode) { - node := &testNode{} + node := &testNode{ + swp: make(map[string]*dexeth.SwapState), + } return &AssetBackend{ baseBackend: &baseBackend{ net: dex.Simnet, @@ -218,8 +279,10 @@ func TestMain(m *testing.M) { tCtx, shutdown = context.WithCancel(context.Background()) doIt := func() int { defer shutdown() - dexeth.Tokens[usdcID].NetTokens[dex.Simnet].SwapContracts[ethContractVersion].Address = common.BytesToAddress(encode.RandomBytes(20)) - dexeth.ContractAddresses[ethContractVersion][dex.Simnet] = common.BytesToAddress(encode.RandomBytes(20)) + dexeth.Tokens[usdcID].NetTokens[dex.Simnet].SwapContracts[0].Address = common.BytesToAddress(encode.RandomBytes(20)) + dexeth.Tokens[usdcID].NetTokens[dex.Simnet].SwapContracts[1].Address = common.BytesToAddress(encode.RandomBytes(20)) + dexeth.ContractAddresses[0][dex.Simnet] = common.BytesToAddress(encode.RandomBytes(20)) + dexeth.ContractAddresses[1][dex.Simnet] = common.BytesToAddress(encode.RandomBytes(20)) return m.Run() } os.Exit(doIt()) @@ -271,7 +334,7 @@ func TestDecodeCoinID(t *testing.T) { func TestRun(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) - backend, err := unconnectedETH(BipID, dexeth.ContractAddresses[0][dex.Simnet], registeredTokens, tLogger, dex.Simnet) + backend, err := unconnectedETH(BipID, registeredTokens, tLogger, dex.Simnet) if err != nil { t.Fatalf("unconnectedETH error: %v", err) } @@ -465,61 +528,77 @@ func TestContract(t *testing.T) { copy(txHash[:], encode.RandomBytes(32)) const gasPrice = 30 const gasTipCap = 2 - swapVal := tSwap2.Value - txVal := tSwap1.Value + tSwap2.Value - locator2 := vector2.Locator() - + const swapVal = 25e8 + const txVal = 5e9 + secret0, secret1, secretHash0, secretHash1 := secretA, secretB, secretHashA, secretHashB + locator0, locator1 := locatorA, locatorB + swaps := map[string]*dexeth.SwapState{ + string(secretHash0[:]): tSwap(97, initLocktime, swapVal, secret0, dexeth.SSInitiated, &participantAddr), + string(secretHash1[:]): tSwap(97, initLocktime, swapVal, secret1, dexeth.SSInitiated, &participantAddr), + string(locator1): tSwap(97, initLocktime, swapVal, secret1, dexeth.SSInitiated, &participantAddr), + string(locator0): tSwap(97, initLocktime, swapVal, secret0, dexeth.SSInitiated, &participantAddr), + string(locator1): tSwap(97, initLocktime, swapVal, secret1, dexeth.SSInitiated, &participantAddr), + } tests := []struct { - name string - coinID []byte - contract []byte - tx *types.Transaction - swap *dexeth.SwapState - swapErr, txErr error - wantErr bool - }{{ - name: "ok", - tx: tTx(gasPrice, gasTipCap, txVal, contractAddr, initCalldata), - contract: dexeth.EncodeContractData(1, locator2), - swap: tSwap(97, initLocktime, swapVal, tRedeem2.Secret, dexeth.SSInitiated, &initParticipantAddr), - coinID: txHash[:], - }, { - name: "new coiner error, wrong tx type", - tx: tTx(gasPrice, gasTipCap, txVal, contractAddr, initCalldata), - contract: dexeth.EncodeContractData(1, locator2), - swap: tSwap(97, initLocktime, swapVal, tRedeem2.Secret, dexeth.SSInitiated, &initParticipantAddr), - coinID: txHash[1:], - wantErr: true, - }, { - name: "confirmations error, swap error", - tx: tTx(gasPrice, gasTipCap, txVal, contractAddr, initCalldata), - contract: dexeth.EncodeContractData(1, locator2), - coinID: txHash[:], - swapErr: errors.New(""), - wantErr: true, - }} + name string + ver uint32 + coinID []byte + tx *types.Transaction + locators [][]byte + swapErr error + wantErr bool + }{ + { + name: "ok v0", + tx: tTx(gasPrice, gasTipCap, txVal, contractAddr, initCalldataV0), + locators: [][]byte{secretHash0[:], secretHash1[:]}, + coinID: txHash[:], + }, { + name: "ok v1", + ver: 1, + tx: tTx(gasPrice, gasTipCap, txVal, contractAddr, initCalldataV1), + locators: [][]byte{locator0, locator1}, + coinID: txHash[:], + }, { + name: "new coiner error, wrong tx type", + ver: 1, + tx: tTx(gasPrice, gasTipCap, txVal, contractAddr, initCalldataV1), + locators: [][]byte{locator0, locator1}, + coinID: txHash[1:], + wantErr: true, + }, { + name: "confirmations error, swap error", + ver: 1, + tx: tTx(gasPrice, gasTipCap, txVal, contractAddr, initCalldataV1), + locators: [][]byte{locator0, locator1}, + coinID: txHash[:], + swapErr: errors.New(""), + wantErr: true, + }, + } for _, test := range tests { eth, node := tNewBackend(BipID) node.tx = test.tx - node.txErr = test.txErr - node.swp = test.swap + node.swp = swaps node.swpErr = test.swapErr eth.contractAddr = *contractAddr - contractData := dexeth.EncodeContractData(1, locator2) // matches initCalldata - contract, err := eth.Contract(test.coinID, contractData) - if test.wantErr { - if err == nil { - t.Fatalf("expected error for test %q", test.name) + for _, locator := range test.locators { + contractData := dexeth.EncodeContractData(test.ver, locator) + contract, err := eth.Contract(test.coinID, contractData) + if test.wantErr { + if err == nil { + t.Fatalf("expected error for test %q", test.name) + } + continue + } + if err != nil { + t.Fatalf("unexpected error for test %q: %v", test.name, err) + } + if contract.SwapAddress != participantAddr.String() || + contract.LockTime.Unix() != initLocktime { + t.Fatalf("returns do not match expected for test %q", test.name) } - continue - } - if err != nil { - t.Fatalf("unexpected error for test %q: %v", test.name, err) - } - if contract.SwapAddress != tRedeem2.V.Participant.String() || - contract.LockTime.Unix() != int64(tRedeem2.V.RefundTimestamp) { - t.Fatalf("returns do not match expected for test %q", test.name) } } } @@ -554,29 +633,32 @@ func TestValidateFeeRate(t *testing.T) { } func TestValidateSecret(t *testing.T) { - secret := bytesToArray(encode.RandomBytes(32)) - secretHash := sha256.Sum256(secret[:]) - rightVec := &dexeth.SwapVector{SecretHash: secretHash} - wrongVec := &dexeth.SwapVector{SecretHash: bytesToArray(encode.RandomBytes(32))} + v := &dexeth.SwapVector{SecretHash: sha256.Sum256(secretA[:]), Value: new(big.Int)} + badV := &dexeth.SwapVector{SecretHash: [32]byte{}, Value: new(big.Int)} tests := []struct { name string - secret [32]byte contractData []byte want bool - }{{ - name: "ok", - contractData: dexeth.EncodeContractData(1, rightVec.Locator()), - want: true, - }, { - name: "not the right hash", - contractData: dexeth.EncodeContractData(1, wrongVec.Locator()), - }, { - name: "bad contract data", - }} + }{ + { + name: "ok v0", + contractData: dexeth.EncodeContractData(0, secretHashA[:]), + want: true, + }, { + name: "ok v1", + contractData: dexeth.EncodeContractData(1, v.Locator()), + want: true, + }, { + name: "not the right hash", + contractData: dexeth.EncodeContractData(1, badV.Locator()), + }, { + name: "bad contract data", + }, + } for _, test := range tests { eth, _ := tNewBackend(BipID) - got := eth.ValidateSecret(secret[:], test.contractData) + got := eth.ValidateSecret(secretA[:], test.contractData) if test.want != got { t.Fatalf("expected %v but got %v for test %q", test.want, got, test.name) } @@ -587,53 +669,68 @@ func TestRedemption(t *testing.T) { receiverAddr, contractAddr := new(common.Address), new(common.Address) copy(receiverAddr[:], encode.RandomBytes(20)) copy(contractAddr[:], encode.RandomBytes(20)) - txHash := bytesToArray(encode.RandomBytes(32)) - secret := tRedeem2.Secret - locator := vector2.Locator() + secret, secretHash := secretB, secretHashB + var txHash [32]byte + copy(txHash[:], encode.RandomBytes(32)) const gasPrice = 30 const gasTipCap = 2 - goodContract := dexeth.EncodeContractData(1, locator) + tests := []struct { - name string - coinID, contractID []byte - swp *dexeth.SwapState - tx *types.Transaction - wantErr bool - }{{ - name: "ok", - tx: tTx(gasPrice, gasTipCap, 0, contractAddr, redeemCalldata), - contractID: goodContract, - coinID: txHash[:], - swp: tSwap(0, 0, 0, secret, dexeth.SSRedeemed, receiverAddr), - }, { - name: "new coiner error, wrong tx type", - tx: tTx(gasPrice, gasTipCap, 0, contractAddr, redeemCalldata), - contractID: goodContract, - coinID: txHash[1:], - wantErr: true, - }, { - name: "confirmations error, swap wrong state", - tx: tTx(gasPrice, gasTipCap, 0, contractAddr, redeemCalldata), - contractID: goodContract, - swp: tSwap(0, 0, 0, secret, dexeth.SSRefunded, receiverAddr), - coinID: txHash[:], - wantErr: true, - }, { - name: "bad contract data", - tx: tTx(gasPrice, gasTipCap, 0, contractAddr, redeemCalldata), - contractID: goodContract[:len(goodContract)-1], - coinID: txHash[:], - swp: tSwap(0, 0, 0, secret, dexeth.SSRedeemed, receiverAddr), - wantErr: true, - }} + name string + ver uint32 + coinID []byte + locator []byte + swp *dexeth.SwapState + tx *types.Transaction + wantErr bool + }{ + { + name: "ok v0", + tx: tTx(gasPrice, gasTipCap, 0, contractAddr, redeemCalldataV0), + locator: secretHash[:], + coinID: txHash[:], + swp: tSwap(0, 0, 0, secret, dexeth.SSRedeemed, receiverAddr), + }, { + name: "ok v1", + ver: 1, + tx: tTx(gasPrice, gasTipCap, 0, contractAddr, redeemCalldataV1), + locator: locatorA, + coinID: txHash[:], + swp: tSwap(0, 0, 0, secret, dexeth.SSRedeemed, receiverAddr), + }, { + name: "new coiner error, wrong tx type", + tx: tTx(gasPrice, gasTipCap, 0, contractAddr, redeemCalldataV0), + locator: secretHash[:], + coinID: txHash[1:], + wantErr: true, + }, { + name: "confirmations error, swap wrong state", + tx: tTx(gasPrice, gasTipCap, 0, contractAddr, redeemCalldataV0), + locator: secretHash[:], + swp: tSwap(0, 0, 0, secret, dexeth.SSRefunded, receiverAddr), + coinID: txHash[:], + wantErr: true, + }, { + name: "validate redeem error", + tx: tTx(gasPrice, gasTipCap, 0, contractAddr, redeemCalldataV0), + locator: secretHash[:31], + coinID: txHash[:], + swp: tSwap(0, 0, 0, secret, dexeth.SSRedeemed, receiverAddr), + wantErr: true, + }, + } + for _, test := range tests { eth, node := tNewBackend(BipID) node.tx = test.tx - node.rcpt = &types.Receipt{BlockNumber: new(big.Int)} - node.swp = test.swp + node.receipt = &types.Receipt{ + BlockNumber: big.NewInt(5), + } + node.swp[string(test.locator)] = test.swp eth.contractAddr = *contractAddr - _, err := eth.Redemption(test.coinID, nil, test.contractID) + contract := dexeth.EncodeContractData(test.ver, test.locator) + _, err := eth.Redemption(test.coinID, nil, contract) if test.wantErr { if err == nil { t.Fatalf("expected error for test %q", test.name) @@ -702,27 +799,32 @@ func TestValidateContract(t *testing.T) { } func testValidateContract(t *testing.T, assetID uint32) { - locator := vector2.Locator() + badLoc := append(locatorA, 8) tests := []struct { name string ver uint32 locator []byte wantErr bool - }{{ - name: "ok", - ver: 1, - locator: locator, - }, { - name: "wrong size", - ver: 1, - locator: locator[:len(locator)-1], - wantErr: true, - }, { - name: "wrong version", - ver: 0, - locator: locator, - wantErr: true, - }} + }{ + { + name: "ok v0", + locator: secretHashA[:], + }, { + name: "ok v1", + ver: 1, + locator: locatorA[:], + }, { + name: "wrong size", + ver: 1, + locator: badLoc, + wantErr: true, + }, { + name: "wrong version", + ver: 0, + locator: locatorA[:], // should be secretHashA + wantErr: true, + }, + } type contractValidator interface { ValidateContract([]byte) error @@ -737,14 +839,14 @@ func testValidateContract(t *testing.T, assetID uint32) { cv = &TokenBackend{ AssetBackend: eth, VersionedToken: &VersionedToken{ - Token: dexeth.Tokens[usdcID], - Ver: ethContractVersion, + Token: dexeth.Tokens[usdcID], + ContractVersion: test.ver, }, } } - swapData := dexeth.EncodeContractData(test.ver, test.locator) - err := cv.ValidateContract(swapData) + contractData := dexeth.EncodeContractData(test.ver, test.locator) + err := cv.ValidateContract(contractData) if test.wantErr { if err == nil { t.Fatalf("expected error for test %q", test.name) diff --git a/server/asset/eth/rpcclient.go b/server/asset/eth/rpcclient.go index 4e61d4ac8f..78131ab790 100644 --- a/server/asset/eth/rpcclient.go +++ b/server/asset/eth/rpcclient.go @@ -17,6 +17,7 @@ import ( "decred.org/dcrdex/dex" dexeth "decred.org/dcrdex/dex/networks/eth" + swapv0 "decred.org/dcrdex/dex/networks/eth/contracts/v0" swapv1 "decred.org/dcrdex/dex/networks/eth/contracts/v1" "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" @@ -47,7 +48,7 @@ type ethConn struct { endpoint string priority uint16 // swapContract is the current ETH swapContract. - swapContract swapContract + swapContracts map[uint32]swapContract // tokens are tokeners for loaded tokens. tokens is not protected by a // mutex, as it is expected that the caller will connect and place calls to // loadToken sequentially in the same thread during initialization. @@ -243,14 +244,25 @@ func (c *rpcclient) connectToEndpoint(ctx context.Context, endpoint endpoint) (* ec.txPoolSupported = true } - es, err := swapv1.NewETHSwap(c.ethContractAddr, ec.Client) + es0, err := swapv0.NewETHSwap(c.ethContractAddr, ec.Client) if err != nil { - return nil, fmt.Errorf("unable to initialize %v contract for %q: %v", c.baseChainName, endpoint, err) + return nil, err + } + sc0 := &swapSourceV0{es0} + + es1, err := swapv1.NewETHSwap(c.ethContractAddr, ec.Client) + if err != nil { + return nil, err + } + sc1 := &swapSourceV1{es1} + + ec.swapContracts = map[uint32]swapContract{ + 0: sc0, + 1: sc1, } - ec.swapContract = &swapSourceV1{es} for assetID, vToken := range c.tokensLoaded { - tkn, err := newTokener(ctx, vToken, c.net, ec.Client) + tkn, err := newTokener(ctx, assetID, vToken, c.net, ec.Client) if err != nil { return nil, fmt.Errorf("error constructing ERC20Swap: %w", err) } @@ -429,7 +441,7 @@ func (c *rpcclient) withClient(f func(ec *ethConn) error, haltOnNotFound ...bool return nil } if len(haltOnNotFound) > 0 && haltOnNotFound[0] && (errors.Is(err, ethereum.NotFound) || strings.Contains(err.Error(), "not found")) { - return err + return ethereum.NotFound } c.log.Errorf("Unpropagated error from %q: %v", ec.endpoint, err) @@ -491,7 +503,7 @@ func (c *rpcclient) loadToken(ctx context.Context, assetID uint32, vToken *Versi c.tokensLoaded[assetID] = vToken for _, cl := range c.clientsCopy() { - tkn, err := newTokener(ctx, vToken, c.net, cl.Client) + tkn, err := newTokener(ctx, assetID, vToken, c.net, cl.Client) if err != nil { return fmt.Errorf("error constructing ERC20Swap: %w", err) } @@ -508,7 +520,21 @@ func (c *rpcclient) withTokener(assetID uint32, f func(*tokener) error) error { } return f(tkn) }) +} +func (c *rpcclient) withSwapContract(assetID uint32, f func(swapContract) error) error { + if assetID == c.baseChainID { + return c.withClient(func(ec *ethConn) error { + sc, found := ec.swapContracts[ProtocolVersion(assetID).ContractVersion()] + if !found { + return fmt.Errorf("no rpc swap contract loaded for asset %d", assetID) + } + return f(sc) + }) + } + return c.withTokener(assetID, func(tkn *tokener) error { + return f(tkn) + }) } // bestHeader gets the best header at the time of calling. @@ -547,20 +573,25 @@ func (c *rpcclient) blockNumber(ctx context.Context) (bn uint64, err error) { }) } -// swap gets a swap keyed by secretHash in the contract. -func (c *rpcclient) status(ctx context.Context, assetID uint32, vector *dexeth.SwapVector) (status *dexeth.SwapStatus, err error) { - if assetID == c.baseChainID { - err = c.withClient(func(ec *ethConn) error { - status, err = ec.swapContract.Status(ctx, vector) - return err - }) - } else { - err = c.withTokener(assetID, func(tkn *tokener) error { - status, err = tkn.Status(ctx, vector) - return err - }) - } - return +func (c *rpcclient) status(ctx context.Context, assetID uint32, locator []byte) (status *dexeth.SwapStatus, err error) { + return status, c.withSwapContract(assetID, func(sc swapContract) error { + status, err = sc.status(ctx, locator) + return err + }) +} + +func (c *rpcclient) vector(ctx context.Context, assetID uint32, locator []byte) (vec *dexeth.SwapVector, err error) { + return vec, c.withSwapContract(assetID, func(sc swapContract) error { + vec, err = sc.vector(ctx, locator) + return err + }) +} + +func (c *rpcclient) statusAndVector(ctx context.Context, assetID uint32, locator []byte) (status *dexeth.SwapStatus, vec *dexeth.SwapVector, err error) { + return status, vec, c.withSwapContract(assetID, func(sc swapContract) error { + status, vec, err = sc.statusAndVector(ctx, locator) + return err + }) } // transaction gets the transaction that hashes to hash from the chain or @@ -572,6 +603,17 @@ func (c *rpcclient) transaction(ctx context.Context, hash common.Hash) (tx *type }, true) // stop on first provider with "not found", because this should be an error if tx does not exist } +func (c *rpcclient) transactionReceipt(ctx context.Context, txHash common.Hash) (r *types.Receipt, err error) { + return r, c.withClient(func(ec *ethConn) error { + r, err = ec.TransactionReceipt(ctx, txHash) + return err + }) +} + +func isNotFoundError(err error) bool { + return strings.Contains(err.Error(), "not found") +} + // dumbBalance gets the account balance, ignoring the effects of unmined // transactions. func (c *rpcclient) dumbBalance(ctx context.Context, ec *ethConn, assetID uint32, addr common.Address) (bal *big.Int, err error) { @@ -586,7 +628,6 @@ func (c *rpcclient) dumbBalance(ctx context.Context, ec *ethConn, assetID uint32 } // smartBalance gets the account balance, including the effects of known -// accountBalance gets the account balance, including the effects of known // unmined transactions. func (c *rpcclient) smartBalance(ctx context.Context, ec *ethConn, assetID uint32, addr common.Address) (bal *big.Int, err error) { tip, err := c.blockNumber(ctx) @@ -659,14 +700,6 @@ func (c *rpcclient) smartBalance(ctx context.Context, ec *ethConn, assetID uint3 return bal, nil } -// receipt fetches the transaction receipt. -func (c *rpcclient) receipt(ctx context.Context, txHash common.Hash) (r *types.Receipt, err error) { - return r, c.withClient(func(ec *ethConn) error { - r, err = ec.TransactionReceipt(ctx, txHash) - return err - }) -} - // accountBalance gets the account balance. If txPool functions are supported by the // client, it will include the effects of unmined transactions, otherwise it will not. func (c *rpcclient) accountBalance(ctx context.Context, assetID uint32, addr common.Address) (bal *big.Int, err error) { diff --git a/server/asset/eth/rpcclient_harness_test.go b/server/asset/eth/rpcclient_harness_test.go index bfc6044275..dc3051ea87 100644 --- a/server/asset/eth/rpcclient_harness_test.go +++ b/server/asset/eth/rpcclient_harness_test.go @@ -42,20 +42,20 @@ func TestMain(m *testing.M) { monitorConnectionsInterval = 3 * time.Second // Run in function so that defers happen before os.Exit is called. - dexeth.MaybeReadSimnetAddrs() run := func() (int, error) { var cancel context.CancelFunc ctx, cancel = context.WithCancel(context.Background()) defer cancel() log := dex.StdOutLogger("T", dex.LevelTrace) - netAddrs, found := dexeth.ContractAddresses[ethContractVersion] + const contractVer = 0 + netAddrs, found := dexeth.ContractAddresses[contractVer] if !found { - return 1, fmt.Errorf("no contract address for eth version %d", ethContractVersion) + return 1, fmt.Errorf("no contract address for eth version %d", contractVer) } ethContractAddr, found := netAddrs[dex.Simnet] if !found { - return 1, fmt.Errorf("no contract address for eth version %d on %s", ethContractVersion, dex.Simnet) + return 1, fmt.Errorf("no contract address for eth version %d on %s", contractVer, dex.Simnet) } ethClient = newRPCClient(BipID, 42, dex.Simnet, []endpoint{{url: wsEndpoint}, {url: alphaIPCFile}}, ethContractAddr, log) @@ -112,7 +112,9 @@ func TestSuggestGasTipCap(t *testing.T) { } func TestStatus(t *testing.T) { - _, err := ethClient.status(ctx, BipID, &dexeth.SwapVector{}) + var secretHash [32]byte + copy(secretHash[:], encode.RandomBytes(32)) + _, err := ethClient.status(ctx, BipID, secretHash[:]) if err != nil { t.Fatal(err) } diff --git a/server/asset/eth/tokener.go b/server/asset/eth/tokener.go index f939de0a13..e42c22acf4 100644 --- a/server/asset/eth/tokener.go +++ b/server/asset/eth/tokener.go @@ -10,8 +10,10 @@ import ( "decred.org/dcrdex/dex" "decred.org/dcrdex/dex/networks/erc20" + erc20v0 "decred.org/dcrdex/dex/networks/erc20/contracts/v0" erc20v1 "decred.org/dcrdex/dex/networks/erc20/contracts/v1" dexeth "decred.org/dcrdex/dex/networks/eth" + swapv0 "decred.org/dcrdex/dex/networks/eth/contracts/v0" swapv1 "decred.org/dcrdex/dex/networks/eth/contracts/v1" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" @@ -19,7 +21,9 @@ import ( // swapContract is a generic source of swap contract data. type swapContract interface { - Status(context.Context, *dexeth.SwapVector) (*dexeth.SwapStatus, error) + status(ctx context.Context, locator []byte) (*dexeth.SwapStatus, error) + vector(ctx context.Context, locator []byte) (*dexeth.SwapVector, error) + statusAndVector(ctx context.Context, locator []byte) (*dexeth.SwapStatus, *dexeth.SwapVector, error) } // erc2Contract exposes methods of a token's ERC20 contract. @@ -38,19 +42,39 @@ type tokener struct { // newTokener is a constructor for a tokener. func newTokener( ctx context.Context, + assetID uint32, vToken *VersionedToken, net dex.Network, be bind.ContractBackend, ) (*tokener, error) { - netToken, swapContract, err := networkToken(vToken, net) + netToken, contract, err := networkToken(vToken, net) if err != nil { return nil, err } - es, err := erc20v1.NewERC20Swap(swapContract.Address, be) - if err != nil { - return nil, err + var tokenAddresser interface { + TokenAddress(opts *bind.CallOpts) (common.Address, error) + } + + var sc swapContract + switch vToken.ContractVersion { + case 0: + es, err := erc20v0.NewERC20Swap(contract.Address, be) + if err != nil { + return nil, err + } + sc = &swapSourceV0{es} + tokenAddresser = es + case 1: + es, err := erc20v1.NewERC20Swap(contract.Address, be) + if err != nil { + return nil, err + } + sc = &swapSourceV1{es} + tokenAddresser = es + default: + return nil, fmt.Errorf("unsupported contract version %d", vToken.ContractVersion) } erc20, err := erc20.NewIERC20(netToken.Address, be) @@ -58,22 +82,22 @@ func newTokener( return nil, err } - boundAddr, err := es.TokenAddress(readOnlyCallOpts(ctx, false)) + boundAddr, err := tokenAddresser.TokenAddress(readOnlyCallOpts(ctx)) if err != nil { return nil, fmt.Errorf("error retrieving bound address for %s version %d contract: %w", - vToken.Name, vToken.Ver, err) + vToken.Name, vToken.ContractVersion, err) } if boundAddr != netToken.Address { return nil, fmt.Errorf("wrong bound address for %s version %d contract. wanted %s, got %s", - vToken.Name, vToken.Ver, netToken.Address, boundAddr) + vToken.Name, vToken.ContractVersion, netToken.Address, boundAddr) } tkn := &tokener{ VersionedToken: vToken, - swapContract: &swapSourceV1{es}, + swapContract: sc, erc20Contract: erc20, - contractAddr: swapContract.Address, + contractAddr: contract.Address, tokenAddr: netToken.Address, } @@ -92,51 +116,175 @@ func (t *tokener) transferred(txData []byte) *big.Int { // swapped calculates the value sent to the swap contracts initiate method. func (t *tokener) swapped(txData []byte) *big.Int { - vectors, err := dexeth.ParseInitiateDataV1(txData) + inits, err := dexeth.ParseInitiateDataV0(txData) if err != nil { return nil } - var v uint64 - for _, vector := range vectors { - v += vector.Value + v := new(big.Int) + for _, init := range inits { + v.Add(v, init.Value) } - return dexeth.GweiToWei(v) + return v } // balanceOf checks the account's token balance. func (t *tokener) balanceOf(ctx context.Context, addr common.Address) (*big.Int, error) { - return t.BalanceOf(readOnlyCallOpts(ctx, false), addr) + return t.BalanceOf(readOnlyCallOpts(ctx), addr) +} + +// swapContractV0 represents a version 0 swap contract for ETH or a token. +type swapContractV0 interface { + Swap(opts *bind.CallOpts, secretHash [32]byte) (swapv0.ETHSwapSwap, error) +} + +// swapSourceV0 wraps a swapContractV0 and translates the swap data to satisfy +// swapSource. +type swapSourceV0 struct { + contract swapContractV0 // *swapv0.ETHSwap or *erc20v0.ERCSwap +} + +// swap gets the swap state for the secretHash on the version 0 contract. +func (s *swapSourceV0) swap(ctx context.Context, secretHash [32]byte) (*dexeth.SwapState, error) { + state, err := s.contract.Swap(readOnlyCallOpts(ctx), secretHash) + if err != nil { + return nil, fmt.Errorf("swap error: %w", err) + } + return dexeth.SwapStateFromV0(&state), nil +} + +// status fetches the SwapStatus, which specifies the current state of mutable +// swap data. +func (s *swapSourceV0) status(ctx context.Context, locator []byte) (*dexeth.SwapStatus, error) { + secretHash, err := dexeth.ParseV0Locator(locator) + if err != nil { + return nil, err + } + swap, err := s.swap(ctx, secretHash) + if err != nil { + return nil, err + } + status := &dexeth.SwapStatus{ + Step: swap.State, + Secret: swap.Secret, + BlockHeight: swap.BlockHeight, + } + return status, nil +} + +// vector generates a SwapVector, containing the immutable data that defines +// the swap. +func (s *swapSourceV0) vector(ctx context.Context, locator []byte) (*dexeth.SwapVector, error) { + secretHash, err := dexeth.ParseV0Locator(locator) + if err != nil { + return nil, err + } + swap, err := s.swap(ctx, secretHash) + if err != nil { + return nil, err + } + vector := &dexeth.SwapVector{ + From: swap.Initiator, + To: swap.Participant, + Value: swap.Value, + SecretHash: secretHash, + LockTime: uint64(swap.LockTime.Unix()), + } + return vector, nil +} + +// statusAndVector generates both the status and the vector simultaneously. For +// version 0, this is better than calling status and vector separately, since +// each makes an identical call to c.swap. +func (s *swapSourceV0) statusAndVector(ctx context.Context, locator []byte) (*dexeth.SwapStatus, *dexeth.SwapVector, error) { + secretHash, err := dexeth.ParseV0Locator(locator) + if err != nil { + return nil, nil, err + } + swap, err := s.swap(ctx, secretHash) + if err != nil { + return nil, nil, err + } + vector := &dexeth.SwapVector{ + From: swap.Initiator, + To: swap.Participant, + Value: swap.Value, + SecretHash: secretHash, + LockTime: uint64(swap.LockTime.Unix()), + } + status := &dexeth.SwapStatus{ + Step: swap.State, + Secret: swap.Secret, + BlockHeight: swap.BlockHeight, + } + return status, vector, nil } -// swapContractV1 represents a version 0 swap contract for ETH or a token. type swapContractV1 interface { Status(opts *bind.CallOpts, c swapv1.ETHSwapVector) (swapv1.ETHSwapStatus, error) } -// swapSourceV1 wraps a swapContractV0 and translates the swap data to satisfy -// swapSource. type swapSourceV1 struct { contract swapContractV1 // *swapv0.ETHSwap or *erc20v0.ERCSwap } -// Swap translates the version 0 swap data to the more general SwapState to -// satisfy the swapSource interface. -func (s *swapSourceV1) Status(ctx context.Context, vector *dexeth.SwapVector) (*dexeth.SwapStatus, error) { - rec, err := s.contract.Status(readOnlyCallOpts(ctx, true), dexeth.SwapVectorToAbigen(vector)) +func (s *swapSourceV1) status(ctx context.Context, locator []byte) (*dexeth.SwapStatus, error) { + v, err := dexeth.ParseV1Locator(locator) if err != nil { - return nil, fmt.Errorf("Swap error: %w", err) + return nil, err + } + rec, err := s.contract.Status(readOnlyCallOpts(ctx), dexeth.SwapVectorToAbigen(v)) + if err != nil { + return nil, err } return &dexeth.SwapStatus{ - BlockHeight: rec.BlockNumber.Uint64(), + Step: dexeth.SwapStep(rec.Step), Secret: rec.Secret, + BlockHeight: rec.BlockNumber.Uint64(), + }, err +} + +func (s *swapSourceV1) vector(ctx context.Context, locator []byte) (*dexeth.SwapVector, error) { + return dexeth.ParseV1Locator(locator) +} + +func (s *swapSourceV1) statusAndVector(ctx context.Context, locator []byte) (*dexeth.SwapStatus, *dexeth.SwapVector, error) { + v, err := dexeth.ParseV1Locator(locator) + if err != nil { + return nil, nil, err + } + + rec, err := s.contract.Status(readOnlyCallOpts(ctx), dexeth.SwapVectorToAbigen(v)) + if err != nil { + return nil, nil, err + } + return &dexeth.SwapStatus{ Step: dexeth.SwapStep(rec.Step), - }, nil + Secret: rec.Secret, + BlockHeight: rec.BlockNumber.Uint64(), + }, v, err +} + +func (s *swapSourceV1) Status(ctx context.Context, locator []byte) (*dexeth.SwapStatus, error) { + vec, err := dexeth.ParseV1Locator(locator) + if err != nil { + return nil, err + } + + status, err := s.contract.Status(readOnlyCallOpts(ctx), dexeth.SwapVectorToAbigen(vec)) + if err != nil { + return nil, err + } + + return &dexeth.SwapStatus{ + Step: dexeth.SwapStep(status.Step), + Secret: status.Secret, + BlockHeight: status.BlockNumber.Uint64(), + }, err } // readOnlyCallOpts is the CallOpts used for read-only contract method calls. -func readOnlyCallOpts(ctx context.Context, includePending bool) *bind.CallOpts { +func readOnlyCallOpts(ctx context.Context) *bind.CallOpts { return &bind.CallOpts{ - Pending: includePending, Context: ctx, } } diff --git a/server/asset/polygon/polygon.go b/server/asset/polygon/polygon.go index b6cfadde31..91f4638115 100644 --- a/server/asset/polygon/polygon.go +++ b/server/asset/polygon/polygon.go @@ -5,9 +5,9 @@ package polygon import ( "fmt" - "time" "decred.org/dcrdex/dex" + dexeth "decred.org/dcrdex/dex/networks/eth" dexpolygon "decred.org/dcrdex/dex/networks/polygon" "decred.org/dcrdex/server/asset" "decred.org/dcrdex/server/asset/eth" @@ -15,51 +15,42 @@ import ( var registeredTokens = make(map[uint32]*eth.VersionedToken) -func registerToken(assetID uint32, ver uint32) { +func registerToken(assetID uint32, protocolVersion dexeth.ProtocolVersion) { token, exists := dexpolygon.Tokens[assetID] if !exists { panic(fmt.Sprintf("no token constructor for asset ID %d", assetID)) } asset.RegisterToken(assetID, ð.TokenDriver{ DriverBase: eth.DriverBase{ - Ver: ver, - UI: token.UnitInfo, - Nam: token.Name, + ProtocolVersion: protocolVersion, + UI: token.UnitInfo, + Nam: token.Name, }, Token: token.Token, }) registeredTokens[assetID] = ð.VersionedToken{ - Token: token, - Ver: ver, + Token: token, + ContractVersion: protocolVersion.ContractVersion(), } } func init() { asset.Register(BipID, &Driver{eth.Driver{ DriverBase: eth.DriverBase{ - Ver: version, - UI: dexpolygon.UnitInfo, - Nam: "Polygon", + ProtocolVersion: eth.ProtocolVersion(BipID), + UI: dexpolygon.UnitInfo, + Nam: "Polygon", }, }}) - registerToken(usdcID, 0) - registerToken(usdtID, 0) - registerToken(wethTokenID, 0) - registerToken(wbtcTokenID, 0) - - if blockPollIntervalStr != "" { - blockPollInterval, _ = time.ParseDuration(blockPollIntervalStr) - if blockPollInterval < time.Second { - panic(fmt.Sprintf("invalid value for blockPollIntervalStr: %q", blockPollIntervalStr)) - } - } + registerToken(usdcID, eth.ProtocolVersion(usdcID)) + registerToken(usdtID, eth.ProtocolVersion(usdtID)) + registerToken(wethTokenID, eth.ProtocolVersion(wethTokenID)) + registerToken(wbtcTokenID, eth.ProtocolVersion(wbtcTokenID)) } const ( - BipID = 966 - ethContractVersion = 0 - version = 0 + BipID = 966 ) var ( @@ -67,12 +58,6 @@ var ( usdtID, _ = dex.BipSymbolID("usdt.polygon") wethTokenID, _ = dex.BipSymbolID("weth.polygon") wbtcTokenID, _ = dex.BipSymbolID("wbtc.polygon") - - // blockPollInterval is the delay between calls to bestBlockHash to check - // for new blocks. Modify at compile time via blockPollIntervalStr: - // go build -tags lgpl -ldflags "-X 'decred.org/dcrdex/server/asset/polygon.blockPollIntervalStr=10s'" - blockPollInterval = time.Second - blockPollIntervalStr string ) type Driver struct { diff --git a/server/cmd/dcrdex/evm-protocol-overrides.json b/server/cmd/dcrdex/evm-protocol-overrides.json new file mode 100644 index 0000000000..bdeaea28d9 --- /dev/null +++ b/server/cmd/dcrdex/evm-protocol-overrides.json @@ -0,0 +1,8 @@ +{ + "eth": 0, + "usdc.eth": 0, + "polygon": 0, + "usdc.polygon": 0, + "wbtc.polygon": 0, + "weth.polygon": 0 +} \ No newline at end of file