From 4ef0bb35a77e3fcd098e76e67b25201d6b717a03 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Mon, 9 Nov 2026 18:23:20 -0500 Subject: [PATCH] Remove open and openext; every balance and swap row is billed to sysio sysio.token, sysio.liq and sysio.swap bill every balance row to sysio, and the first credit creates the row, so open is removed from all three. close stays. Every sysio.swap row is now billed to sysio: pair-token balances, deposits, the configuration, the rows inittoken writes, and the fundyield and yield payout receipts. The first credit of a token creates its deposit row, so openext is removed; closeext stays. Holders' RAM is unchanged by depositing, sending, receiving, withdrawing and closing. sysio.liq's calls into sysio.swap no longer open deposit rows or carry sysio.payer. clio transfer drops --pay-ram-to-open, which sent open. Change-Id: Ieb42335cd3ccaea653672cd8ad63767dd056ca2e --- contracts/sysio.liq/README.md | 2 +- .../sysio.liq/include/sysio.liq/sysio.liq.hpp | 5 +- contracts/sysio.liq/src/sysio.liq.cpp | 51 ++-- contracts/sysio.liq/sysio.liq.abi | 23 -- contracts/sysio.liq/sysio.liq.wasm | Bin 43015 -> 41228 bytes contracts/sysio.swap/Commands.md | 30 +-- contracts/sysio.swap/README.md | 2 +- .../include/sysio.swap/sysio.swap.hpp | 24 +- .../ricardian/sysio.swap.contracts.md | 51 +--- contracts/sysio.swap/sysio.swap.abi | 46 ---- contracts/sysio.swap/sysio.swap.cpp | 50 ++-- contracts/sysio.swap/sysio.swap.wasm | Bin 49668 -> 48373 bytes contracts/sysio.swap/token_functions.cpp | 37 +-- .../include/sysio.token/sysio.token.hpp | 21 +- contracts/sysio.token/sysio.token.abi | 23 -- contracts/sysio.token/sysio.token.bad.abi | 23 -- contracts/sysio.token/sysio.token.cpp | 23 +- contracts/sysio.token/sysio.token.wasm | Bin 10450 -> 9562 bytes contracts/tests/sysio.kicker_tests.cpp | 8 - contracts/tests/sysio.liq_tests.cpp | 15 +- contracts/tests/sysio.swap_tests.cpp | 240 +++++++++++------- contracts/tests/sysio.synd_tests.cpp | 3 +- contracts/tests/sysio.token_tests.cpp | 62 +---- contracts/tests/sysio_liq_tester.hpp | 19 +- docs/emergency-stop-playbook.md | 2 +- programs/clio/main.cpp | 20 +- unittests/kv_token_tests.cpp | 18 +- unittests/sysio_system_tester.hpp | 10 - 28 files changed, 245 insertions(+), 563 deletions(-) diff --git a/contracts/sysio.liq/README.md b/contracts/sysio.liq/README.md index dbb9690541..1d3b758a29 100644 --- a/contracts/sysio.liq/README.md +++ b/contracts/sysio.liq/README.md @@ -68,7 +68,7 @@ and sets the tick parameters. The pre-launch positions are replayed by | `burn(token_code, amount)` | `sysio.synd` | Burn shadow out of `sysio.synd`'s own row | | `mintyield(chain_code, token_code, amount)` | `sysio.synd` | LIQ_YIELD into the pending balance | | `queueyield(sym)` | none | Pending yield into the swap's reservoir | -| `transfer`, `open`, `close`, `claim` | holder | `sysio.token`'s shape; `close` refuses while yield is owed | +| `transfer`, `close`, `claim` | holder | `sysio.token`'s shape, balance rows billed to `sysio`; `close` refuses while yield is owed | | `addyield(from, quantity, target)` | `from` | Distribute WIRE to `target`'s holders | | `regliqpool(...)` | privileged caller, epoch 0 | Seed a shadow's yield pool | diff --git a/contracts/sysio.liq/include/sysio.liq/sysio.liq.hpp b/contracts/sysio.liq/include/sysio.liq/sysio.liq.hpp index 83f7c1ea00..536abdcfb5 100644 --- a/contracts/sysio.liq/include/sysio.liq/sysio.liq.hpp +++ b/contracts/sysio.liq/include/sysio.liq/sysio.liq.hpp @@ -135,7 +135,6 @@ namespace sysio { /// and sysio.bond may debit their own balance. Settles both yield positions, preserves supply, /// and requires the Andon cord clear, including self-settlement. [[sysio::action]] void settle(name custodian, name beneficiary, asset quantity); - [[sysio::action]] void open(name owner, symbol symbol, name ram_payer); /// Erase `owner`'s empty row for `symbol`. Refused while the row is still /// owed yield, so closing never discards WIRE. [[sysio::action]] void close(name owner, symbol symbol); @@ -261,9 +260,9 @@ namespace sysio { /// The one funnel every balance move goes through: settle `row` at `index`, /// stamp it, then apply `delta`. Nothing else writes `balance`. static void settle_and_adjust(opp::shadow::account& row, u128 index, const asset& delta); - /// Apply `delta` to `owner`'s row for its symbol through the funnel. A row + /// Apply `delta` to `owner`'s row for its symbol through the funnel, billed to `sysio`. A row /// created here is stamped at the current index, so no history is credited. - void adjust_account(name owner, const asset& delta, name payer); + void adjust_account(name owner, const asset& delta); /// Grow `sym`'s supply by `quantity`; false (and no change) past its headroom. bool grow_supply(symbol_code sym, uint64_t quantity); /// Advance `sym`'s index by `quantity` WIRE over its supply, carrying the diff --git a/contracts/sysio.liq/src/sysio.liq.cpp b/contracts/sysio.liq/src/sysio.liq.cpp index 924619fcc4..05439a9f0f 100644 --- a/contracts/sysio.liq/src/sysio.liq.cpp +++ b/contracts/sysio.liq/src/sysio.liq.cpp @@ -36,9 +36,6 @@ constexpr std::string_view CLAIM_MEMO = "sysio.liq claim"; /// Memo of the transfer that brings the WIRE `creditowed` credits in from sysio.synd. constexpr std::string_view CREDIT_MEMO = "sysio.liq creditowed"; -/// The `sysio.payer` seat: an inline action to an unprivileged contract that bills a -/// row to `actor` must carry it beside `actor`'s active permission. -permission_level payer_of(name actor) { return permission_level{ actor, "sysio.payer"_n }; } permission_level active_of(name actor) { return permission_level{ actor, "active"_n }; } void drop(const char* path, const char* reason) { @@ -86,7 +83,7 @@ void liq::recredit(name holder, asset quantity) { const currency_stats st = stat_of(quantity.symbol.code()); check(quantity.symbol == st.supply.symbol, symbol_precision_mismatch_msg); check(grow_supply(quantity.symbol.code(), static_cast(quantity.amount)), "supply exceeds the asset range"); - adjust_account(holder, quantity, ram_payer); + adjust_account(holder, quantity); } // --------------------------------------------------------------------------- @@ -100,7 +97,7 @@ void liq::mint(name to, sysio::slug_name token_code, uint64_t amount) { const currency_stats st = stat_by_token(token_code); const symbol_code sym = st.supply.symbol.code(); check(grow_supply(sym, amount), "supply exceeds the asset range"); - adjust_account(to, asset{ static_cast(amount), st.supply.symbol }, ram_payer); + adjust_account(to, asset{ static_cast(amount), st.supply.symbol }); } void liq::burn(sysio::slug_name token_code, uint64_t amount) { @@ -109,7 +106,7 @@ void liq::burn(sysio::slug_name token_code, uint64_t amount) { const currency_stats st = stat_by_token(token_code); const asset quantity{ static_cast(amount), st.supply.symbol }; // Settle, then burn: the row keeps every subunit of yield accrued to now. - adjust_account(SYND_ACCOUNT, -quantity, ram_payer); + adjust_account(SYND_ACCOUNT, -quantity); stats statstable(get_self()); statstable.modify(ram_payer, symbol_key{ st.supply.symbol.code().raw() }, [&](currency_stats& s) { s.supply -= quantity; @@ -200,10 +197,9 @@ void liq::queueyield(symbol_code sym) { // Minted to this contract and handed on in the same transaction, so its row is // settled at one index and accrues nothing on the way through. check(grow_supply(sym, static_cast(quantity.amount)), "supply exceeds the asset range"); - adjust_account(get_self(), quantity, ram_payer); + adjust_account(get_self(), quantity); - action(std::vector{ payer_of(get_self()), active_of(get_self()) }, - SWAP_ACCOUNT, "fundyield"_n, + action(active_of(get_self()), SWAP_ACCOUNT, "fundyield"_n, std::make_tuple(get_self(), st.pair_symbol, quantity)).send(); action(active_of(get_self()), get_self(), "transfer"_n, std::make_tuple(get_self(), SWAP_ACCOUNT, quantity, std::string{})).send(); @@ -230,8 +226,8 @@ void liq::transfer(name from, name to, asset quantity, string memo) { check(quantity.symbol == st.supply.symbol, symbol_precision_mismatch_msg); check(memo.size() <= MAX_MEMO_BYTES, "memo has more than 256 bytes"); - adjust_account(from, -quantity, ram_payer); - adjust_account(to, quantity, ram_payer); + adjust_account(from, -quantity); + adjust_account(to, quantity); } void liq::settle(name custodian, name beneficiary, asset quantity) { @@ -246,21 +242,10 @@ void liq::settle(name custodian, name beneficiary, asset quantity) { if (custodian == beneficiary) { const auto row = accounts(get_self(), custodian.value).try_get(symbol_key{quantity.symbol.code().raw()}); check(row && row->balance.amount >= quantity.amount, overdrawn_balance_msg); - adjust_account(custodian, asset{0, quantity.symbol}, ram_payer); + adjust_account(custodian, asset{0, quantity.symbol}); } else { - adjust_account(custodian, -quantity, ram_payer); - adjust_account(beneficiary, quantity, ram_payer); - } -} - -void liq::open(name owner, symbol symbol, name ram_payer_) { - require_auth(ram_payer_); - check(is_account(owner), "owner account does not exist"); - const currency_stats st = stat_of(symbol.code()); - check(st.supply.symbol == symbol, symbol_precision_mismatch_msg); - accounts holdings(get_self(), owner.value); - if (!holdings.contains(symbol_key{ symbol.code().raw() })) { - adjust_account(owner, asset{ 0, symbol }, ram_payer_); + adjust_account(custodian, -quantity); + adjust_account(beneficiary, quantity); } } @@ -337,22 +322,16 @@ void liq::regliqpool(sysio::slug_name chain_code, sysio::slug_name token_code, s const asset shadow{ static_cast(initial_chain_amount), sym }; const asset wire { static_cast(initial_wire_amount), WIRE_SYM }; const extended_symbol shadow_symbol{ sym, get_self() }; - const extended_symbol wire_symbol { WIRE_SYM, TOKEN_ACCOUNT }; // The LCO liq is protocol-owned and already in outpost custody: its shadow is // minted to sysio, which seeds the pool with it and holds the pool's shares. check(grow_supply(sym.code(), initial_chain_amount), "supply exceeds the asset range"); - adjust_account(SYSTEM_ACCOUNT, shadow, ram_payer); + adjust_account(SYSTEM_ACCOUNT, shadow); stats statstable(get_self()); statstable.modify(ram_payer, symbol_key{ sym.code().raw() }, [&](currency_stats& s) { s.pair_symbol = pair_symbol.code(); }); - const std::vector sysio_billed{ payer_of(SYSTEM_ACCOUNT), active_of(SYSTEM_ACCOUNT) }; - action(sysio_billed, SWAP_ACCOUNT, "openext"_n, - std::make_tuple(SYSTEM_ACCOUNT, SYSTEM_ACCOUNT, shadow_symbol)).send(); - action(sysio_billed, SWAP_ACCOUNT, "openext"_n, - std::make_tuple(SYSTEM_ACCOUNT, SYSTEM_ACCOUNT, wire_symbol)).send(); // The shadow has no pair yet, so its seed carries the swap's own authority. action(std::vector{ active_of(SYSTEM_ACCOUNT), active_of(SWAP_ACCOUNT) }, get_self(), "transfer"_n, @@ -360,7 +339,7 @@ void liq::regliqpool(sysio::slug_name chain_code, sysio::slug_name token_code, s // The WIRE side is the T5 dex earmark, drained from the treasury. action(active_of(SYSTEM_ACCOUNT), TOKEN_ACCOUNT, "transfer"_n, std::make_tuple(SYSTEM_ACCOUNT, SWAP_ACCOUNT, wire, std::string{})).send(); - action(std::vector{ payer_of(SYSTEM_ACCOUNT), active_of(SYSTEM_ACCOUNT), active_of(SWAP_ACCOUNT) }, + action(std::vector{ active_of(SYSTEM_ACCOUNT), active_of(SWAP_ACCOUNT) }, SWAP_ACCOUNT, "inittoken"_n, std::make_tuple(SYSTEM_ACCOUNT, pair_symbol, extended_asset{ shadow, get_self() }, extended_asset{ wire, TOKEN_ACCOUNT }, fee, SYSTEM_ACCOUNT, @@ -403,19 +382,19 @@ void liq::settle_and_adjust(opp::shadow::account& row, u128 index, const asset& check(row.balance.amount >= 0, overdrawn_balance_msg); } -void liq::adjust_account(name owner, const asset& delta, name payer) { +void liq::adjust_account(name owner, const asset& delta) { accounts holdings(get_self(), owner.value); const symbol_key key{ delta.symbol.code().raw() }; const u128 index = current_index(delta.symbol.code()); const auto row = holdings.try_get(key); if (!row) { check(delta.amount >= 0, "no balance object found"); - holdings.emplace(payer, key, opp::shadow::account{ delta, index, 0 }); + holdings.emplace(ram_payer, key, opp::shadow::account{ delta, index, 0 }); return; } opp::shadow::account updated = *row; settle_and_adjust(updated, index, delta); - holdings.modify(payer, key, [&](opp::shadow::account& a) { a = updated; }); + holdings.modify(ram_payer, key, [&](opp::shadow::account& a) { a = updated; }); } bool liq::grow_supply(symbol_code sym, uint64_t quantity) { diff --git a/contracts/sysio.liq/sysio.liq.abi b/contracts/sysio.liq/sysio.liq.abi index eae479ea67..b0ec60b8d0 100644 --- a/contracts/sysio.liq/sysio.liq.abi +++ b/contracts/sysio.liq/sysio.liq.abi @@ -175,24 +175,6 @@ } ] }, - { - "name": "open", - "base": "", - "fields": [ - { - "name": "owner", - "type": "name" - }, - { - "name": "symbol", - "type": "symbol" - }, - { - "name": "ram_payer", - "type": "name" - } - ] - }, { "name": "pending_yield", "base": "", @@ -383,11 +365,6 @@ "type": "mintyield", "ricardian_contract": "" }, - { - "name": "open", - "type": "open", - "ricardian_contract": "" - }, { "name": "queueyield", "type": "queueyield", diff --git a/contracts/sysio.liq/sysio.liq.wasm b/contracts/sysio.liq/sysio.liq.wasm index 078d36dada49661d1fe8d9b57625a21cd35cd9c1..96c14968729946f25085b5971fd5e38944a3b755 100755 GIT binary patch delta 6870 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 TY`&y8=vzwYv2Sd8x}y9q%$~Ml delta 8103 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 - ---- -spec_version: "0.2.0" -title: Open extended Balance -summary: 'Open a zero quantity extended balance for {{nowrap user}}' ---- - -{{ram_payer}} agrees to establish a zero quantity extended balance for {{user}} -for the {{ext_symbol}} extended symbol. - -If {{user}} does not have an extended balance for {{ext_symbol}}, {{ram_payer}} will be designated as the RAM payer of {{user}}'s extended balance for {{ext_symbol}}. As a result, RAM will be deducted from {{ram_payer}}’s resources to create the necessary records. - -The authorization of {{ram_payer}} is required. - -

closeext

--- @@ -28,7 +12,7 @@ If the extended balance is nonzero, it will be transfered to {{to}} before closi In order to function properly, it is necessary that the contract of {{ext_symbol}} permanently has a transfer action that satisfies the conditions (1), (2), (6) of the present contract's transfer action. -RAM will be refunded to the RAM payer of {{user}}'s extended balance for {{ext_symbol}}. +RAM will be refunded to sysio, which pays for every extended balance. The authorization of {{user}} is required. @@ -45,9 +29,9 @@ This action is executed as a response to a notification of a transfer action wit The transfer is accepted only if the extended symbol formed by the transfer's contract and the symbol {{asset_to_symbol quantity}} is the system token set by setconfig, or is the first leg of the pair it forms with the system token, or the transfer carries this contract's authority (the authority that creates pairs, so that a pair's first leg can be seeded before the pair exists). Any other transfer fails. -The default response is to deposit {{quantity}} to {{from}}'s extended balance for that extended symbol. This is only possible if such extended balance previously exists. +The default response is to deposit {{quantity}} to {{from}}'s extended balance for that extended symbol. If {{from}} does not have that extended balance, it is created, and its RAM is billed to sysio. -If {{memo}} starts with "deposit to:", the account {{from}} will be replaced by the subsequent content of {{memo}} whenever it is possible. +If {{memo}} starts with "deposit to:", the account {{from}} will be replaced by the subsequent content of {{memo}} whenever it is possible. The named account must exist. Two typed routes take precedence over the memo, and each accepts exactly one transfer. If {{from}} is a shadow token from which this contract has a yield payout outstanding (see accrueyield), {{quantity}} must be that payout, which was already credited to the pool; the receipt is retired and nothing else is deposited. Otherwise, if {{from}} has a yield funding pending (see fundyield), {{quantity}} must be the announced amount, which fills the announced pair's reservoir. In either case any other transfer from {{from}} fails. @@ -87,7 +71,7 @@ The pair token supply minted is the square root of the product of the two initia If {{yield_leg}} is given it must be the first leg, and the pair becomes a yield pool on that shadow token: the yield the contract is owed on the shadow it holds is settled into the second leg without minting, before every addliquidity and remliquidity and on accrueyield. Without {{yield_leg}} the pair is a plain pool. -RAM will be deducted from {{user}}’s resources to create the necessary records. +The RAM of the necessary records is billed to sysio. Authorization of {{user}} and of the contract is required. @@ -210,7 +194,7 @@ summary: 'Announce {{nowrap quantity}} of shadow for the reservoir of {{nowrap p {{from}} announces the transfer of exactly {{quantity}}, in the shadow symbol of the yield pool {{pair_token}}, to this contract. The transfer of {{quantity}} from {{from}} that follows, in this transaction or a later one, is added to the reservoir of {{pair_token}}, the shadow queued to be sold through the pool, and not to {{from}}'s extended balance. While the announcement is pending, any other transfer from {{from}} to this contract is refused. A new announcement by {{from}} replaces the pending one. When the delivery fills a reservoir that was empty, the pair's tick clock restarts. -RAM for the record of the announcement will be deducted from {{from}}'s resources, and returned by cancelyield or by the delivery. +The RAM of the record of the announcement is billed to sysio, and returned by cancelyield or by the delivery. Authorization of {{from}} is required. @@ -223,7 +207,7 @@ title: Cancel yield funding summary: 'Drop the pending funding announcement of {{nowrap from}}' --- -{{from}} agrees to withdraw their pending fundyield announcement. The record is erased and its RAM returned to {{from}}, and transfers from {{from}} to this contract are treated as ordinary deposits again. The action fails if {{from}} has no pending announcement. +{{from}} agrees to withdraw their pending fundyield announcement. The record is erased and its RAM returned to sysio, and transfers from {{from}} to this contract are treated as ordinary deposits again. The action fails if {{from}} has no pending announcement. Authorization of {{from}} is required. @@ -276,20 +260,7 @@ summary: 'Close {{nowrap owner}}’s zero quantity balance' {{owner}} agree to close their zero quantity balance for the {{symbol_to_symbol_code symbol}} token. -RAM will be refunded to the RAM payer of the {{symbol_to_symbol_code symbol}} token balance for {{owner}}. - - -

open

- ---- -spec_version: "0.2.0" -title: Open Token Balance -summary: 'Open a zero quantity balance for {{nowrap owner}}' ---- - -{{ram_payer}} agrees to establish a zero quantity balance for {{owner}} for the {{symbol_to_symbol_code symbol}} token. - -If {{owner}} does not have a balance for {{symbol_to_symbol_code symbol}}, {{ram_payer}} will be designated as the RAM payer of the {{symbol_to_symbol_code symbol}} token balance for {{owner}}. As a result, RAM will be deducted from {{ram_payer}}’s resources to create the necessary records. +RAM will be refunded to sysio, which pays for every {{symbol_to_symbol_code symbol}} token balance.

transfer

@@ -305,9 +276,7 @@ summary: 'Send {{nowrap quantity}} from {{nowrap from}} to {{nowrap to}}' (2) {{#if memo}} There is a memo attached to the transfer stating: {{memo}} {{/if}} -(3) If {{from}} is not already the RAM payer of their {{asset_to_symbol_code quantity}} token balance, {{from}} will be designated as such. As a result, RAM will be deducted from {{from}}’s resources to refund the original RAM payer. - -(4) If {{to}} does not have a balance for {{asset_to_symbol_code quantity}}, {{from}} will be designated as the RAM payer of the {{asset_to_symbol_code quantity}} token balance for {{to}}. As a result, RAM will be deducted from {{from}}’s resources to create the necessary records. +(3) If {{to}} does not have a balance for {{asset_to_symbol_code quantity}}, one is created. The RAM of both balances is billed to sysio, not to {{from}} or {{to}}. -(5) The account {{from}} is notified. -(6) The account {{to}} is notified. \ No newline at end of file +(4) The account {{from}} is notified. +(5) The account {{to}} is notified. \ No newline at end of file diff --git a/contracts/sysio.swap/sysio.swap.abi b/contracts/sysio.swap/sysio.swap.abi index 9f90819beb..5e430dfcc1 100644 --- a/contracts/sysio.swap/sysio.swap.abi +++ b/contracts/sysio.swap/sysio.swap.abi @@ -341,42 +341,6 @@ } ] }, - { - "name": "open", - "base": "", - "fields": [ - { - "name": "owner", - "type": "name" - }, - { - "name": "symbol", - "type": "symbol" - }, - { - "name": "ram_payer", - "type": "name" - } - ] - }, - { - "name": "openext", - "base": "", - "fields": [ - { - "name": "user", - "type": "name" - }, - { - "name": "payer", - "type": "name" - }, - { - "name": "ext_symbol", - "type": "extended_symbol" - } - ] - }, { "name": "pair_identity_key", "base": "", @@ -644,16 +608,6 @@ "type": "inittoken", "ricardian_contract": "" }, - { - "name": "open", - "type": "open", - "ricardian_contract": "" - }, - { - "name": "openext", - "type": "openext", - "ricardian_contract": "" - }, { "name": "remliquidity", "type": "remliquidity", diff --git a/contracts/sysio.swap/sysio.swap.cpp b/contracts/sysio.swap/sysio.swap.cpp index d16ad07f68..ec18bbf196 100644 --- a/contracts/sysio.swap/sysio.swap.cpp +++ b/contracts/sysio.swap/sysio.swap.cpp @@ -16,16 +16,6 @@ namespace { namespace sysio { -void swap::openext( const name& user, const name& payer, const extended_symbol& ext_symbol) { - check( is_account( user ), "user account does not exist" ); - require_auth( payer ); - evodexacnts acnts( get_self(), user.value ); - const auto key = key_of(ext_symbol); - if( !acnts.contains(key) ) { - acnts.emplace( payer, key, evodex_account{ extended_asset{0, ext_symbol} } ); - } -} - void swap::closeext( const name& user, const name& to, const extended_symbol& ext_symbol, string memo) { require_auth( user ); evodexacnts acnts( get_self(), user.value ); @@ -167,7 +157,7 @@ void swap::add_signed_liq(name user, asset to_add, bool is_buying, add_signed_ext_balance(user, -to_pay1); add_signed_ext_balance(user, -to_pay2); - (to_add.amount > 0)? add_balance(user, to_add, user) : sub_balance(user, -to_add); + (to_add.amount > 0)? add_balance(user, to_add) : sub_balance(user, -to_add); update_price_accumulators(token); statstable.modify( name{}, key, [&]( auto& a ) { a.supply += to_add; @@ -286,7 +276,7 @@ void swap::setconfig(name fee_authority, extended_symbol system_token) { check( is_account( system_token.get_contract() ), "system token contract does not exist" ); check( system_token.get_symbol().is_valid(), "invalid system token symbol" ); swapconfig_t config( get_self() ); - config.set( swap_config{ fee_authority, system_token }, get_self() ); + config.set( swap_config{ fee_authority, system_token }, RAM_PAYER ); } swap::swap_config swap::configured() const { @@ -330,7 +320,7 @@ std::optional yield_leg) // shadow can have. check( *yield_leg == initial_pool1.get_extended_symbol(), "yield_leg must be the pair's first leg" ); reservoirs reservoir_table( get_self() ); - reservoir_table.emplace( user, key, reservoir{ extended_asset{ 0, *yield_leg } } ); + reservoir_table.emplace( RAM_PAYER, key, reservoir{ extended_asset{ 0, *yield_leg } } ); } check( 0 <= initial_fee && initial_fee <= MAX_FEE, "fee out of range" ); if (fee_authority == name{}) { @@ -339,7 +329,7 @@ std::optional yield_leg) check( is_account( fee_authority ), "fee authority account does not exist" ); } - statstable.emplace( user, key, currency_stats{ + statstable.emplace( RAM_PAYER, key, currency_stats{ .supply = new_token, .max_supply = asset{MAX, new_symbol}, .issuer = get_self(), @@ -352,11 +342,11 @@ std::optional yield_leg) } ); priceaccums accums( get_self() ); - accums.emplace( user, key, price_accumulator{ .last_update = current_time_point() } ); + accums.emplace( RAM_PAYER, key, price_accumulator{ .last_update = current_time_point() } ); placeindex(user, new_symbol, initial_pool1, initial_pool2 ); // The locked shares count toward supply but are credited to nobody. - add_balance(user, new_token - locked_shares, user); + add_balance(user, new_token - locked_shares); add_signed_ext_balance(user, -initial_pool1); add_signed_ext_balance(user, -initial_pool2); } @@ -364,7 +354,7 @@ std::optional yield_leg) void swap::placeindex(name user, symbol evo_symbol, extended_asset pool1, extended_asset pool2 ) { evoindexes indextable( get_self() ); - indextable.emplace( user, identity_of(pool1.get_extended_symbol(), pool2.get_extended_symbol()), + indextable.emplace( RAM_PAYER, identity_of(pool1.get_extended_symbol(), pool2.get_extended_symbol()), pair_index{ evo_symbol }, "the pool is already indexed" ); } @@ -431,7 +421,7 @@ swap::currency_stats swap::accrue(const pair_key& key, const currency_stats& tok const extended_asset payout{ asset{ int64_t(owed), payout_symbol.get_symbol() }, payout_symbol.get_contract() }; yieldpayouts payouts( get_self() ); - payouts.emplace( get_self(), contract_key{ shadow.get_contract() }, + payouts.emplace( RAM_PAYER, contract_key{ shadow.get_contract() }, payout_receipt{ token.supply.symbol.code(), payout }, "a yield payout from this contract is still pending" ); // The pool changes: close the accumulators' interval at the old price first. @@ -454,11 +444,9 @@ void swap::fundyield(name from, symbol_code pair_token, asset quantity) { const extended_symbol& shadow = require_yield_leg(*token); check( quantity.symbol == shadow.get_symbol(), "quantity must be in the pair's shadow symbol" ); check( quantity.amount > 0, "quantity must be positive" ); - // Billed to `from`: only the matching transfer erases the row, so an - // announcement nobody delivers would otherwise sit on the contract's RAM, - // one per account that ever called this. cancelyield refunds it. + // One row per funder, erased by the matching transfer or by cancelyield. yieldfunds funds( get_self() ); - funds.upsert( from, funder_key{ from }, + funds.upsert( RAM_PAYER, funder_key{ from }, fund_receipt{ pair_token, extended_asset{ quantity, shadow.get_contract() } } ); } @@ -623,13 +611,19 @@ swap::pair_identity_key swap::identity_of(const extended_symbol& a, const extend void swap::add_signed_ext_balance( const name& user, const extended_asset& to_add ) { check( to_add.quantity.is_valid(), "invalid asset" ); + if (to_add.quantity.amount == 0) return; evodexacnts acnts( get_self(), user.value ); const auto key = key_of(to_add.get_extended_symbol()); - check( acnts.contains(key), "extended_symbol not registered for this user,\ - please run openext action or write exchange details in the memo of your transfer"); - acnts.modify( name{}, key, [&]( auto& a ) { - a.balance += to_add; - check( a.balance.quantity.amount >= 0, "insufficient funds"); - }); + if (acnts.contains(key)) { + acnts.modify( RAM_PAYER, key, [&]( auto& a ) { + a.balance += to_add; + check( a.balance.quantity.amount >= 0, "insufficient funds"); + }); + return; + } + // The first credit creates the row; a debit with no row has nothing to draw on. + check( to_add.quantity.amount > 0, "insufficient funds" ); + check( is_account( user ), "user account does not exist" ); + acnts.emplace( RAM_PAYER, key, evodex_account{ to_add } ); } } // namespace sysio diff --git a/contracts/sysio.swap/sysio.swap.wasm b/contracts/sysio.swap/sysio.swap.wasm index 16ad3075883d98d4b0d1b0448d77c3629b4246ee..031fe16f5826cb23fe17e83333b45c1a2cd73947 100755 GIT binary patch delta 10859 zcmai43t$vgmaSLS9XjcBy7K=g)lCSUkc99{kdIK|D>%dd6H!SB4WxktlE9!M4TubX z16L6hBqAz0;@|`n1qMXas3-^sWSo&WvnuQCy143$JEO~<`>H#Mj=B-6-g|Z5efQpX zfA79tvqt;+e^jqhS-(&yiXy%iITeahAyz0AYA~o&Xu)7m1kvB>y}@HgCp@TB*tmml z_TY*YJc|C-2eK0+1dpueD9+6uQL7Wx-fExJR|XrnMpjfWibD(fu-Hi@8W zNnM@4reS77^+La5=b@--08~}gE~#lyBuvEor8DdO4N3&f6+SvGOd%*nO$}ZVe-cWR zQ;CXlI-@i#+U0UN9Zq47aVx6R?bdB-tm<~V;~ep-TTjq5huiIl)OB}UyxXB_iH;~o zTzpb&QoK7ZIYo6O>8Xw+6(3{MbTvK0fxk>g68;=n_|qdXTi3Iq6S5*5k=ccKv1-n9@*Ba zID?AW)=I@%apixAsNb}ze-buDk9XDyEvODP6qjNuZl{T-Pd#03D5k#DD*|4%1U+l? zRJm6(lz{0Dn2Q#Bl@j$-IVP&6-e4$Wc{sa8y{0` z-PE(?G}M;ce84tKr8%}S&2e4qLOj%*;#k73Es-&*P-&O*XfIW9+7%toC|*D*)h?Gi zL3bDDYE2bE+lsD=*(BzB?Kz4e@-#(PXqJ13yBi3z7b?q`Ds$fnI^gz*Tsr4&baXfD zX8(Zhqkulu{>=$R&!g9LFD>qx-=zmbdj#$7T1w+$J??@La{AHt6kRx-^%NVQNjqcR z(JzTK1HAfFo4%iRjL3CXrx^mg+XDK_bTu|RuFw$p=mkYS)nO>}bsMF|^-suGhnlND z*9^^!4j7tG-K6Sv5*hI{DJCu+J7B2HK&au+htg-|-rUROUVE7`(WU579=p@98Cr=N zEz#I{QNspg`ipctE?YcFSK?+wKl=mf#B;PTzISXcB%%Pnp-zxot)v6-3F_}p((Caz ziJqh<6p3OQk}x>s>yykbq|_t=crn}od#HDmOp^jV^* zUN)&PDNppLX-V^vZir^;jEGV!3mlpWt`3;=z67|Ql-cDk=dg|X0P33DOSXghK$?*} zq;L?p_li=jML=*Y3J4Y2@y)A}UnZhOQg}cF^uhE-aKc8E)nQ}hTq z=jFF(e@Y)Qls-xsCQK?$n@)G9dJ=|WgP+~-BOCl$YHkR)48RM-Fxr(iHTtc~p_PSR zPS?}A0c3oi1UzTvB#8!^kUm%nbU0CZk4Tor2q@V#T9%qCSNh^p@k3RbCDLeihPNNg zorX1?75BU}l+F|<4t=CqDg2!nbG9my!n7zvTueCiI2`qcYX zuYJDhkhX2U(?Yh)0x^nuW#;x9t%6r6eM|)vctYkk!Tj}`ymm7<3i7g<>)*s$XNA?m z0-G|+#0EN(`KzokQktF!!~%J9w1EDz zFh!)$N7)4;m12ysXy0NKp}pSd-}{j83Tdx~VD(iEt(29WUph2Q)nCBY<82xN$U$*F zq|ui~S@O*;!9MTp*Lf?z5S!q5mqKNp-budzKJeBb#%k*P;63;yZS&k1?3>2S=V?Y+ zaEjE3&C*Foz_OM6cjD#CqYPFBIo+_-c^q^%v`CrKc_Id-_i{*NO<`pH1H3RiYyf02 z`HaqV1bSo4(LN@LF4g+~`0D1z?t`N0KDJL|t!llI331TSNC;AJ$Y z`T0o*4r{eP%1A|Wv)_hl3eU(^)?Vq zR;nJ=X+ZV?k7jnE6nAg>Yq`hS$+OWJZ<_c$eOO+U+G(_tzk{yRpS)=_wksRbRXtCf zqMco5i^(K%Mt{$4mcVY}X?@Pu?S5cc_u_Ux@Z0W-s35ne@YAiiSz->|mHW4!fZGk2 zalQ<|Ebxs)ENb?3Q`fyufAIBF54}&ZdE?vX&CBcXGfjCnq@;RntT;9UVadj(AT{i> z_vwqge(jSA^E)Q_^UI*b`|=I-@r(3Uel~3Ev;1M|M;EDQk7?@Ji?puCV($IZ>P;@_ z4TxVC4C1lZ3M52UVR7P@7a{LxNLsi=nearRsjE~~xG3f7{sG_J=pA6+dFnBV90r;+8CEpQB8JoWURlvH zT3q7~dSAM;SBCU^Bbp!SHA3{CgD@|BSR;Ix3eA&h2?f*30mKo|URG0T8<`X{6x~fK^gmDIPz;x>ZWOFr(2M_a zC8x;6AvZfuV)kJ%rqDN%crF_;+}>%jVW$WB9dXmx2%Q2V>mLOk0_6IJ~q`mVI!?QK%M;+Hl2kLn7Go8q+0?FW;4t(x8X8 zd6C%Yo-T+&K9I#}jsw!!IN(Nldw@?w(pLivZzore&?N)dN;P0^IHTydLpL;iIu#Af z4szTW&cUr6!s1>AIE;Uz1iVprTYJDuDHGBS9$6_jft6+%Mr@q9LN+$&Tp1LV0idE1 zu@vzL!)B4x^zxIZLctx{cY5GJ5bPL~A34*S0v#JT$P8r{s|J;**IuKS20bk9p~-_k zP!GLMy>7gfR=5VLuU?=duFS-nrF6c!U`Yqzh(joCx^bkt0-B~4DhGswc!EBHP=v=7?VS_RkR&Vj0~!a;T`FT_cBg4nQj-u zuf`j|=Ba=Qp<8Lgi8n)bA$^uL_OTq}V9qN#)**?&3{AIobORDxST0U=_)1k{dPH|< zv_6lvjcIgLBhotpdVuoBW<*9>C!&3=G;Ztw_3$~`ICdhxu8b`Z)f9bm6$kZjB0+cF zoauj8cx>>%Qe$jRAE>qx9VvS0TCEXXo_k=4Dco>wi;fV%2$T<0WUwk{uxH`@Bf)i~ zfjsU2B965e0j5mMMA$&_}{wcw@@c$vBWk!aG2AUUzVOIwlE^ zZp3(E4IDQ$Y)sjZwVpU57NDMZLyY$%7&=m9z#u@c^9X~#iFIo*As)CA(ji_aQ0jp) zL7%!$J4yY0_Y$SoiH4Hu3h&Obw;51@AF-$}gJWCAn=4l=sJa>Vd!biteDHm&y zW0_2WhbrcG;c;Q;pka~};_lN@h>*FCH6d=@6N{-XBO1pq1JqlmuuG@ugI}imWPzB&{QA>7o!1_ zq4~d;Gw87i@nt}bfCFJC>eIZC3!fG+qy{<+0sU(@7{<#)x|Z{IyV>}zVkNoXyh9(1 z%becX04GgB9X|CAs$V6sa(5vTx!gngL|Ap>7* zsWu+3=u&MoUNAarnkSAA-?GQwT-G;04mF}Zar^}WoQ{`TsvTCD)gK5FtYh>rV&r;w z~FnVhd4yCh@f`Ad{JWeNiLmRquE+myNDE&9ilH1waDnoWJD zmWwmAWoo+YFAPDIPV@Xk;b`4&klcXuP~3cvR!kd~$>}13_+cxEx5~3H#KxyNWH292 zo2UNqT^c>T5XANxnLrz+XNbFLSJzZJF@1>GzOB5Q3n9fEB$H(nu&8(W0I`B<%E$gh z?7o!RnTGd&=G=F*MbLZYX|&?GSQ>xJV2i!@??QoaWvHrwSU@Lk@#QifwnF7B32=w~d#aV+Y$*>2s|e%{qew zI8^W(P8V;VVjb+Lc;>_~$xYf1N&e=0B=4I!N%?|t#l=j3?zRKWM?*tG*j`|*@gxvyV;g3xZOCHZlgZ) z`-v^h)$<<^VxgocX<>>W>bX$Tb>9wy>K684LY`VUh+oGy=g`ik1p0hl7w-OjUJi|^ zA#QG6lSE(FqcDmvQsRy|q+X zpGw~@y0@$3io+~tMkwSj;Kw|!m3FaerGVeNc${dYuNE)EJ&0$03f)_`4t&SdPr@OK z`*+r_2&4VWqHJ2%aCd4uS}DW5P(X+TLfcGPOR|CN#wBIq5n8q6q$ncSigcQ}biCNv zd|+u;HTpyFhRye>(YTVhvaB1Uj<}--&A)SbN;}#`z_&(XMY;j3*YC{4o@d_0SS#-O zmB^7;zr5>d5V~tQZ_}aW-J>KixCOw3PnI)i-rau@69D=i0Isa)4#NJhG8a#=il4q! zhr~p=VcwPZ{UpRK65{Js8v(IuwN$7lR?mvST6V1Cx`#3Kz2_NGCE@;d&qK`6)-}Dv zR(f^KOcaq>gahvli%{Gl;mXzqJGiX?w_nN4Zj;=$uFV9}zprH$2K;)VxI+7W-G_#( zzZ)bRTW|0JSJpR*Pv!n^*-$B-qH7ygi+xnT@$0-!#~Rc}5j{nTYBR51fdRm3QS?M| z%*OjPZQWD^pFCktJe}XvO?w6fdGUOT6OPF`Q+)9rWo_;Ox4HYV6q>a;(eoQ?=L!|K zsf-$+CjC`{+Ob(~=kd)$L^ECAya4WE!ItdgY735E06W0o83Fxy+Oy?z z@jKdjUq!crf-AkagF>2P-AD-#Au}RPuZDQAOf@An-lzeR+LmQGekpzN;d9M*H~Ix1 z?EYwxfhq2Og5i z6(;Ico9W#hmJ(!8^n-IcZ+`RL59;Dy;Xin|=qK{EgO}9KxzcOY^AQWRCCk_UA6fGE zAIfsa&Vmq+E%fJ|`9H&BOLO;KDMGwSLm$nml-yx6s{IkLgdN16jll2!u| z91WxzR?1`uC-THq6h9i%K;jXeZa}&50)?^*3c1S!%03gw15DrB4A1oO0F!rw_U}#| zbz0gF(keHfD%mbksM7;!TuB$wGrtq2Q#P2+V?4ri$_CSUj9g7VsfkTFG@~h)bR`i%T&uf`I!K;S;Ii_XFfm=bUtZWB{)aTdtNrr3$V@CU`S9)q%iag9m|KCSU%ju^5G^gPgp*a75|;b zM`t>{5vDR)9y=pU10fSCjx#kZD?-0Wz4xx^Y%=u9-gt45-rw7jbEy*%sbL~40YEY# z+k54Su9kYcTXFLU=Lgg`ea|WpAaCPj01MPsGQDHT?qP1WTLgpHCL^eic5| zFFxdj;5yk0v8(wLEqN;Rxq!|;m8-tgO8E@@6LKl=21qykaT zWY{nBiHHiH>0kC|p~}ormu~G5dAZFaoE38o&?=UHVqJP`Y!BF09C9>xE_44WMgI#u z_e_2eCvG0@bQEF$j9C+kE!cl!eS{@JDNd=lr?Dz^xK82vBw`NM^V&*L=CCS^rCN%? z(b6l$!I>XN&VUz;0g|g0R-Lj`o9w_xTpqV@T8N(!;0y*HWXp~!rdM`31H-5+fT%;6 zNsB~=M?xm&!t6ih^BKPR=vhyC(8n#L?0n@QmpF}^aPu^D4>-BH!Jk1?sGfp}vPM{{ z@kIXpN+)ox1@W5SlTzo^fl1Ln37rog!zH_b4nSfyy84@Zd1%f6Dc~K4=Im#?sUIUy zrKQr$X9tNt(@v!Fd<(^A77L{P*8%D$@+yk2QGX(C;NaXjZu~nfelB!0Ob>||Kq5X)AJ8@hApOjaB;Gna@T&1B0@(q>*j>Qri_QGTW{^f@* zyAFiI>u2=G1A{xPafelNkd`XLD}V1s(GNK2>BnNI_W6l1I4-n%pz}65^L$1xLoK`V z>2v>m@UO2u-5d95*bTMp`t{u#9{BXtma!cJ*WcLl@&B}zVSq;eE+@E2xRJr}p0&vF z3CLh%7b8vQM@@9$MhIs8IKV;}uU~oRkwdLjKp+8|tOX{xP}ycQhu^>V%qPDH&)eIM zzD>>wf$e?ozmGm~WnJ%yK^NCwguY$kS0O9@516iBzy8LNCq6oOith$PBgfBef9;(o zU*!=%w1t_S;8M|D37p^Vd+hux_$3R<+`zdby!MIEybvIWT-n5!C%Pho67q=-?pR(= zo&PYj1|VB3D%$08_VT|U+;@rR%Bhcq7hyT@#4upng{Fr;{roEcIwatI49H~d4mb9o z(MJyL3J*OPZZw4(_lFw~g&U7j_k%@Q593EzE=A{Ga|qb)U(x+E)vlN;Pbkh+4!ZB4 zkCF~$(TfL5B5P_Jj5+w}-)tIkIDO`9|DxLZ>IS2s7Wp;)nhZs8;$2;{v~of9Y-90~ z%9@7ihC3AhvZ{HNHFNzInDQpzA_2E>Nj)alRo2wc@z)s=SM^eV`!u?As2goOlt$m; z|09Pospy4VN_ruS?tG!Jd+kzx-R!!`U)311Di>7NRQZ+RwKbIu)wMMS6IoeRXoY6g^QLnSlDWp zQq18Ty8dDk^*&ri%MUj=6mwOJ;#}>ZYljVe>{R1db=3`iqs~8PNxgq|v-_okY8)?F zxuDKpIr|R7zpT2xp`K12IZA7fj>qhK&MMA(9CY#MhUDs+`XzJbR998wN0!DM2)14U rk~(_tU`q3qW6!9x^+Xe9ti7x_Uli2qm1fsm{L~UI!)a?PP?Y}zd-wa> delta 11615 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 diff --git a/contracts/sysio.swap/token_functions.cpp b/contracts/sysio.swap/token_functions.cpp index f6f8c2f669..2c962cd549 100644 --- a/contracts/sysio.swap/token_functions.cpp +++ b/contracts/sysio.swap/token_functions.cpp @@ -20,10 +20,8 @@ void swap::transfer( const name& from, const name& to, const asset& quantity, check( quantity.symbol == st.supply.symbol, "symbol precision mismatch" ); check( memo.size() <= 256, "memo has more than 256 bytes" ); - auto payer = has_auth( to ) ? to : from; - sub_balance( from, quantity ); - add_balance( to, quantity, payer ); + add_balance( to, quantity ); if (to == get_self()) ontransfer(from, to, quantity, memo); // line added to code from eosio.token } @@ -35,40 +33,17 @@ void swap::sub_balance( const name& owner, const asset& value ) { check( from.has_value(), "no balance object found" ); check( from->balance.amount >= value.amount, "overdrawn balance" ); - from_acnts.modify( owner, key, [&]( auto& a ) { + from_acnts.modify( RAM_PAYER, key, [&]( auto& a ) { a.balance -= value; }); } -void swap::add_balance( const name& owner, const asset& value, const name& ram_payer ) +void swap::add_balance( const name& owner, const asset& value ) { accounts to_acnts( get_self(), owner.value ); - const account_key key{ value.symbol.code().raw() }; - if( !to_acnts.contains( key ) ) { - to_acnts.emplace( ram_payer, key, account{ value } ); - } else { - to_acnts.modify( name{}, key, [&]( auto& a ) { - a.balance += value; - }); - } -} - -void swap::open( const name& owner, const symbol& symbol, const name& ram_payer ) -{ - require_auth( ram_payer ); - - check( is_account( owner ), "owner account does not exist" ); - - auto sym_code_raw = symbol.code().raw(); - stats statstable( get_self() ); - const auto st = statstable.get( pair_key{ sym_code_raw }, "symbol does not exist" ); - check( st.supply.symbol == symbol, "symbol precision mismatch" ); - - accounts acnts( get_self(), owner.value ); - const account_key key{ sym_code_raw }; - if( !acnts.contains( key ) ) { - acnts.emplace( ram_payer, key, account{ asset{0, symbol} } ); - } + to_acnts.upsert( RAM_PAYER, account_key{ value.symbol.code().raw() }, + account{ value }, // default if new + [&]( auto& a ) { a.balance += value; } ); // updater if exists } void swap::close( const name& owner, const symbol& symbol ) diff --git a/contracts/sysio.token/include/sysio.token/sysio.token.hpp b/contracts/sysio.token/include/sysio.token/sysio.token.hpp index 3319f50481..6e0ca5bf45 100644 --- a/contracts/sysio.token/include/sysio.token/sysio.token.hpp +++ b/contracts/sysio.token/include/sysio.token/sysio.token.hpp @@ -72,22 +72,8 @@ namespace sysio { const asset& quantity, const string& memo ); /** - * Allows `ram_payer` to create an account `owner` with zero balance for - * token `symbol` at the expense of `ram_payer`. - * - * @param owner - the account to be created, - * @param symbol - the token to be payed with by `ram_payer`, - * @param ram_payer - the account that supports the cost of this action. - * - * More information can be read [here](https://github.com/SYSIO/sysio.contracts/issues/62) - * and [here](https://github.com/SYSIO/sysio.contracts/issues/61). - */ - [[sysio::action]] - void open( const name& owner, const symbol& symbol, const name& ram_payer ); - - /** - * This action is the opposite for open, it closes the account `owner` - * for token `symbol`. + * Erases the empty balance row of `owner` for token `symbol`. Balance rows are + * billed to `sysio` and created by the first credit. * * @param owner - the owner account to execute the close action for, * @param symbol - the symbol of the token to execute the close action for. @@ -116,7 +102,6 @@ namespace sysio { using issue_action = sysio::action_wrapper<"issue"_n, &token::issue>; using retire_action = sysio::action_wrapper<"retire"_n, &token::retire>; using transfer_action = sysio::action_wrapper<"transfer"_n, &token::transfer>; - using open_action = sysio::action_wrapper<"open"_n, &token::open>; using close_action = sysio::action_wrapper<"close"_n, &token::close>; // Account row + key types are public so foreign contracts can read @@ -152,7 +137,7 @@ namespace sysio { using stats = kv::scoped_table<"stat"_n, stat_key, currency_stats>; void sub_balance( const name& owner, const asset& value ); - void add_balance( const name& owner, const asset& value, const name& ram_payer ); + void add_balance( const name& owner, const asset& value ); }; } diff --git a/contracts/sysio.token/sysio.token.abi b/contracts/sysio.token/sysio.token.abi index 1a41eaf86e..945660e77a 100644 --- a/contracts/sysio.token/sysio.token.abi +++ b/contracts/sysio.token/sysio.token.abi @@ -87,24 +87,6 @@ } ] }, - { - "name": "open", - "base": "", - "fields": [ - { - "name": "owner", - "type": "name" - }, - { - "name": "symbol", - "type": "symbol" - }, - { - "name": "ram_payer", - "type": "name" - } - ] - }, { "name": "retire", "base": "", @@ -168,11 +150,6 @@ "type": "issue", "ricardian_contract": "" }, - { - "name": "open", - "type": "open", - "ricardian_contract": "" - }, { "name": "retire", "type": "retire", diff --git a/contracts/sysio.token/sysio.token.bad.abi b/contracts/sysio.token/sysio.token.bad.abi index 6a6ddc31ac..fe70e0b37b 100644 --- a/contracts/sysio.token/sysio.token.bad.abi +++ b/contracts/sysio.token/sysio.token.bad.abi @@ -77,24 +77,6 @@ } ] }, - { - "name": "open", - "base": "", - "fields": [ - { - "name": "owner", - "type": "name" - }, - { - "name": "symbol", - "type": "symbol" - }, - { - "name": "ram_payer", - "type": "name" - } - ] - }, { "name": "retire", "base": "", @@ -148,11 +130,6 @@ "type": "issue", "ricardian_contract": "" }, - { - "name": "open", - "type": "open", - "ricardian_contract": "" - }, { "name": "retire", "type": "retire", diff --git a/contracts/sysio.token/sysio.token.cpp b/contracts/sysio.token/sysio.token.cpp index d907bf8733..76c41496be 100644 --- a/contracts/sysio.token/sysio.token.cpp +++ b/contracts/sysio.token/sysio.token.cpp @@ -50,7 +50,7 @@ void token::issue( const name& to, const asset& quantity, const string& memo ) s.supply += quantity; }); - add_balance( st.issuer, quantity, ram_payer ); + add_balance( st.issuer, quantity ); } void token::retire( const asset& quantity, const string& memo ) @@ -96,7 +96,7 @@ void token::transfer( const name& from, check( memo.size() <= 256, "memo has more than 256 bytes" ); sub_balance( from, quantity ); - add_balance( to, quantity, ram_payer ); + add_balance( to, quantity ); } void token::sub_balance( const name& owner, const asset& value ) { @@ -107,7 +107,7 @@ void token::sub_balance( const name& owner, const asset& value ) { }); } -void token::add_balance( const name& owner, const asset& value, const name& ram_payer ) +void token::add_balance( const name& owner, const asset& value ) { accounts to_acnts( get_self(), owner.value ); to_acnts.upsert( ram_payer, acct_key{value.symbol.code().raw()}, @@ -115,23 +115,6 @@ void token::add_balance( const name& owner, const asset& value, const name& ram_ [&]( auto& a ) { a.balance += value; } ); // updater if exists } -void token::open( const name& owner, const symbol& symbol, const name& ram_payer ) -{ - require_auth( ram_payer ); - - check( is_account( owner ), "owner account does not exist" ); - - auto sym_code_raw = symbol.code().raw(); - stats statstable( get_self(), sym_code_raw ); - const auto& st = statstable.get( stat_key{sym_code_raw}, "symbol does not exist" ); - check( st.supply.symbol == symbol, "symbol precision mismatch" ); - - accounts acnts( get_self(), owner.value ); - if( !acnts.contains( acct_key{sym_code_raw} ) ) { - acnts.emplace( ram_payer, acct_key{sym_code_raw}, account{asset{0, symbol}} ); - } -} - void token::close( const name& owner, const symbol& symbol ) { require_auth( owner ); diff --git a/contracts/sysio.token/sysio.token.wasm b/contracts/sysio.token/sysio.token.wasm index 119356ed8088deb1c61e0ac21c3f0d55cbab760e..3cb63b33cb3c5ccc7d2b832527df03f4f88c9165 100755 GIT binary patch delta 1477 zcmZuxUrbw77(d@Ry_ZYxEj^S{OH1MOQlO|~zy|Gx8(i3C-Liz?gKshfV%8;N0%Dxe zLZZnYe1IGiGcyz2;xY{{OLbYuvNVK2A21r3QTO7W%=TawAN=!Rh~K#_$OAX+{l4#f z-|zgsKj+*Z&OAK1(8_XW>7ocO9$l4P5D#l?;=(MSniw4)86BHBH+f-Xd~{}%=!;u+ zu0r|r^RpwbPs|Ww+nC-qs;A$cJ~ufsIz2rxH8V0cIX*!u*`YM?n#Aih@d3;0tE%*K zu2)-Dz;EdmudcByE2!%_{)53_pw`myM=1@3JQ`dC0F40wJ;ZV#AAwO>ED|$EobQYB z59oOHnD?I}X%FGtoC21k{et+2L;R+L?Cl%gEZw5!VDmU75sL_6dh}0=@d)4enGj2 zpoYX79+Vr@CAPqH-3nf+q6{=jKsUVa|9G>U$-ta?3d=vgh(1-sc21ZH;mw#8&T^qh zxY}QS;+=s*(r=z>DP(MIfDHQxuZ(M^z)X6m<=icT&;$r7JdTy+NAh##zW~?eyT0e> z=DHdLeX=e$d^x(cE>BktL%aN}>Kfe25Bgi7g4;rK3Pg4XUaDDLCA>}L*v5>ZQWA18 z5QKg5Mj!*4d>U8*T~^k2Lxp@?a{xMIeJ~20G98?xPw&ewgFVnC9|eUnXzk0TwS;`9 zHiEGEy8Q@y7-^4nU@Lcz2%GRgH30{}J7sm~C>)eSp^u?E|2Sk*voE5GOHzyX%i6kx z+O@at_|A^E>JDNGz0qzNjZ`ACIr9FF580mC#a5;%kSy2*b-Z{sLCg@|J^{Q_0NSn)s)o%*dvBt- zGSN|)6$zOV;XTMyj3QGpA7U&17VYr4Y>7{SlfM#Y00Z(nS7^5+fF}FtPV8yje z7ZSP;kW3+FcukbV1T>i+_##OqF=!0Tv6Y1AqF|yX3X+f@_=%v#5cQn*x+O$!ws+1w z=bnGRbMEaAJH8p1SsggC*ONJt!9_EB3H-B@o3E&Cw5Cwk;$su;(TTyA<7whP`FBIc zgAzU-9~v1P8cy#^jt!0srH6>Id@zLvaZ{X{Ozle!4y97@@$}$uawJX)w5=V)YZ9;5 zghhQ`e_=rp=SDI21&Vxzj~AD4qtNH`l^TWt|D~`3t9%ChF~-WuTsl=>(XxW~t`IXr z<~`NZ;Ub)y<5bJAEM-jI{22p@eM*vTKtcR1Yhcs;>MzzY^d&Ep^j4 zl)1zh&1gsJiCl7xTCOk=EjmQ?1RMmu3L1Hp*@1o3wAEg4FoFbCUqc?WS17yPk}bts#cW<+(b*Bab#h2t|SCzKLQxpqw}%83P=Sy4s2$~Dv_DwhyJCC0d=pq5tY}rcf(2mtLh$H{ z4*a&Xgan|1S4xxDpwSu*5?_|KJIdx2Wpjd~H_YMViV5}wQ%l8VPJJ0~2rv-vbj2Lf zgG9LCZF`ln7Nj zB?0#7&^o~WgHSC3I~N+J8`NW!osZnwWMu{IRBu$Sukl!JNn(DScek8kPzdBajqizY z^KI%{Wu($0+h0`65e(fg{hW}NnB->T6l?Ne{p&b_!qAqX#rgsH$s z2p?4YQDxo#;5vQtlX>zig3~=^ud8QgGXs9Qynk@v`wOsV&i>Qu$G*P(^lrZtTBGwXyiP+E^cq zq$Ur%l-w^ylJS%nOQuEq^?j){8I4bhq5b$PKj}zP>XrJ#r6j-P@daYuV}$&AZZUhS Pemhm`8ya0_!i4+_Wy8l* diff --git a/contracts/tests/sysio.kicker_tests.cpp b/contracts/tests/sysio.kicker_tests.cpp index d35802fed4..b56b1a4409 100644 --- a/contracts/tests/sysio.kicker_tests.cpp +++ b/contracts/tests/sysio.kicker_tests.cpp @@ -110,8 +110,6 @@ BOOST_AUTO_TEST_CASE(call_pattern_independence) { BOOST_FIXTURE_TEST_CASE(twap_after_trade_and_current_supply, kicker_tester) { wait(math::day_sec); const auto old_gift = expected(); - BOOST_REQUIRE_EQUAL(success(), openext("bob"_n, "bob"_n, extended_symbol{WIRE_SYM, TOKEN_ACCOUNT})); - BOOST_REQUIRE_EQUAL(success(), openext("bob"_n, "bob"_n, extended_symbol{LIQSOL_SYM, LIQ_ACCOUNT})); BOOST_REQUIRE_EQUAL(success(), transfer_wire(SYSIO_ACCOUNT, "bob"_n, seed)); BOOST_REQUIRE_EQUAL(success(), transfer_wire("bob"_n, SWAP_ACCOUNT, seed)); BOOST_REQUIRE_EQUAL(success(), exchange("bob"_n, POOL_SYM, extended_asset{asset(seed, WIRE_SYM), TOKEN_ACCOUNT}, @@ -137,8 +135,6 @@ BOOST_FIXTURE_TEST_CASE(twap_after_trade_and_current_supply, kicker_tester) { BOOST_FIXTURE_TEST_CASE(large_same_block_trade_cannot_amplify_gift, kicker_tester) { constexpr uint64_t attack = 10'000'000 * UNIT; wait(3 * math::day_sec); - BOOST_REQUIRE_EQUAL(success(), openext("bob"_n, "bob"_n, extended_symbol{WIRE_SYM, TOKEN_ACCOUNT})); - BOOST_REQUIRE_EQUAL(success(), openext("bob"_n, "bob"_n, extended_symbol{LIQSOL_SYM, LIQ_ACCOUNT})); BOOST_REQUIRE_EQUAL(success(), transfer_wire(SYSIO_ACCOUNT, "bob"_n, attack)); BOOST_REQUIRE_EQUAL(success(), transfer_wire("bob"_n, SWAP_ACCOUNT, attack)); const auto baseline = expected(); @@ -743,8 +739,6 @@ BOOST_AUTO_TEST_CASE(one_block_spike_cashed_in_one_block_pays_the_inflated_gift) BOOST_REQUIRE_EQUAL(t.success(), t.change(200, 1, cap)); BOOST_REQUIRE_EQUAL(t.success(), t.configure(t.budget, 1, t.SYSIO_ACCOUNT, math::min_twap_window_sec, tightest)); - BOOST_REQUIRE_EQUAL(t.success(), t.openext("bob"_n, "bob"_n, extended_symbol{t.WIRE_SYM, t.TOKEN_ACCOUNT})); - BOOST_REQUIRE_EQUAL(t.success(), t.openext("bob"_n, "bob"_n, extended_symbol{t.LIQSOL_SYM, t.LIQ_ACCOUNT})); BOOST_REQUIRE_EQUAL(t.success(), t.transfer_wire(t.SYSIO_ACCOUNT, "bob"_n, pump)); BOOST_REQUIRE_EQUAL(t.success(), t.transfer_wire("bob"_n, t.SWAP_ACCOUNT, pump)); t.wait(math::min_twap_window_sec); @@ -871,8 +865,6 @@ BOOST_FIXTURE_TEST_CASE(pump_straddling_a_checkpoint_funds_no_payout, kicker_tes }; BOOST_REQUIRE_EQUAL(success(), configure(budget, 1, SYSIO_ACCOUNT, window)); BOOST_REQUIRE_EQUAL(100'000 * UNIT, supply()); - BOOST_REQUIRE_EQUAL(success(), openext("bob"_n, "bob"_n, extended_symbol{WIRE_SYM, TOKEN_ACCOUNT})); - BOOST_REQUIRE_EQUAL(success(), openext("bob"_n, "bob"_n, extended_symbol{LIQSOL_SYM, LIQ_ACCOUNT})); BOOST_REQUIRE_EQUAL(success(), transfer_wire(SYSIO_ACCOUNT, "bob"_n, pump)); BOOST_REQUIRE_EQUAL(success(), transfer_wire("bob"_n, SWAP_ACCOUNT, pump)); diff --git a/contracts/tests/sysio.liq_tests.cpp b/contracts/tests/sysio.liq_tests.cpp index 2951bc31f5..46167b3f87 100644 --- a/contracts/tests/sysio.liq_tests.cpp +++ b/contracts/tests/sysio.liq_tests.cpp @@ -115,15 +115,14 @@ BOOST_FIXTURE_TEST_CASE(settle_before_mutate_conserves_every_subunit, sysio_liq_ BOOST_REQUIRE_EQUAL(0u, pot()); BOOST_REQUIRE_EQUAL(0, owed("alice"_n)); - // A row opened after the index moved is stamped, not zeroed: nothing is owed, - // claiming sends nothing, and a later credit accrues only from here. - BOOST_REQUIRE_EQUAL(success(), open("dave"_n, LIQSOL_SYM, "dave"_n)); + // A row created after the index moved is stamped, not zeroed: nothing is owed, + // claiming sends nothing, and later yield accrues only from here. + BOOST_REQUIRE_EQUAL(success(), mint("dave"_n, 100 * UNIT)); BOOST_REQUIRE_EQUAL(index(), account_row("dave"_n)["index_checkpoint"].as_uint128()); + BOOST_REQUIRE_EQUAL(0, owed("dave"_n)); const int64_t dave_before = wire_balance("dave"_n); BOOST_REQUIRE_EQUAL(success(), claim("dave"_n)); BOOST_REQUIRE_EQUAL(dave_before, wire_balance("dave"_n)); - BOOST_REQUIRE_EQUAL(success(), mint("dave"_n, 100 * UNIT)); - BOOST_REQUIRE_EQUAL(0, owed("dave"_n)); BOOST_REQUIRE_EQUAL(success(), addyield("carol"_n, 50 * UNIT)); // 500 supply: dave holds a fifth BOOST_REQUIRE_EQUAL(10 * UNIT, owed("dave"_n)); @@ -192,7 +191,6 @@ BOOST_FIXTURE_TEST_CASE(the_index_grows_past_64_bits, sysio_liq_tester) try { BOOST_FIXTURE_TEST_CASE(addyield_distributes_only_supplied_wire, sysio_liq_tester) try { constexpr int64_t intake = 100 * UNIT; BOOST_REQUIRE_EQUAL(success(), mint("alice"_n, 100 * UNIT)); - BOOST_REQUIRE_EQUAL(success(), openext("alice"_n, "alice"_n, extended_symbol{WIRE_SYM, TOKEN_ACCOUNT})); BOOST_REQUIRE_EQUAL(success(), transfer_wire("alice"_n, SWAP_ACCOUNT, intake)); const auto treasury_before = wire_balance(SYSIO_ACCOUNT); const auto holder_before = wire_balance("alice"_n); @@ -328,11 +326,6 @@ BOOST_FIXTURE_TEST_CASE(a_holder_can_sell_shadow_into_the_yield_pool_for_wire, s BOOST_REQUIRE_EQUAL(success(), mint("alice"_n, 100 * UNIT)); BOOST_REQUIRE_EQUAL(success(), regliqpool(SOLANA, LIQSOL, POOL_SYM, 1000 * UNIT, 1000 * UNIT)); - const extended_symbol shadow_ext{ LIQSOL_SYM, LIQ_ACCOUNT }; - const extended_symbol wire_ext{ WIRE_SYM, TOKEN_ACCOUNT }; - BOOST_REQUIRE_EQUAL(success(), openext("alice"_n, "alice"_n, shadow_ext)); - BOOST_REQUIRE_EQUAL(success(), openext("alice"_n, "alice"_n, wire_ext)); - constexpr int64_t sold = 50 * UNIT; BOOST_REQUIRE_EQUAL(success(), transfer_shadow("alice"_n, SWAP_ACCOUNT, sold)); BOOST_REQUIRE_EQUAL(50 * UNIT, shadow_balance("alice"_n)); diff --git a/contracts/tests/sysio.swap_tests.cpp b/contracts/tests/sysio.swap_tests.cpp index aee3f3e11e..c529651fc8 100644 --- a/contracts/tests/sysio.swap_tests.cpp +++ b/contracts/tests/sysio.swap_tests.cpp @@ -110,8 +110,11 @@ class sysio_swap_tester : public tester { set_abi( "anothertoken"_n, contracts::token_abi().data() ); set_privileged( "anothertoken"_n ); + // Privileged like the deployment makes it (sysio.roa::setsyscode): its + // pair-token balance rows are billed to sysio. set_code( "sysio.swap"_n, contracts::swap_wasm() ); set_abi( "sysio.swap"_n, contracts::swap_abi().data() ); + set_privileged( "sysio.swap"_n ); set_code( "carol"_n, contracts::token_wasm() ); set_abi( "carol"_n, contracts::token_abi().data() ); @@ -225,20 +228,6 @@ class sysio_swap_tester : public tester { ( "memo", memo) ); } - action_result open( name owner, symbol sym, name ram_payer ) { - return push_action( "sysio.swap"_n, owner, "open"_n, mvo() - ( "owner", owner) - ( "symbol", sym) - ( "ram_payer", ram_payer) - ); - } - action_result openext( name user, name payer, extended_symbol ext_symbol ) { - return push_action( "sysio.swap"_n, payer, "openext"_n, mvo() - ( "user", user) - ( "payer", payer) - ( "ext_symbol", ext_symbol) - ); - } action_result closeext ( const name user, const name to, const extended_symbol ext_symbol ){ return push_action( "sysio.swap"_n, user, "closeext"_n, mvo() ( "user", user ) @@ -267,10 +256,10 @@ class sysio_swap_tester : public tester { action_result inittoken( name user, symbol new_symbol, extended_asset initial_pool1, extended_asset initial_pool2, int initial_fee, name fee_authority, asset locked_shares, std::optional yield_leg = std::nullopt ){ - // inittoken bills the new rows to `user`, so the action must carry the - // user's sysio.payer permission in addition to the two active authorities. + // inittoken needs the user's and the contract's authority; the pair's rows + // are billed to sysio. return push_swap_action( "inittoken"_n, - { {user, config::sysio_payer_name}, {user, config::active_name}, {"sysio.swap"_n, config::active_name} }, + { {user, config::active_name}, {"sysio.swap"_n, config::active_name} }, mvo() ( "user", user) ("new_symbol", new_symbol) @@ -306,10 +295,9 @@ class sysio_swap_tester : public tester { vector pending_payout( name contract ) { return get_kv_row( "sysio.swap"_n, "yieldpayouts"_n, { contract.to_uint64_t() } ); } - // fundyield is the funder's own call; the fixture adds its payer permission, - // which the row is billed to. + // fundyield is the funder's own call; its row is billed to sysio. action_result fundyield( name from, symbol_code pair_token, asset quantity ) { - return push_action( "sysio.swap"_n, from, "fundyield"_n, mvo() + return push_swap_action( "fundyield"_n, { {from, config::active_name} }, mvo() ( "from", from )( "pair_token", pair_token )( "quantity", quantity ) ); } @@ -320,6 +308,18 @@ class sysio_swap_tester : public tester { int64_t ram_usage( name account ) const { return control->get_resource_limits_manager().get_account_ram_usage( account ); } + // The account billed for `user`'s pair-token balance row of `pair_token`. + name balance_payer( name user, symbol_code pair_token ) const { + return row_payer( "accounts"_n, { user.to_uint64_t(), pair_token.value } ); + } + // The account billed for `user`'s deposit row of `ext`. + name deposit_payer( name user, const extended_symbol& ext ) const { + return row_payer( "evodexacnts"_n, { user.to_uint64_t(), ext.contract.to_uint64_t(), ext.sym.value() } ); + } + action_result send_pair_token( name from, name to, asset quantity ) { + return push_action( "sysio.swap"_n, from, "transfer"_n, mvo() + ( "from", from )( "to", to )( "quantity", quantity )( "memo", "" ) ); + } // The pending funding announced by `funder`, as a variant; null when none. fc::variant pending_funding( name funder ) { const auto data = get_kv_row( "sysio.swap"_n, "yieldfunds"_n, { funder.to_uint64_t() } ); @@ -346,9 +346,9 @@ class sysio_swap_tester : public tester { action announce; announce.account = "sysio.swap"_n; announce.name = "fundyield"_n; - // The announcement's row is billed to the funder, so it carries the payer - // permission as well as the active one. - announce.authorization = { {from, config::sysio_payer_name}, {from, config::active_name} }; + // The announcement's row is billed to sysio, so it carries only the funder's + // active permission. + announce.authorization = { {from, config::active_name} }; announce.data = abi_ser.variant_to_binary( "fundyield", mvo()( "from", from )( "pair_token", pair )( "quantity", quantity ), fixture_abi_yield_function() ); @@ -518,8 +518,13 @@ class sysio_swap_tester : public tester { const auto data = get_kv_row( "sysio.liq"_n, "yieldidx"_n, { sym.value } ); return data.empty() ? shadow_index_row{ 0, 0, 0 } : fc::raw::unpack( data ); } - // A user's deposit of any extended symbol, resolved without `extend`. + // A user's deposit of any extended symbol, resolved without `extend`; 0 without a row. int64_t deposit_of( name user, const extended_symbol& ext ); + // Whether `user` has a deposit row for `ext`. + bool has_deposit( name user, const extended_symbol& ext ) { + return !get_kv_row( "sysio.swap"_n, "evodexacnts"_n, + { user.to_uint64_t(), ext.contract.to_uint64_t(), ext.sym.value() } ).empty(); + } action_result addliquidity(name user, asset to_buy, asset max_asset1, asset max_asset2) { return push_action( "sysio.swap"_n, user, "addliquidity"_n, mvo() ( "user", user ) @@ -601,14 +606,24 @@ class sysio_swap_tester : public tester { // Raw KV read of a composite-keyed row: every key word big-endian, in order // (for a scoped table the scope is the first word). Empty when absent. - vector get_kv_row( name code, name table, std::initializer_list key_words ) { + static std::string kv_key( std::initializer_list key_words ) { std::string key; for (uint64_t word : key_words) for (int shift = 56; shift >= 0; shift -= 8) key.push_back( char((word >> shift) & 0xff) ); - const auto row = control->kv().find_row( code, chain::compute_table_id(table.to_uint64_t()), key ); + return key; + } + vector get_kv_row( name code, name table, std::initializer_list key_words ) { + const auto row = control->kv().find_row( code, chain::compute_table_id(table.to_uint64_t()), kv_key( key_words ) ); if (!row) return {}; return vector( row->value.begin(), row->value.end() ); } + // The account billed for a sysio.swap row, keyed as get_kv_row keys it; the test fails without the row. + name row_payer( name table, std::initializer_list key_words ) const { + const auto row = control->kv().find_row( "sysio.swap"_n, chain::compute_table_id(table.to_uint64_t()), + kv_key( key_words ) ); + BOOST_REQUIRE_MESSAGE( row.has_value(), "no " << table << " row" ); + return row->payer; + } // A user's deposit of `sym`, with the token contract resolved as `extend` does: // the evodexacnts row keyed by the extended symbol, scoped by the user. // Declared here, defined after `extend`. @@ -655,14 +670,6 @@ class sysio_swap_tester : public tester { BOOST_REQUIRE_EQUAL( success(), issue( "anothertoken"_n, "bob"_n, "bob"_n, asset::from_string("461168601842738.7903 VOICE"), "") ); BOOST_REQUIRE_EQUAL( success(), issue( "sysio.token"_n, "alice"_n, "alice"_n, asset::from_string("46116860184273879.03 TUSD"), "") ); } - void many_openext() { - BOOST_REQUIRE_EQUAL( success(), openext( "alice"_n, "alice"_n, system_token ) ); - BOOST_REQUIRE_EQUAL( success(), openext( "alice"_n, "alice"_n, extended_symbol{symbol::from_string("4,VOICE"), "anothertoken"_n}) ); - BOOST_REQUIRE_EQUAL( success(), openext( "alice"_n, "alice"_n, extended_symbol{symbol::from_string("2,TUSD"), "sysio.token"_n}) ); - BOOST_REQUIRE_EQUAL( success(), openext( "bob"_n, "alice"_n, system_token ) ); - BOOST_REQUIRE_EQUAL( success(), openext( "bob"_n, "alice"_n, extended_symbol{symbol::from_string("4,VOICE"), "anothertoken"_n}) ); - BOOST_REQUIRE_EQUAL( success(), openext( "bob"_n, "alice"_n, extended_symbol{symbol::from_string("2,TUSD"), "sysio.token"_n}) ); - } // The deposits the pools are seeded from. The system token is always // accepted; VOICE and TUSD have no pair yet, so their deposits carry the // contract's authority. @@ -867,7 +874,7 @@ vector sysio_swap_tester::total() { int64_t sysio_swap_tester::deposit_of( name user, const extended_symbol& ext ) { const auto data = get_kv_row( "sysio.swap"_n, "evodexacnts"_n, { user.to_uint64_t(), ext.contract.to_uint64_t(), ext.sym.value() } ); - BOOST_REQUIRE_MESSAGE( !data.empty(), "no deposit row for " << user << " " << ext.sym ); + if (data.empty()) return 0; const auto row = abi_ser.binary_to_variant( "evodex_account", data, fixture_abi_yield_function() ); return to_int( fc::json::to_string( row["balance"]["quantity"], @@ -893,11 +900,9 @@ struct sysio_swap_yield_tester : public sysio_swap_tester { void setup_yield_pool() { create_tokens_and_issue(); abi_ser.set_abi( swap_abi_def(), fixture_abi_yield_function() ); - many_openext(); many_transfer(); BOOST_REQUIRE_EQUAL( success(), shadow_mint( "alice"_n, asset( ShadowIssuance, SHD4 ) ) ); BOOST_REQUIRE_EQUAL( success(), transfer( "sysio.token"_n, "alice"_n, "bob"_n, asset( BobDonationBudget, WIRE9 ), "" ) ); - BOOST_REQUIRE_EQUAL( success(), openext( "alice"_n, "alice"_n, SHADOW ) ); BOOST_REQUIRE_EQUAL( success(), seed_transfer( "sysio.liq"_n, "alice"_n, asset( YieldPoolShadow, SHD4 ), "" ) ); BOOST_REQUIRE_EQUAL( success(), inittoken( "alice"_n, SHEO6, shd( YieldPoolShadow ), extend( asset( YieldPoolWire, WIRE9 ) ), 10, name{}, 0, SHADOW ) ); @@ -936,7 +941,6 @@ struct sysio_swap_yield_tester : public sysio_swap_tester { const int64_t front_run = YieldPoolShadow / 100; BOOST_REQUIRE_LT( 0, yield_reference::sandwich_gain( YieldPoolShadow, YieldPoolWire, fee, uint32_t(fee + 1), front_run ) ); - BOOST_REQUIRE_EQUAL( success(), openext( "bob"_n, "bob"_n, SHADOW ) ); BOOST_REQUIRE_EQUAL( success(), shadow_transfer( "alice"_n, "bob"_n, asset( front_run, SHD4 ), "" ) ); BOOST_REQUIRE_EQUAL( success(), shadow_transfer( "bob"_n, "sysio.swap"_n, asset( front_run, SHD4 ), "" ) ); // Settle what the pool is owed first, so the front-run is priced against @@ -971,7 +975,6 @@ int64_t sysio_swap_tester::settle_swap( name user, symbol_code pair, asset in, s void sysio_swap_tester::setup_pools() { create_tokens_and_issue(); abi_ser.set_abi( swap_abi_def(), fixture_abi_yield_function() ); - many_openext(); many_transfer(); BOOST_REQUIRE_EQUAL( success(), transfer( "sysio.token"_n, "alice"_n, "sysio.swap"_n, asset::from_string("168601842738.7903 EOS"), "") ); @@ -997,9 +1000,6 @@ BOOST_FIXTURE_TEST_CASE( add_rem_liquidity, sysio_swap_tester ) try { transfer( "anothertoken"_n, "bob"_n, "alice"_n, asset::from_string("500000000.0000 VOICE"), ""); abi_ser.set_abi(abi_evo, fixture_abi_yield_function()); - - many_openext(); - transfer( "sysio.token"_n, "alice"_n, "sysio.swap"_n, asset::from_string("10000000.0000 EOS"), ""); seed_transfer( "anothertoken"_n, "alice"_n, asset::from_string("200000000.0000 VOICE"), ""); @@ -1098,7 +1098,6 @@ BOOST_FIXTURE_TEST_CASE( exchange_action, sysio_swap_tester ) try { create_tokens_and_issue(); transfer( "anothertoken"_n, "bob"_n, "alice"_n, asset::from_string("500000000.0000 VOICE"), ""); abi_ser.set_abi(abi_evo, fixture_abi_yield_function()); - many_openext(); transfer( "sysio.token"_n, "alice"_n, "sysio.swap"_n, asset::from_string("10000000.0000 EOS"), ""); seed_transfer( "anothertoken"_n, "alice"_n, asset::from_string("200000000.0000 VOICE"), ""); inittoken( "alice"_n, EVO4, @@ -1187,7 +1186,6 @@ BOOST_FIXTURE_TEST_CASE( increasing_poolvalue, sysio_swap_tester) try { create_tokens_and_issue(); abi_ser.set_abi(abi_evo, fixture_abi_yield_function()); - many_openext(); many_transfer(); transfer( "sysio.token"_n, "alice"_n, "sysio.swap"_n, asset::from_string("168601842738.7903 EOS"), ""); @@ -1298,8 +1296,6 @@ BOOST_FIXTURE_TEST_CASE( memoexchange_test, sysio_swap_tester ) try { transfer( "anothertoken"_n, "bob"_n, "alice"_n, asset::from_string("0.0001 VOICE"), ""); abi_ser.set_abi(abi_evo, fixture_abi_yield_function()); - - many_openext(); many_transfer(); BOOST_REQUIRE_EQUAL( success(), @@ -1405,28 +1401,6 @@ BOOST_FIXTURE_TEST_CASE( the_other_actions, sysio_swap_tester ) try { BOOST_REQUIRE_EQUAL(abi_serializer::to_abi(accnt2->abi, abi_evo), true); abi_ser.set_abi(abi_evo, fixture_abi_yield_function()); - // add_signed_ext_balance - BOOST_REQUIRE_EQUAL( wasm_assert_msg("extended_symbol not registered for this user,\ - please run openext action or write exchange details in the memo of your transfer"), - transfer( "sysio.token"_n, "alice"_n, "sysio.swap"_n, asset::from_string("0.1000 EOS"), "") ); - - // OPENEXT - BOOST_REQUIRE_EQUAL( error("missing authority of natalia"), - push_action( "sysio.swap"_n, "bob"_n, "openext"_n, mvo() - ( "user", "bob"_n)( "payer", "natalia"_n ) - ( "ext_symbol", extended_symbol{VOICE4, "anothertoken"_n} )) - ); - BOOST_REQUIRE_EQUAL( success(), openext( "alice"_n, "alice"_n, - extended_symbol{EOS4, "sysio.token"_n}) ); - BOOST_REQUIRE_EQUAL( success(), openext( "alice"_n, "alice"_n, - extended_symbol{VOICE4, "anothertoken"_n}) ); - BOOST_REQUIRE_EQUAL( success(), openext( "alice"_n, "alice"_n, - extended_symbol{VOICE4, "anothertoken"_n}) ); - BOOST_REQUIRE_EQUAL( wasm_assert_msg( "user account does not exist"), - openext( "cat"_n, "alice"_n, extended_symbol{VOICE4, "anothertoken"_n}) ); - BOOST_REQUIRE_EQUAL( success(), openext( "alice"_n, "alice"_n, - extended_symbol{VOICE4, "anothertoken"_n}) ); - // ONTRANSFER BOOST_REQUIRE_EQUAL( wasm_assert_msg("This transfer is not for sysio.swap"), transfer( "badtoken"_n, "alice"_n, "bob"_n, asset::from_string("1000.0000 EOS"), "")); @@ -1434,8 +1408,14 @@ BOOST_FIXTURE_TEST_CASE( the_other_actions, sysio_swap_tester ) try { transfer( "badtoken"_n, "alice"_n, "sysio.swap"_n, asset::from_string("-1000.0000 EOS"), "")); BOOST_REQUIRE_EQUAL( wasm_assert_msg("Donation not accepted"), transfer( "sysio.token"_n, "alice"_n, "sysio.swap"_n, asset::from_string("1000.0000 EOS"), "deposit to: sysio.swap") ); + // The first credit creates the deposit row; a deposit for an account that does not + // exist is refused. + BOOST_REQUIRE_EQUAL( wasm_assert_msg("user account does not exist"), transfer( "sysio.token"_n, "alice"_n, + "sysio.swap"_n, asset::from_string("1000.0000 EOS"), "deposit to: cat") ); + BOOST_REQUIRE( !has_deposit( "alice"_n, extended_symbol{EOS4, "sysio.token"_n} ) ); BOOST_REQUIRE_EQUAL( success(), transfer( "sysio.token"_n, "alice"_n, "sysio.swap"_n, asset::from_string("1000.0000 EOS"), "") ); + BOOST_REQUIRE_EQUAL( 1000'0000, deposit_of( "alice"_n, extended_symbol{EOS4, "sysio.token"_n} ) ); // Only the system token and the first legs of existing pairs are accepted; // a first leg's seed gets in ahead of its pair only with the contract's authority. BOOST_REQUIRE_EQUAL( wasm_assert_msg("token is not a leg of any pair"), transfer( "anothertoken"_n, "alice"_n, "sysio.swap"_n, @@ -1456,6 +1436,10 @@ BOOST_FIXTURE_TEST_CASE( the_other_actions, sysio_swap_tester ) try { extend(asset::from_string("-0.0001 EOS")) )); BOOST_REQUIRE_EQUAL( wasm_assert_msg("insufficient funds"), withdraw( "alice"_n, "bob"_n, extend(asset::from_string("0.0003 EOS")) )); + // A debit with no deposit row has nothing to draw on. + BOOST_REQUIRE( !has_deposit( "bob"_n, extended_symbol{EOS4, "sysio.token"_n} ) ); + BOOST_REQUIRE_EQUAL( wasm_assert_msg("insufficient funds"), withdraw( "bob"_n, "alice"_n, + extend(asset::from_string("0.0001 EOS")) )); // INITTOKEN BOOST_REQUIRE_EQUAL( error("missing authority of sysio.swap"), @@ -1562,7 +1546,6 @@ struct sysio_swap_unconfigured_tester : public sysio_swap_tester { BOOST_FIXTURE_TEST_CASE( nothing_works_before_setconfig, sysio_swap_unconfigured_tester ) try { create_tokens_and_issue(); abi_ser.set_abi( swap_abi_def(), fixture_abi_yield_function() ); - many_openext(); // Without a system token nothing can be deposited, with or without the // contract's authority, and no pair can be created, whoever its authority is. BOOST_REQUIRE_EQUAL( wasm_assert_msg("swap not configured"), @@ -1596,7 +1579,6 @@ BOOST_FIXTURE_TEST_CASE( nothing_works_before_setconfig, sysio_swap_unconfigured BOOST_FIXTURE_TEST_CASE( seed_locks_shares, sysio_swap_tester ) try { create_tokens_and_issue(); abi_ser.set_abi( swap_abi_def(), fixture_abi_yield_function() ); - many_openext(); many_transfer(); const int64_t minted = 100'000'000'000; // sqrt(1e10 * 1e12) const int64_t locked = 5'000'000; @@ -1649,7 +1631,6 @@ BOOST_FIXTURE_TEST_CASE( seed_locks_shares, sysio_swap_tester ) try { BOOST_FIXTURE_TEST_CASE( yield_leg_rules, sysio_swap_tester ) try { create_tokens_and_issue(); abi_ser.set_abi( swap_abi_def(), fixture_abi_yield_function() ); - many_openext(); many_transfer(); const extended_symbol voice{ VOICE4, "anothertoken"_n }; const extended_symbol tusd{ TUSD2, "sysio.token"_n }; @@ -1946,13 +1927,10 @@ BOOST_FIXTURE_TEST_CASE( yield_accrues_before_a_swap_is_priced, sysio_swap_yield BOOST_FIXTURE_TEST_CASE( yield_payout_route_is_exact, sysio_swap_yield_tester ) try { setup_yield_pool(); // A transfer from a shadow contract with no claim outstanding is an ordinary - // deposit, and needs the ordinary deposit row: the payout route only exists - // while a receipt does. + // deposit, which creates its deposit row: the payout route only exists while a + // receipt does. BOOST_REQUIRE_EQUAL( success(), shadow_mint( "sysio.liq"_n, asset( 1'0000, SHD4 ) ) ); - BOOST_REQUIRE_EQUAL( wasm_assert_msg("extended_symbol not registered for this user," - " please run openext action or write exchange details in the memo of your transfer"), - shadow_transfer( "sysio.liq"_n, "sysio.swap"_n, asset( 1'0000, SHD4 ), "" ) ); - BOOST_REQUIRE_EQUAL( success(), openext( "sysio.liq"_n, "alice"_n, SHADOW ) ); + BOOST_REQUIRE( !has_deposit( "sysio.liq"_n, SHADOW ) ); BOOST_REQUIRE_EQUAL( success(), shadow_transfer( "sysio.liq"_n, "sysio.swap"_n, asset( 1'0000, SHD4 ), "" ) ); BOOST_REQUIRE_EQUAL( 1'0000, deposit_of( "sysio.liq"_n, SHADOW ) ); BOOST_REQUIRE_EQUAL( YieldPoolWire, system_balance( SHEO.value ).at(1) ); @@ -1994,7 +1972,11 @@ BOOST_FIXTURE_TEST_CASE( yield_funding_fills_the_reservoir, sysio_swap_yield_tes // untouched, and the delivery goes to the reservoir. const int64_t first = 100'0000; const int64_t alice_deposit = deposit_of( "alice"_n, SHADOW ); + const int64_t alice_ram = ram_usage( "alice"_n ); BOOST_REQUIRE_EQUAL( success(), fundyield( "alice"_n, SHEO, asset( first, SHD4 ) ) ); + // The announcement is billed to sysio, not to the funder. + BOOST_REQUIRE_EQUAL( config::system_account_name, row_payer( "yieldfunds"_n, { "alice"_n.to_uint64_t() } ) ); + BOOST_REQUIRE_EQUAL( alice_ram, ram_usage( "alice"_n ) ); auto pending = pending_funding( "alice"_n ); BOOST_REQUIRE_EQUAL( SHEO6.name(), pending["pair"].as_string() ); BOOST_REQUIRE_EQUAL( asset( first, SHD4 ).to_string(), pending["quantity"]["quantity"].as_string() ); @@ -2028,14 +2010,15 @@ BOOST_FIXTURE_TEST_CASE( yield_funding_fills_the_reservoir, sysio_swap_yield_tes BOOST_REQUIRE_EQUAL( first + second + third, reservoir_of( SHEO ) ); BOOST_REQUIRE( pending_funding( "alice"_n ).is_null() ); - // The announcement's row is the funder's, not the contract's, and cancelling - // gives it back. Otherwise one abandoned announcement per account that ever - // called fundyield would sit on the contract's RAM with no way to reclaim it. + // The announcement's row is billed to sysio, not to the funder or the contract, + // and cancelling frees it. { - const int64_t swap_ram = ram_usage( "sysio.swap"_n ); - const int64_t bob_ram = ram_usage( "bob"_n ); + const int64_t sysio_ram = ram_usage( config::system_account_name ); + const int64_t swap_ram = ram_usage( "sysio.swap"_n ); + const int64_t bob_ram = ram_usage( "bob"_n ); BOOST_REQUIRE_EQUAL( success(), fundyield( "bob"_n, SHEO, asset( 5'0000, SHD4 ) ) ); - BOOST_REQUIRE_LT( bob_ram, ram_usage( "bob"_n ) ); + BOOST_REQUIRE_LT( sysio_ram, ram_usage( config::system_account_name ) ); + BOOST_REQUIRE_EQUAL( bob_ram, ram_usage( "bob"_n ) ); BOOST_REQUIRE_EQUAL( swap_ram, ram_usage( "sysio.swap"_n ) ); // While it is pending bob cannot deposit anything else... BOOST_REQUIRE_EQUAL( wasm_assert_msg("yield funding does not match the pending fundyield"), @@ -2044,6 +2027,7 @@ BOOST_FIXTURE_TEST_CASE( yield_funding_fills_the_reservoir, sysio_swap_yield_tes BOOST_REQUIRE_EQUAL( error("missing authority of bob"), cancelyield( "bob"_n, "alice"_n ) ); BOOST_REQUIRE_EQUAL( success(), cancelyield( "bob"_n ) ); BOOST_REQUIRE( pending_funding( "bob"_n ).is_null() ); + BOOST_REQUIRE_EQUAL( sysio_ram, ram_usage( config::system_account_name ) ); BOOST_REQUIRE_EQUAL( bob_ram, ram_usage( "bob"_n ) ); BOOST_REQUIRE_EQUAL( wasm_assert_msg("no pending fundyield"), cancelyield( "bob"_n ) ); // Ordinary deposits work again. @@ -2528,9 +2512,7 @@ BOOST_FIXTURE_TEST_CASE( fee_authority_configuration, sysio_swap_tester ) try { BOOST_REQUIRE_EQUAL( success(), create( "sysio.token"_n, "alice"_n, asset::from_string("1000.0000 CVO") ) ); BOOST_REQUIRE_EQUAL( success(), issue( "sysio.token"_n, "alice"_n, "alice"_n, asset::from_string("1000.0000 CVO"), "" ) ); abi_ser.set_abi( swap_abi_def(), fixture_abi_yield_function() ); - many_openext(); many_transfer(); - BOOST_REQUIRE_EQUAL( success(), openext( "alice"_n, "alice"_n, extended_symbol{ CVO4, "sysio.token"_n } ) ); BOOST_REQUIRE_EQUAL( success(), seed_transfer( "sysio.token"_n, "alice"_n, asset::from_string("1000.0000 CVO"), "" ) ); // EVO adopts the configured authority (sysio); ETUSD names alice as its own. @@ -2569,7 +2551,6 @@ BOOST_FIXTURE_TEST_CASE( indextable, sysio_swap_tester ) try { abi_def abi_evo; BOOST_REQUIRE_EQUAL(abi_serializer::to_abi(accnt2->abi, abi_evo), true); abi_ser.set_abi(abi_evo, fixture_abi_yield_function()); - many_openext(); transfer( "sysio.token"_n, "alice"_n, "sysio.swap"_n, asset::from_string("10000000.0000 EOS"), ""); seed_transfer( "anothertoken"_n, "alice"_n, asset::from_string("200000000.0000 VOICE"), ""); @@ -2601,11 +2582,10 @@ BOOST_FIXTURE_TEST_CASE( indextable, sysio_swap_tester ) try { BOOST_REQUIRE( get_kv_row( "sysio.swap"_n, "evoindex"_n, { "sysio.token"_n.to_uint64_t(), EOS4.value(), "anothertoken"_n.to_uint64_t(), VOICE4.value() } ).empty() ); - // A first leg with a different precision is a different token: it has no - // deposit row. A second leg that is not exactly the system token, by + // A first leg with a different precision is a different token: alice has none + // of it on deposit. A second leg that is not exactly the system token, by // precision or by contract, is not a pair. - BOOST_REQUIRE_EQUAL(wasm_assert_msg("extended_symbol not registered for this user,\ - please run openext action or write exchange details in the memo of your transfer"), + BOOST_REQUIRE_EQUAL(wasm_assert_msg("insufficient funds"), inittoken( "alice"_n, EOS4, extend(asset::from_string("1.00000 VOICE")), extend(asset::from_string("1.0000 EOS")), 10, name{}) ); @@ -2898,7 +2878,6 @@ BOOST_FIXTURE_TEST_CASE( precision_extremes, sysio_swap_tester ) try { // trades can be pushed to the int64 asset ceiling with no external limit. create_tokens_and_issue(); abi_ser.set_abi( swap_abi_def(), fixture_abi_yield_function() ); - many_openext(); const asset all_eos = asset::from_string("461168601842738.7903 EOS"); const asset all_voice = asset::from_string("461168601842738.7903 VOICE"); const asset all_tusd = asset::from_string("46116860184273879.03 TUSD"); @@ -3000,7 +2979,6 @@ BOOST_FIXTURE_TEST_CASE( minimum_fee_closes_the_free_window, sysio_swap_tester ) // 1000.0000 EOS against 1000.0000 VOICE, 10 bps. create_tokens_and_issue(); abi_ser.set_abi( swap_abi_def(), fixture_abi_yield_function() ); - many_openext(); BOOST_REQUIRE_EQUAL( success(), transfer( "sysio.token"_n, "alice"_n, "sysio.swap"_n, asset::from_string("2000.0000 EOS"), "") ); BOOST_REQUIRE_EQUAL( success(), seed_transfer( "anothertoken"_n, "bob"_n, asset::from_string("2000.0000 VOICE"), "deposit to: alice") ); BOOST_REQUIRE_EQUAL( success(), inittoken( "alice"_n, EVO4, @@ -3054,8 +3032,6 @@ BOOST_FIXTURE_TEST_CASE( guard_semantics_on_the_memo_path, sysio_swap_tester ) t abi_ser.set_abi( swap_abi_def(), fixture_abi_yield_function() ); // badtoken's EOS is a different token from the system token's EOS: it is the // pair's first leg, seeded with the contract's authority. - BOOST_REQUIRE_EQUAL( success(), openext( "alice"_n, "alice"_n, extended_symbol{EOS4, "badtoken"_n}) ); - BOOST_REQUIRE_EQUAL( success(), openext( "alice"_n, "alice"_n, SYSTEM_TOKEN) ); BOOST_REQUIRE_EQUAL( wasm_assert_msg("token is not a leg of any pair"), transfer( "badtoken"_n, "alice"_n, "sysio.swap"_n, asset::from_string("2000.0000 EOS"), "") ); BOOST_REQUIRE_EQUAL( success(), seed_transfer( "badtoken"_n, "alice"_n, asset::from_string("2000.0000 EOS"), "") ); @@ -3191,7 +3167,6 @@ BOOST_FIXTURE_TEST_CASE( price_accumulators_span_extreme_prices, sysio_swap_test // needs the high limb and the average still recovers the price exactly. create_tokens_and_issue(); abi_ser.set_abi( swap_abi_def(), fixture_abi_yield_function() ); - many_openext(); BOOST_REQUIRE_EQUAL( success(), transfer( "sysio.token"_n, "alice"_n, "sysio.swap"_n, asset::from_string("461168601842738.7903 EOS"), "") ); BOOST_REQUIRE_EQUAL( success(), seed_transfer( "anothertoken"_n, "bob"_n, asset::from_string("461168601842738.7903 VOICE"), "deposit to: alice") ); const int64_t init_max = 1'000'000'000'000'000 - 1; @@ -3232,7 +3207,7 @@ BOOST_FIXTURE_TEST_CASE( abi_surface_is_pinned, sysio_swap_tester ) try { for (const auto& a : abi.actions) actions.insert(a.name.to_string()); const std::set expected_actions{ "accrueyield", "addliquidity", "cancelyield", "changefee", "close", "closeext", "exchange", "fundyield", - "inittoken", "open", "openext", "remliquidity", "setconfig", "setyield", "sync", "tickyield", "transfer", + "inittoken", "remliquidity", "setconfig", "setyield", "sync", "tickyield", "transfer", "withdraw" }; BOOST_REQUIRE( actions == expected_actions ); @@ -3338,7 +3313,6 @@ BOOST_FIXTURE_TEST_CASE( a_freeze_refuses_every_token_move, sysio_swap_tester ) create_tokens_and_issue(); transfer( "anothertoken"_n, "bob"_n, "alice"_n, asset::from_string("500000000.0000 VOICE"), ""); abi_ser.set_abi(swap_abi, fixture_abi_yield_function()); - many_openext(); BOOST_REQUIRE_EQUAL( success(), transfer( "sysio.token"_n, "alice"_n, "sysio.swap"_n, asset::from_string("10000000.0000 EOS"), "") ); BOOST_REQUIRE_EQUAL( success(), seed_transfer( "anothertoken"_n, "alice"_n, @@ -3369,7 +3343,6 @@ BOOST_FIXTURE_TEST_CASE( a_freeze_refuses_every_token_move, sysio_swap_tester ) // What moves nothing runs. BOOST_REQUIRE_EQUAL( success(), sync( EVO ) ); BOOST_REQUIRE_EQUAL( success(), changefee( EVO, 20 ) ); - BOOST_REQUIRE_EQUAL( success(), openext( "carol"_n, "alice"_n, system_token ) ); BOOST_REQUIRE_EQUAL( success(), andon::clear(*this, andon_abi_ser) ); BOOST_REQUIRE_EQUAL( success(), exchange( "alice"_n, EVO, extend(asset::from_string("1.0000 EOS")), @@ -3414,4 +3387,75 @@ BOOST_FIXTURE_TEST_CASE( a_freeze_refuses_inittoken_accrual_and_a_selling_tick, extend(asset::from_string("1.00 TUSD")), extend( asset( 1'0000, WIRE9 ) ), 10, name{}) ); } FC_LOG_AND_RETHROW() + +// Pair-token balance rows are billed to sysio: minting, sending, receiving and closing leave the +// holders' RAM unchanged. +BOOST_FIXTURE_TEST_CASE( pair_token_balances_are_billed_to_sysio, sysio_swap_tester ) try { + const name sysio_account = config::system_account_name; + setup_pools(); + BOOST_REQUIRE_EQUAL( sysio_account, balance_payer( "alice"_n, EVO ) ); + + // A send into a new row bills neither party. + int64_t alice_ram = ram_usage( "alice"_n ); + int64_t bob_ram = ram_usage( "bob"_n ); + BOOST_REQUIRE_EQUAL( success(), send_pair_token( "alice"_n, "bob"_n, asset::from_string("100.0000 EVO") ) ); + BOOST_REQUIRE_EQUAL( alice_ram, ram_usage( "alice"_n ) ); + BOOST_REQUIRE_EQUAL( bob_ram, ram_usage( "bob"_n ) ); + BOOST_REQUIRE_EQUAL( sysio_account, balance_payer( "bob"_n, EVO ) ); + + // The sender of a row it did not create keeps its RAM, and so does the recipient. + BOOST_REQUIRE_EQUAL( success(), send_pair_token( "bob"_n, "alice"_n, asset::from_string("40.0000 EVO") ) ); + BOOST_REQUIRE_EQUAL( bob_ram, ram_usage( "bob"_n ) ); + BOOST_REQUIRE_EQUAL( alice_ram, ram_usage( "alice"_n ) ); + BOOST_REQUIRE_EQUAL( sysio_account, balance_payer( "bob"_n, EVO ) ); + BOOST_REQUIRE_EQUAL( sysio_account, balance_payer( "alice"_n, EVO ) ); + + // Shares minted by addliquidity are billed to sysio too. + BOOST_REQUIRE_EQUAL( success(), addliquidity( "alice"_n, asset::from_string("50.0000 EVO"), + asset::from_string("1000000.0000 VOICE"), + asset::from_string("1000000.0000 EOS") ) ); + BOOST_REQUIRE_EQUAL( alice_ram, ram_usage( "alice"_n ) ); + BOOST_REQUIRE_EQUAL( sysio_account, balance_payer( "alice"_n, EVO ) ); + + // Emptying and closing a row leaves the holder's RAM unchanged. + BOOST_REQUIRE_EQUAL( success(), send_pair_token( "bob"_n, "alice"_n, asset::from_string("60.0000 EVO") ) ); + BOOST_REQUIRE_EQUAL( success(), push_action( "sysio.swap"_n, "bob"_n, "close"_n, mvo() + ( "owner", "bob"_n )( "symbol", EVO4 ) ) ); + BOOST_REQUIRE_EQUAL( 0, lp_balance( "bob"_n, EVO ) ); + BOOST_REQUIRE_EQUAL( bob_ram, ram_usage( "bob"_n ) ); +} FC_LOG_AND_RETHROW() + +// The configuration, the rows inittoken writes and the deposit rows are billed to +// sysio. The first credit creates a deposit row; depositing, withdrawing and closing +// leave the holders' RAM unchanged. +BOOST_FIXTURE_TEST_CASE( swap_rows_are_billed_to_sysio, sysio_swap_tester ) try { + const name sysio_account = config::system_account_name; + BOOST_REQUIRE_EQUAL( sysio_account, row_payer( "swapconfig"_n, { "swapconfig"_n.to_uint64_t() } ) ); + setup_pools(); + BOOST_REQUIRE_EQUAL( sysio_account, row_payer( "stat"_n, { EVO.value } ) ); + BOOST_REQUIRE_EQUAL( sysio_account, row_payer( "priceaccum"_n, { EVO.value } ) ); + BOOST_REQUIRE_EQUAL( sysio_account, row_payer( "evoindex"_n, + { "anothertoken"_n.to_uint64_t(), VOICE4.value(), "sysio.token"_n.to_uint64_t(), EOS4.value() } ) ); + BOOST_REQUIRE_EQUAL( sysio_account, deposit_payer( "alice"_n, SYSTEM_TOKEN ) ); + + // A deposit for another account creates that account's row; neither is billed. + // setup_pools deposited all of alice's EOS, so she withdraws some to send. + BOOST_REQUIRE_EQUAL( success(), withdraw( "alice"_n, "alice"_n, extend( asset::from_string("1.0000 EOS") ) ) ); + BOOST_REQUIRE( !has_deposit( "carol"_n, SYSTEM_TOKEN ) ); + const int64_t alice_ram = ram_usage( "alice"_n ); + const int64_t carol_ram = ram_usage( "carol"_n ); + BOOST_REQUIRE_EQUAL( success(), transfer( "sysio.token"_n, "alice"_n, "sysio.swap"_n, + asset::from_string("1.0000 EOS"), "deposit to: carol" ) ); + BOOST_REQUIRE_EQUAL( 1'0000, deposit_of( "carol"_n, SYSTEM_TOKEN ) ); + BOOST_REQUIRE_EQUAL( sysio_account, deposit_payer( "carol"_n, SYSTEM_TOKEN ) ); + BOOST_REQUIRE_EQUAL( alice_ram, ram_usage( "alice"_n ) ); + BOOST_REQUIRE_EQUAL( carol_ram, ram_usage( "carol"_n ) ); + + // Withdrawing and closing leave the holder's RAM unchanged. + BOOST_REQUIRE_EQUAL( success(), withdraw( "carol"_n, "carol"_n, extend( asset::from_string("0.5000 EOS") ) ) ); + BOOST_REQUIRE_EQUAL( success(), closeext( "carol"_n, "carol"_n, SYSTEM_TOKEN ) ); + BOOST_REQUIRE( !has_deposit( "carol"_n, SYSTEM_TOKEN ) ); + BOOST_REQUIRE_EQUAL( carol_ram, ram_usage( "carol"_n ) ); +} FC_LOG_AND_RETHROW() + BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/contracts/tests/sysio.synd_tests.cpp b/contracts/tests/sysio.synd_tests.cpp index 8078a5645c..b41839f6ea 100644 --- a/contracts/tests/sysio.synd_tests.cpp +++ b/contracts/tests/sysio.synd_tests.cpp @@ -240,8 +240,7 @@ class sysio_synd_tester : public tester { deploy(TOKENS_ACCOUNT, contracts::tokens_wasm(), contracts::tokens_abi(), tokens_abi_ser); deploy(TOKEN_ACCOUNT, contracts::token_wasm(), contracts::token_abi(), token_abi_ser); deploy(MSGCH_ACCOUNT, contracts::msgch_wasm(), contracts::msgch_abi(), msgch_abi_ser); - // The swap is unprivileged, as it is deployed; it bills a user's rows to the user. - deploy(SWAP_ACCOUNT, contracts::swap_wasm(), contracts::swap_abi(), swap_abi_ser, false); + deploy(SWAP_ACCOUNT, contracts::swap_wasm(), contracts::swap_abi(), swap_abi_ser); deploy(LIQ_ACCOUNT, contracts::liq_wasm(), contracts::liq_abi(), liq_abi_ser); deploy(SYND_ACCOUNT, contracts::synd_wasm(), contracts::synd_abi(), synd_abi_ser); deploy(BOND_ACCOUNT, contracts::bond_wasm(), contracts::bond_abi(), bond_abi_ser); diff --git a/contracts/tests/sysio.token_tests.cpp b/contracts/tests/sysio.token_tests.cpp index 34b9095228..d4a7c23cc0 100644 --- a/contracts/tests/sysio.token_tests.cpp +++ b/contracts/tests/sysio.token_tests.cpp @@ -100,16 +100,6 @@ class sysio_token_tester : public tester { ); } - action_result open( account_name owner, - const string& symbolname, - account_name ram_payer ) { - return push_action( ram_payer, "open"_n, mvo() - ( "owner", owner ) - ( "symbol", symbolname ) - ( "ram_payer", ram_payer ) - ); - } - action_result close( account_name owner, const string& symbolname ) { return push_action( owner, "close"_n, mvo() @@ -258,6 +248,12 @@ BOOST_FIXTURE_TEST_CASE( issue_tests, sysio_token_tester ) try { issue( "alice"_n, asset::from_string("-1.000 TKN"), "hola" ) ); + BOOST_REQUIRE_EQUAL( wasm_assert_msg( "tokens can only be issued to issuer account" ), + push_action( "alice"_n, "issue"_n, mvo() + ( "to", "bob" ) + ( "quantity", asset::from_string("1.000 TKN") ) + ( "memo", "" ) ) ); + BOOST_REQUIRE_EQUAL( success(), issue( "alice"_n, asset::from_string("1.000 TKN"), "hola" ) ); @@ -364,52 +360,6 @@ BOOST_FIXTURE_TEST_CASE( transfer_tests, sysio_token_tester ) try { } FC_LOG_AND_RETHROW() -BOOST_FIXTURE_TEST_CASE( open_tests, sysio_token_tester ) try { - - auto token = create( "alice"_n, asset::from_string("1000 CERO")); - - auto alice_balance = get_account("alice"_n, "0,CERO"); - BOOST_REQUIRE_EQUAL(true, alice_balance.is_null() ); - BOOST_REQUIRE_EQUAL( wasm_assert_msg("tokens can only be issued to issuer account"), - push_action( "alice"_n, "issue"_n, mvo() - ( "to", "bob") - ( "quantity", asset::from_string("1000 CERO") ) - ( "memo", "") ) ); - BOOST_REQUIRE_EQUAL( success(), issue( "alice"_n, asset::from_string("1000 CERO"), "issue" ) ); - - alice_balance = get_account("alice"_n, "0,CERO"); - REQUIRE_MATCHING_OBJECT( alice_balance, mvo() - ("balance", "1000 CERO") - ); - - auto bob_balance = get_account("bob"_n, "0,CERO"); - BOOST_REQUIRE_EQUAL(true, bob_balance.is_null() ); - - BOOST_REQUIRE_EQUAL( wasm_assert_msg("owner account does not exist"), - open( "nonexistent"_n, "0,CERO", "alice"_n ) ); - BOOST_REQUIRE_EQUAL( success(), - open( "bob"_n, "0,CERO", "alice"_n ) ); - - bob_balance = get_account("bob"_n, "0,CERO"); - REQUIRE_MATCHING_OBJECT( bob_balance, mvo() - ("balance", "0 CERO") - ); - - BOOST_REQUIRE_EQUAL( success(), transfer( "alice"_n, "bob"_n, asset::from_string("200 CERO"), "hola" ) ); - - bob_balance = get_account("bob"_n, "0,CERO"); - REQUIRE_MATCHING_OBJECT( bob_balance, mvo() - ("balance", "200 CERO") - ); - - BOOST_REQUIRE_EQUAL( wasm_assert_msg( "symbol does not exist" ), - open( "carol"_n, "0,INVALID", "alice"_n ) ); - - BOOST_REQUIRE_EQUAL( wasm_assert_msg( "symbol precision mismatch" ), - open( "carol"_n, "1,CERO", "alice"_n ) ); - -} FC_LOG_AND_RETHROW() - BOOST_FIXTURE_TEST_CASE( close_tests, sysio_token_tester ) try { auto token = create( "alice"_n, asset::from_string("1000 CERO")); diff --git a/contracts/tests/sysio_liq_tester.hpp b/contracts/tests/sysio_liq_tester.hpp index 783a78b0be..896b372eb5 100644 --- a/contracts/tests/sysio_liq_tester.hpp +++ b/contracts/tests/sysio_liq_tester.hpp @@ -64,7 +64,7 @@ class sysio_liq_tester : public tester { deploy(CHAINS_ACCOUNT, contracts::chains_wasm(), contracts::chains_abi(), chains_abi_ser, true); deploy(TOKENS_ACCOUNT, contracts::tokens_wasm(), contracts::tokens_abi(), tokens_abi_ser, true); deploy(TOKEN_ACCOUNT, contracts::token_wasm(), contracts::token_abi(), token_abi_ser, true); - deploy(SWAP_ACCOUNT, contracts::swap_wasm(), contracts::swap_abi(), swap_abi_ser, false); + deploy(SWAP_ACCOUNT, contracts::swap_wasm(), contracts::swap_abi(), swap_abi_ser, true); deploy(LIQ_ACCOUNT, contracts::liq_wasm(), contracts::liq_abi(), liq_abi_ser, true); // WIRE: the treasury supply on sysio, and working balances for the funders. @@ -161,9 +161,6 @@ class sysio_liq_tester : public tester { action_result transfer_shadow(name from, name to, int64_t amount, const std::string& memo = "") { return push_liq(from, "transfer"_n, mvo()("from", from)("to", to)("quantity", asset(amount, LIQSOL_SYM))("memo", memo)); } - action_result open(name owner, symbol sym, name ram_payer) { - return push_liq(ram_payer, "open"_n, mvo()("owner", owner)("symbol", sym)("ram_payer", ram_payer)); - } action_result close(name owner, symbol sym) { return push_liq(owner, "close"_n, mvo()("owner", owner)("symbol", sym)); } @@ -195,25 +192,15 @@ class sysio_liq_tester : public tester { action_result tickyield(symbol pair = POOL_SYM, name signer = "alice"_n) { return push(SWAP_ACCOUNT, swap_abi_ser, signer, "tickyield"_n, mvo()("pair_token", pair.to_symbol_code())); } - // The swap is unprivileged and bills a user's rows to the user, so these carry the - // user's `sysio.payer` beside `active`, as the swap suite's own pushes do. - /// A user's deposit row on the swap for one token, RAM billed to `payer`. - action_result openext(name user, name payer, const extended_symbol& ext_symbol) { - return push(SWAP_ACCOUNT, swap_abi_ser, payer, "openext"_n, mvo() - ("user", user)("payer", payer)("ext_symbol", ext_symbol), - sysio_system::test_support::payer_authorization(payer)); - } /// Sell `ext_asset_in` from the user's deposit into `pair` for at least `min_expected`. action_result exchange(name user, symbol pair, const extended_asset& ext_asset_in, const asset& min_expected) { return push(SWAP_ACCOUNT, swap_abi_ser, user, "exchange"_n, mvo() - ("user", user)("pair_token", pair.to_symbol_code())("ext_asset_in", ext_asset_in)("min_expected", min_expected), - sysio_system::test_support::payer_authorization(user)); + ("user", user)("pair_token", pair.to_symbol_code())("ext_asset_in", ext_asset_in)("min_expected", min_expected)); } /// Move `to_withdraw` from the user's deposit on the swap to `to`'s wallet. action_result withdraw(name user, name to, const extended_asset& to_withdraw) { return push(SWAP_ACCOUNT, swap_abi_ser, user, "withdraw"_n, mvo() - ("user", user)("to", to)("to_withdraw", to_withdraw)("memo", ""), - sysio_system::test_support::payer_authorization(user)); + ("user", user)("to", to)("to_withdraw", to_withdraw)("memo", "")); } // --- rows --- diff --git a/docs/emergency-stop-playbook.md b/docs/emergency-stop-playbook.md index 6d02b3d4c0..de00c1e230 100644 --- a/docs/emergency-stop-playbook.md +++ b/docs/emergency-stop-playbook.md @@ -108,7 +108,7 @@ Each item is a deploy or upgrade step. An item not done is a freeze that cannot | While the cord is pulled | Refused or deferred | Still runs | |---|---|---| -| `sysio.swap` | every action that moves tokens (among them `exchange`, `addliquidity`, `remliquidity`, `withdraw`, `closeext` of a funded deposit, `tickyield` and `accrueyield` with yield owed, `inittoken`) | reads, and actions that move nothing (`sync`, `changefee`, `openext`, `setyield`) | +| `sysio.swap` | every action that moves tokens (among them `exchange`, `addliquidity`, `remliquidity`, `withdraw`, `closeext` of a funded deposit, `tickyield` and `accrueyield` with yield owed, `inittoken`) | reads, and actions that move nothing (`sync`, `changefee`, `setyield`) | | `sysio.liq` | `transfer` to anything but a custody contract (`sysio.bond`, `sysio.synd`, `sysio.opreg`), so holders cannot move shadow LIQ between themselves; `claim` by anyone but a custody contract; `queueyield` | `mint`; transfers into a custody contract; `claim` by a custody contract (it moves WIRE into custody) | | `sysio.bond` | `claim` | `request`, `addbounty`, `accept`, `hold`, `approve`, `rslvvalid`, `rslvinvalid`, `sweepyield`, `prune` | | `sysio.kicker` | `kick` returns without paying and without writing: `last_kick` stays where it was, so accrual keeps running and the first paying `kick` after the clear covers it, within the 72-hour catch-up | `setconfig`, `addpool`, `setpool`, `rmpool` | diff --git a/programs/clio/main.cpp b/programs/clio/main.cpp index cf297bb04a..f00f9ea1e7 100644 --- a/programs/clio/main.cpp +++ b/programs/clio/main.cpp @@ -808,17 +808,6 @@ fc::variant regproducer_variant(const account_name& producer, const public_key_t ; } -chain::action create_open(const string& contract, const name& owner, symbol sym, const name& ram_payer) { - auto open_ = fc::mutable_variant_object - ("owner", owner) - ("symbol", sym) - ("ram_payer", ram_payer); - return action { - get_account_permissions(tx_permission, {ram_payer, sysio::chain::config::active_name}), - name(contract), "open"_n, variant_to_bin( name(contract), "open"_n, open_ ) - }; -} - chain::action create_transfer(const string& contract, const name& sender, const name& recipient, asset amount, const string& memo ) { auto transfer = fc::mutable_variant_object @@ -3036,7 +3025,6 @@ int main( int argc, char** argv ) { string recipient; string amount; string memo; - bool pay_ram = false; auto transfer_cmd = app.add_subcommand("transfer", localized("Transfer tokens from account to account")); transfer_cmd->add_option("sender", sender, localized("The account sending tokens"))->required(); @@ -3044,18 +3032,12 @@ int main( int argc, char** argv ) { transfer_cmd->add_option("amount", amount, localized("The amount of tokens to send"))->required(); transfer_cmd->add_option("memo", memo, localized("The memo for the transfer")); transfer_cmd->add_option("--contract,-c", con, localized("The contract that controls the token, defaults to sysio.token for asset amounts")); - transfer_cmd->add_flag("--pay-ram-to-open", pay_ram, localized("Pay RAM to open recipient's token balance row")); add_standard_transaction_options_plus_signing(transfer_cmd, "sender@active"); transfer_cmd->callback([&] { auto transfer_amount = to_asset(name(con), amount); auto transfer = create_transfer(con, name(sender), name(recipient), transfer_amount, memo); - if (!pay_ram) { - send_actions( { transfer }, signing_keys_opt.get_keys()); - } else { - auto open_ = create_open(con, name(recipient), transfer_amount.get_symbol(), name(sender)); - send_actions( { open_, transfer }, signing_keys_opt.get_keys()); - } + send_actions( { transfer }, signing_keys_opt.get_keys()); }); // Net subcommand diff --git a/unittests/kv_token_tests.cpp b/unittests/kv_token_tests.cpp index c3913df072..f2e8831b7f 100644 --- a/unittests/kv_token_tests.cpp +++ b/unittests/kv_token_tests.cpp @@ -41,11 +41,6 @@ struct kv_token_tester : validating_tester { fc::mutable_variant_object()("quantity", quantity)("memo", memo)); } - void open(name owner, symbol sym, name payer) { - push_action("sysio.token"_n, "open"_n, payer, - fc::mutable_variant_object()("owner", owner)("symbol", sym)("ram_payer", payer)); - } - void close(name owner, symbol sym) { push_action("sysio.token"_n, "close"_n, owner, fc::mutable_variant_object()("owner", owner)("symbol", sym)); @@ -96,21 +91,20 @@ BOOST_FIXTURE_TEST_CASE(kv_token_retire, kv_token_tester) { BOOST_TEST_MESSAGE("Token retire succeeded with KV-backed sysio.token"); } -BOOST_FIXTURE_TEST_CASE(kv_token_open_close, kv_token_tester) { +BOOST_FIXTURE_TEST_CASE(kv_token_close, kv_token_tester) { auto sym = symbol(SY(4, WIRE)); create_token("alice"_n, asset(1000000000, sym)); + issue("alice"_n, asset(10000, sym)); produce_block(); - // Open account for bob (alice pays) - open("bob"_n, sym, "alice"_n); + // Empty alice's balance, then close the zero-balance row + transfer("alice"_n, "bob"_n, asset(10000, sym)); produce_block(); - - // Close bob's zero-balance account - close("bob"_n, sym); + close("alice"_n, sym); produce_block(); - BOOST_TEST_MESSAGE("Token open/close succeeded with KV-backed sysio.token"); + BOOST_TEST_MESSAGE("Token close succeeded with KV-backed sysio.token"); } BOOST_FIXTURE_TEST_CASE(kv_token_overdrawn_check, kv_token_tester) { diff --git a/unittests/sysio_system_tester.hpp b/unittests/sysio_system_tester.hpp index 324f69f51c..95f8f2cf3c 100644 --- a/unittests/sysio_system_tester.hpp +++ b/unittests/sysio_system_tester.hpp @@ -76,16 +76,6 @@ class sysio_system_tester : public validating_tester { get_balance(name("sysio")) + get_balance(name("sysio.ramfee")) + get_balance(name("sysio.stake")) + get_balance(name("sysio.ram")) ); } - action_result open( account_name owner, - const string& symbolname, - account_name ram_payer ) { - return push_action( ram_payer, "open"_n, mvo() - ( "owner", owner ) - ( "symbol", symbolname ) - ( "ram_payer", ram_payer ) - ); - } - transaction_trace_ptr setup_producer_accounts( const std::vector& accounts ) { account_name creator(config::system_account_name); signed_transaction trx;