From 1cb701994b571a590a5a0ad354b540fbd200f0ec Mon Sep 17 00:00:00 2001 From: Martin Beckmann Date: Tue, 20 Feb 2024 10:55:25 +0100 Subject: [PATCH] Track tokens lost in settlement contract (#2410) # Description Currently we don't really know if the interactions suggested in a quote were able to pay out the advertised amount because the interactions actually produced the required tokens or if the settlement contract happened to have buffers big enough to pay out any difference that might arise. # Changes Track sell and buy token balance of the settlement contract before and after the settlement. If the settlement contract has less fewer tokens than before we at least partly paid the quote our of our own pocket. This PR only adds tracking and logging of these balances but does not yet act on the difference. Theoretically a solver could try to sell any token the settlement contract has in its buffers (not just the sell_token) so the check is not bullet proof but it seems to be a fair assumption for now that they will only use the sell token balance. ## How to test CI should continue to work and we should see the new logs. --- crates/contracts/artifacts/Solver.json | 2 +- crates/contracts/solidity/Solver.sol | 12 ++-- .../src/price_estimation/trade_verifier.rs | 63 +++++++++++-------- 3 files changed, 47 insertions(+), 30 deletions(-) diff --git a/crates/contracts/artifacts/Solver.json b/crates/contracts/artifacts/Solver.json index e93eb9b7f2..5adf514a33 100644 --- a/crates/contracts/artifacts/Solver.json +++ b/crates/contracts/artifacts/Solver.json @@ -1 +1 @@ -{"abi":[{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"owner","type":"address"}],"name":"storeBalance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract ISettlement","name":"settlementContract","type":"address"},{"internalType":"address payable","name":"trader","type":"address"},{"internalType":"address","name":"sellToken","type":"address"},{"internalType":"uint256","name":"sellAmount","type":"uint256"},{"internalType":"address","name":"nativeToken","type":"address"},{"internalType":"address payable","name":"receiver","type":"address"},{"internalType":"bytes","name":"settlementCall","type":"bytes"}],"name":"swap","outputs":[{"internalType":"uint256","name":"gasUsed","type":"uint256"},{"internalType":"uint256[]","name":"queriedBalances","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"}],"bytecode":"0x608060405234801561001057600080fd5b5061069d806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c806351a937b01461003b578063aee8d1f714610065575b600080fd5b61004e61004936600461048d565b61007a565b60405161005c929190610566565b60405180910390f35b6100786100733660046105b4565b610284565b005b60006060333014610111576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603860248201527f6f6e6c792073696d756c6174696f6e206c6f67696320697320616c6c6f77656460448201527f20746f2063616c6c202773776170272066756e6374696f6e0000000000000000606482015260840160405180910390fd5b6040517f66b00f6800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8b81166004830152898116602483015260448201899052878116606483015286811660848301528a16906366b00f689060a401600060405180830381600087803b15801561019957600080fd5b505af11580156101ad573d6000803e3d6000fd5b5050505060005a905061020d85858080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505073ffffffffffffffffffffffffffffffffffffffff8f16929150506103cd565b506000545a61021c908361061c565b610226919061061c565b600180546040805160208084028201810190925282815293965083018282801561026f57602002820191906000526020600020905b81548152602001906001019080831161025b575b50505050509150509850989650505050505050565b60005a9050600173eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee73ffffffffffffffffffffffffffffffffffffffff851614610351576040517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84811660048301528516906370a0823190602401602060405180830381865afa158015610328573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061034c9190610635565b61036a565b8273ffffffffffffffffffffffffffffffffffffffff16315b815460018181018455600093845260208420909101919091558054146103925761116c610396565b61342b5b61ffff169050805a6103a8908461061c565b6103b2919061064e565b6000808282546103c2919061064e565b909155505050505050565b60606103db836000846103e2565b9392505050565b606060008473ffffffffffffffffffffffffffffffffffffffff16848460405161040c9190610661565b60006040518083038185875af1925050503d8060008114610449576040519150601f19603f3d011682016040523d82523d6000602084013e61044e565b606091505b50925090508061046057815160208301fd5b509392505050565b73ffffffffffffffffffffffffffffffffffffffff8116811461048a57600080fd5b50565b60008060008060008060008060e0898b0312156104a957600080fd5b88356104b481610468565b975060208901356104c481610468565b965060408901356104d481610468565b95506060890135945060808901356104eb81610468565b935060a08901356104fb81610468565b925060c089013567ffffffffffffffff8082111561051857600080fd5b818b0191508b601f83011261052c57600080fd5b81358181111561053b57600080fd5b8c602082850101111561054d57600080fd5b6020830194508093505050509295985092959890939650565b6000604082018483526020604081850152818551808452606086019150828701935060005b818110156105a75784518352938301939183019160010161058b565b5090979650505050505050565b600080604083850312156105c757600080fd5b82356105d281610468565b915060208301356105e281610468565b809150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b8181038181111561062f5761062f6105ed565b92915050565b60006020828403121561064757600080fd5b5051919050565b8082018082111561062f5761062f6105ed565b6000825160005b818110156106825760208186018101518583015201610668565b50600092019182525091905056fea164736f6c6343000811000a","deployedBytecode":"0x608060405234801561001057600080fd5b50600436106100365760003560e01c806351a937b01461003b578063aee8d1f714610065575b600080fd5b61004e61004936600461048d565b61007a565b60405161005c929190610566565b60405180910390f35b6100786100733660046105b4565b610284565b005b60006060333014610111576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603860248201527f6f6e6c792073696d756c6174696f6e206c6f67696320697320616c6c6f77656460448201527f20746f2063616c6c202773776170272066756e6374696f6e0000000000000000606482015260840160405180910390fd5b6040517f66b00f6800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8b81166004830152898116602483015260448201899052878116606483015286811660848301528a16906366b00f689060a401600060405180830381600087803b15801561019957600080fd5b505af11580156101ad573d6000803e3d6000fd5b5050505060005a905061020d85858080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505073ffffffffffffffffffffffffffffffffffffffff8f16929150506103cd565b506000545a61021c908361061c565b610226919061061c565b600180546040805160208084028201810190925282815293965083018282801561026f57602002820191906000526020600020905b81548152602001906001019080831161025b575b50505050509150509850989650505050505050565b60005a9050600173eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee73ffffffffffffffffffffffffffffffffffffffff851614610351576040517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84811660048301528516906370a0823190602401602060405180830381865afa158015610328573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061034c9190610635565b61036a565b8273ffffffffffffffffffffffffffffffffffffffff16315b815460018181018455600093845260208420909101919091558054146103925761116c610396565b61342b5b61ffff169050805a6103a8908461061c565b6103b2919061064e565b6000808282546103c2919061064e565b909155505050505050565b60606103db836000846103e2565b9392505050565b606060008473ffffffffffffffffffffffffffffffffffffffff16848460405161040c9190610661565b60006040518083038185875af1925050503d8060008114610449576040519150601f19603f3d011682016040523d82523d6000602084013e61044e565b606091505b50925090508061046057815160208301fd5b509392505050565b73ffffffffffffffffffffffffffffffffffffffff8116811461048a57600080fd5b50565b60008060008060008060008060e0898b0312156104a957600080fd5b88356104b481610468565b975060208901356104c481610468565b965060408901356104d481610468565b95506060890135945060808901356104eb81610468565b935060a08901356104fb81610468565b925060c089013567ffffffffffffffff8082111561051857600080fd5b818b0191508b601f83011261052c57600080fd5b81358181111561053b57600080fd5b8c602082850101111561054d57600080fd5b6020830194508093505050509295985092959890939650565b6000604082018483526020604081850152818551808452606086019150828701935060005b818110156105a75784518352938301939183019160010161058b565b5090979650505050505050565b600080604083850312156105c757600080fd5b82356105d281610468565b915060208301356105e281610468565b809150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b8181038181111561062f5761062f6105ed565b92915050565b60006020828403121561064757600080fd5b5051919050565b8082018082111561062f5761062f6105ed565b6000825160005b818110156106825760208186018101518583015201610668565b50600092019182525091905056fea164736f6c6343000811000a","devdoc":{"methods":{}},"userdoc":{"methods":{}}} +{"abi":[{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"owner","type":"address"}],"name":"storeBalance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract ISettlement","name":"settlementContract","type":"address"},{"internalType":"address payable","name":"trader","type":"address"},{"internalType":"address","name":"sellToken","type":"address"},{"internalType":"uint256","name":"sellAmount","type":"uint256"},{"internalType":"address","name":"buyToken","type":"address"},{"internalType":"address","name":"nativeToken","type":"address"},{"internalType":"address payable","name":"receiver","type":"address"},{"internalType":"bytes","name":"settlementCall","type":"bytes"}],"name":"swap","outputs":[{"internalType":"uint256","name":"gasUsed","type":"uint256"},{"internalType":"uint256[]","name":"queriedBalances","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"}],"bytecode":"0x608060405234801561001057600080fd5b506108cd806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c806329055e4f1461003b578063aee8d1f714610065575b600080fd5b61004e6100493660046106aa565b61007a565b60405161005c929190610796565b60405180910390f35b6100786100733660046107e4565b6104bd565b005b60006060333014610111576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603860248201527f6f6e6c792073696d756c6174696f6e206c6f67696320697320616c6c6f77656460448201527f20746f2063616c6c202773776170272066756e6374696f6e0000000000000000606482015260840160405180910390fd5b6040517f66b00f6800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8c811660048301528a81166024830152604482018a9052878116606483015286811660848301528b16906366b00f689060a401600060405180830381600087803b15801561019957600080fd5b505af11580156101ad573d6000803e3d6000fd5b50506040517faee8d1f700000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff808d1660048301528e16602482015230925063aee8d1f79150604401600060405180830381600087803b15801561022057600080fd5b505af1158015610234573d6000803e3d6000fd5b50506040517faee8d1f700000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff808b1660048301528e16602482015230925063aee8d1f79150604401600060405180830381600087803b1580156102a757600080fd5b505af11580156102bb573d6000803e3d6000fd5b5050505060005a905061033185858080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050508d73ffffffffffffffffffffffffffffffffffffffff166105ea90919063ffffffff16565b506000545a610340908361084c565b61034a919061084c565b6040517faee8d1f700000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8c811660048301528e166024820152909350309063aee8d1f790604401600060405180830381600087803b1580156103bc57600080fd5b505af11580156103d0573d6000803e3d6000fd5b50506040517faee8d1f700000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8b811660048301528f16602482015230925063aee8d1f79150604401600060405180830381600087803b15801561044357600080fd5b505af1158015610457573d6000803e3d6000fd5b5050505060018054806020026020016040519081016040528092919081815260200182805480156104a757602002820191906000526020600020905b815481526020019060010190808311610493575b5050505050915050995099975050505050505050565b60005a9050600173eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee73ffffffffffffffffffffffffffffffffffffffff85161461058a576040517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84811660048301528516906370a0823190602401602060405180830381865afa158015610561573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105859190610865565b6105a3565b8273ffffffffffffffffffffffffffffffffffffffff16315b815460018101835560009283526020909220909101555a6105c4908261084c565b6105d09061116c61087e565b6000808282546105e0919061087e565b9091555050505050565b60606105f8836000846105ff565b9392505050565b606060008473ffffffffffffffffffffffffffffffffffffffff1684846040516106299190610891565b60006040518083038185875af1925050503d8060008114610666576040519150601f19603f3d011682016040523d82523d6000602084013e61066b565b606091505b50925090508061067d57815160208301fd5b509392505050565b73ffffffffffffffffffffffffffffffffffffffff811681146106a757600080fd5b50565b60008060008060008060008060006101008a8c0312156106c957600080fd5b89356106d481610685565b985060208a01356106e481610685565b975060408a01356106f481610685565b965060608a0135955060808a013561070b81610685565b945060a08a013561071b81610685565b935060c08a013561072b81610685565b925060e08a013567ffffffffffffffff8082111561074857600080fd5b818c0191508c601f83011261075c57600080fd5b81358181111561076b57600080fd5b8d602082850101111561077d57600080fd5b6020830194508093505050509295985092959850929598565b6000604082018483526020604081850152818551808452606086019150828701935060005b818110156107d7578451835293830193918301916001016107bb565b5090979650505050505050565b600080604083850312156107f757600080fd5b823561080281610685565b9150602083013561081281610685565b809150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b8181038181111561085f5761085f61081d565b92915050565b60006020828403121561087757600080fd5b5051919050565b8082018082111561085f5761085f61081d565b6000825160005b818110156108b25760208186018101518583015201610898565b50600092019182525091905056fea164736f6c6343000811000a","deployedBytecode":"0x608060405234801561001057600080fd5b50600436106100365760003560e01c806329055e4f1461003b578063aee8d1f714610065575b600080fd5b61004e6100493660046106aa565b61007a565b60405161005c929190610796565b60405180910390f35b6100786100733660046107e4565b6104bd565b005b60006060333014610111576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603860248201527f6f6e6c792073696d756c6174696f6e206c6f67696320697320616c6c6f77656460448201527f20746f2063616c6c202773776170272066756e6374696f6e0000000000000000606482015260840160405180910390fd5b6040517f66b00f6800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8c811660048301528a81166024830152604482018a9052878116606483015286811660848301528b16906366b00f689060a401600060405180830381600087803b15801561019957600080fd5b505af11580156101ad573d6000803e3d6000fd5b50506040517faee8d1f700000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff808d1660048301528e16602482015230925063aee8d1f79150604401600060405180830381600087803b15801561022057600080fd5b505af1158015610234573d6000803e3d6000fd5b50506040517faee8d1f700000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff808b1660048301528e16602482015230925063aee8d1f79150604401600060405180830381600087803b1580156102a757600080fd5b505af11580156102bb573d6000803e3d6000fd5b5050505060005a905061033185858080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050508d73ffffffffffffffffffffffffffffffffffffffff166105ea90919063ffffffff16565b506000545a610340908361084c565b61034a919061084c565b6040517faee8d1f700000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8c811660048301528e166024820152909350309063aee8d1f790604401600060405180830381600087803b1580156103bc57600080fd5b505af11580156103d0573d6000803e3d6000fd5b50506040517faee8d1f700000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8b811660048301528f16602482015230925063aee8d1f79150604401600060405180830381600087803b15801561044357600080fd5b505af1158015610457573d6000803e3d6000fd5b5050505060018054806020026020016040519081016040528092919081815260200182805480156104a757602002820191906000526020600020905b815481526020019060010190808311610493575b5050505050915050995099975050505050505050565b60005a9050600173eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee73ffffffffffffffffffffffffffffffffffffffff85161461058a576040517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84811660048301528516906370a0823190602401602060405180830381865afa158015610561573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105859190610865565b6105a3565b8273ffffffffffffffffffffffffffffffffffffffff16315b815460018101835560009283526020909220909101555a6105c4908261084c565b6105d09061116c61087e565b6000808282546105e0919061087e565b9091555050505050565b60606105f8836000846105ff565b9392505050565b606060008473ffffffffffffffffffffffffffffffffffffffff1684846040516106299190610891565b60006040518083038185875af1925050503d8060008114610666576040519150601f19603f3d011682016040523d82523d6000602084013e61066b565b606091505b50925090508061067d57815160208301fd5b509392505050565b73ffffffffffffffffffffffffffffffffffffffff811681146106a757600080fd5b50565b60008060008060008060008060006101008a8c0312156106c957600080fd5b89356106d481610685565b985060208a01356106e481610685565b975060408a01356106f481610685565b965060608a0135955060808a013561070b81610685565b945060a08a013561071b81610685565b935060c08a013561072b81610685565b925060e08a013567ffffffffffffffff8082111561074857600080fd5b818c0191508c601f83011261075c57600080fd5b81358181111561076b57600080fd5b8d602082850101111561077d57600080fd5b6020830194508093505050509295985092959850929598565b6000604082018483526020604081850152818551808452606086019150828701935060005b818110156107d7578451835293830193918301916001016107bb565b5090979650505050505050565b600080604083850312156107f757600080fd5b823561080281610685565b9150602083013561081281610685565b809150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b8181038181111561085f5761085f61081d565b92915050565b60006020828403121561087757600080fd5b5051919050565b8082018082111561085f5761085f61081d565b6000825160005b818110156108b25760208186018101518583015201610898565b50600092019182525091905056fea164736f6c6343000811000a","devdoc":{"methods":{}},"userdoc":{"methods":{}}} diff --git a/crates/contracts/solidity/Solver.sol b/crates/contracts/solidity/Solver.sol index 495622a5d8..791da21f48 100644 --- a/crates/contracts/solidity/Solver.sol +++ b/crates/contracts/solidity/Solver.sol @@ -31,6 +31,7 @@ contract Solver { /// @param trader - address of the order owner doing the trade /// @param sellToken - address of the token being sold /// @param sellAmount - amount being sold + /// @param buyToken - address of the token being bought /// @param nativeToken - ERC20 version of the chain's token /// @param receiver - address receiving the bought tokens /// @param settlementCall - the calldata of the `settle()` call @@ -42,6 +43,7 @@ contract Solver { address payable trader, address sellToken, uint256 sellAmount, + address buyToken, address nativeToken, address payable receiver, bytes calldata settlementCall @@ -53,10 +55,14 @@ contract Solver { // Prepare the trade in the context of the trader so we are allowed // to set approvals and things like that. Trader(trader).prepareSwap(settlementContract, sellToken, sellAmount, nativeToken, receiver); + this.storeBalance(sellToken, address(settlementContract)); + this.storeBalance(buyToken, address(settlementContract)); uint256 gasStart = gasleft(); // TODO can we assume the overhead of this function call to be negligible due to inlining? address(settlementContract).doCall(settlementCall); gasUsed = gasStart - gasleft() - _simulationOverhead; + this.storeBalance(sellToken, address(settlementContract)); + this.storeBalance(buyToken, address(settlementContract)); queriedBalances = _queriedBalances; } @@ -72,9 +78,7 @@ contract Solver { ? owner.balance : IERC20(token).balanceOf(owner) ); - // Account for costs of gas used outside of metered section. Noting that - // reading cold storage costs more which results in higher overhead for the first call - uint256 measurementOverhead = _queriedBalances.length == 1 ? 13355 : 4460; - _simulationOverhead += gasStart - gasleft() + measurementOverhead; + // Account for costs of gas used outside of metered section. + _simulationOverhead += gasStart - gasleft() + 4460; } } diff --git a/crates/shared/src/price_estimation/trade_verifier.rs b/crates/shared/src/price_estimation/trade_verifier.rs index 504be72b59..355120c9ce 100644 --- a/crates/shared/src/price_estimation/trade_verifier.rs +++ b/crates/shared/src/price_estimation/trade_verifier.rs @@ -22,7 +22,7 @@ use { order::{OrderData, OrderKind, BUY_ETH_ADDRESS}, signature::{Signature, SigningScheme}, }, - number::nonzero::U256 as NonZeroU256, + number::{conversions::u256_to_big_int, nonzero::U256 as NonZeroU256}, std::sync::Arc, web3::{ethabi::Token, types::CallRequest}, }; @@ -127,6 +127,7 @@ impl TradeVerifying for TradeVerifier { verification.from, query.sell_token, sell_amount, + query.buy_token, self.native_token, verification.receiver, Bytes(settlement.data.unwrap().0), @@ -176,18 +177,18 @@ impl TradeVerifying for TradeVerifier { .simulate(call, overrides, Some(block)) .await .context("failed to simulate quote")?; - let summary = - SettleOutput::decode(&output).context("could not decode simulation output")?; + let summary = SettleOutput::decode(&output, query.kind) + .context("could not decode simulation output")?; let verified = VerifiedEstimate { - out_amount: summary - .out_amount(query.kind) - .context("could not compute out_amount")?, + out_amount: summary.out_amount, gas: summary.gas_used.as_u64(), solver: trade.solver, }; tracing::debug!( out_diff = ?trade.out_amount.abs_diff(verified.out_amount), gas_diff = ?trade.gas_estimate.abs_diff(verified.gas), + lost_buy_amount = ?summary.buy_tokens_diff, + lost_sell_amount = ?summary.sell_tokens_diff, time = ?start.elapsed(), promised_out_amount = ?trade.out_amount, promised_gas = trade.gas_estimate, @@ -306,39 +307,51 @@ fn add_balance_queries( settlement } -/// Output of `Trader::settle` smart contract call. +/// Analyzed output of `Solver::settle` smart contract call. #[derive(Debug)] struct SettleOutput { /// Gas used for the `settle()` call. gas_used: U256, - /// Balances queried during the simulation in the order specified during the - /// simulation set up. - queried_balances: Vec, + /// `out_amount` perceived by the trader (sell token for buy orders or buy + /// token for sell order) + out_amount: U256, + /// Difference in buy tokens of the settlement contract before and after the + /// trade. + buy_tokens_diff: num::BigInt, + /// Difference in sell tokens of the settlement contract before and after + /// the trade. + sell_tokens_diff: num::BigInt, } impl SettleOutput { - fn decode(output: &[u8]) -> Result { + fn decode(output: &[u8], kind: OrderKind) -> Result { let function = Solver::raw_contract().abi.function("swap").unwrap(); let tokens = function.decode_output(output).context("decode")?; - let (gas_used, queried_balances) = Tokenize::from_token(Token::Tuple(tokens))?; - Ok(Self { - gas_used, - queried_balances, - }) - } + let (gas_used, balances): (U256, Vec) = Tokenize::from_token(Token::Tuple(tokens))?; + + let settlement_sell_balance_before = u256_to_big_int(&balances[0]); + let settlement_buy_balance_before = u256_to_big_int(&balances[1]); + + let trader_balance_before = balances[2]; + let trader_balance_after = balances[3]; + + let settlement_sell_balance_after = u256_to_big_int(&balances[4]); + let settlement_buy_balance_after = u256_to_big_int(&balances[5]); - /// Computes the actual [`Trade::out_amount`] based on the simulation. - fn out_amount(&self, kind: OrderKind) -> Result { - let balances = &self.queried_balances; - let balance_before = balances.first().context("no balance before settlement")?; - let balance_after = balances.get(1).context("no balance after settlement")?; let out_amount = match kind { // for sell orders we track the buy_token amount which increases during the settlement - OrderKind::Sell => balance_after.checked_sub(*balance_before), + OrderKind::Sell => trader_balance_after.checked_sub(trader_balance_before), // for buy orders we track the sell_token amount which decreases during the settlement - OrderKind::Buy => balance_before.checked_sub(*balance_after), + OrderKind::Buy => trader_balance_before.checked_sub(trader_balance_after), }; - out_amount.context("underflow during out_amount computation") + let out_amount = out_amount.context("underflow during out_amount computation")?; + + Ok(SettleOutput { + gas_used, + out_amount, + buy_tokens_diff: settlement_buy_balance_before - settlement_buy_balance_after, + sell_tokens_diff: settlement_sell_balance_before - settlement_sell_balance_after, + }) } }