Skip to content

Commit

Permalink
Merge pull request #60 from pnetwork-association/feat/singleton-adapter
Browse files Browse the repository at this point in the history
Support single token pair per adapter
  • Loading branch information
envin3 authored Nov 8, 2024
2 parents 04c3817 + 5ca48a2 commit 70efd6e
Show file tree
Hide file tree
Showing 13 changed files with 151 additions and 116 deletions.
92 changes: 42 additions & 50 deletions cpp/contracts/adapter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,23 @@ namespace eosio {

asset adapter::calculate_fees(const asset& quantity) {
registry_adapter _registry(get_self(), get_self().value);
auto idx = _registry.get_index<adapter_registry_idx_xtoken>();
auto search_token = _registry.find(quantity.symbol.code().raw());
auto search_xerc20 = idx.find(quantity.symbol.code().raw());

asset min_fee;
if (search_token != _registry.end()) {
min_fee = search_token->min_fee;
} else if (search_xerc20 != idx.end()) {
min_fee = search_xerc20->min_fee;
} else {
check(false, "invalid quantity given for calculating the fees");
}
check(_registry.exists(), "contract not inizialized");
auto registry_data = _registry.get();

check(
quantity.symbol == registry_data.token_symbol ||
quantity.symbol == registry_data.xerc20_symbol,
"invalid quantity given for calculating the fees"
);

// Fees are expressed in the wrapped token (xerc20), hence why
// the min_fee.symbol
auto ref_symbol = min_fee.symbol;
auto ref_symbol = registry_data.min_fee.symbol;

uint128_t fee_amount = (FEE_BASIS_POINTS * quantity.amount) / FEE_BASIS_POINTS_DIVISOR;
asset fee = asset(fee_amount, ref_symbol);

return fee < min_fee ? min_fee : fee;
return fee < registry_data.min_fee ? registry_data.min_fee : fee;
}

void adapter::check_symbol_is_valid(const name& account, const symbol& sym) {
Expand All @@ -42,34 +38,33 @@ void adapter::create(
const asset& min_fee
) {
require_auth(get_self());
registry_adapter _registry(get_self(), get_self().value);
check(!_registry.exists(), "adapter already initialized");

auto _token_bytes = token_bytes.extract_as_byte_array();
check(is_account(xerc20), "xERC20 account does not exist");
check(min_fee.symbol == xerc20_symbol, "invalid minimum fee symbol");

registry_adapter _registry(get_self(), get_self().value);
auto itr = _registry.find(token_symbol.code().raw());
check(itr == _registry.end(), "token already registered");
check_symbol_is_valid(xerc20, xerc20_symbol);

// Checks done only for the local token
if (token != name(0)) {
// Check token symbol if toekn is local to a EOS like chain
check_symbol_is_valid(token, token_symbol);
// Difference in precision allowed if token is not local
check(token_symbol.precision() == xerc20_symbol.precision(), "invalid xerc20 precision");
// Do not check token validity if token is not local
check(is_account(token), "token account does not exist");
}

checksum256 c;
_registry.emplace( get_self(), [&]( auto& r ) {
r.xerc20 = xerc20;
r.xerc20_symbol = xerc20_symbol;
r.token = token;
r.token_symbol = token_symbol;
r.token_bytes = token_bytes;
r.min_fee = min_fee;
});
// Default value for the token symbol on non-local deployments
symbol non_local_token_symbol = symbol(symbol_code("XXX"), token_symbol.precision());

adapter_registry_table registry_data {
.token = token,
.token_symbol = token == name(0) ? non_local_token_symbol : token_symbol,
.token_bytes = token_bytes,
.xerc20 = xerc20,
.xerc20_symbol = xerc20_symbol,
.min_fee = min_fee
};
_registry.set(registry_data, get_self());

storage _storage(get_self(), get_self().value);
_storage.get_or_create(get_self(), adapter::empty_storage);
Expand Down Expand Up @@ -191,11 +186,9 @@ void adapter::settle(const name& caller, const operation& operation, const metad
require_auth(caller);

registry_adapter _registry(get_self(), get_self().value);
auto idx_registry = _registry.get_index<adapter_registry_idx_token_bytes>();
auto search_token_bytes = idx_registry.find(operation.token);
check(search_token_bytes != idx_registry.end(), "underlying token does not match with adapter registry");
check(search_token_bytes->xerc20_symbol == operation.amount.symbol, "registered xerc20 symbols differs from the operation one"); // TODO: test me

check(_registry.exists(), "contract not inizialized");
auto registry_data = _registry.get();
check(registry_data.token_bytes == operation.token, "underlying token does not match with adapter registry");
checksum256 event_id; // output
pam::check_authorization(get_self(), operation, metadata, event_id);

Expand All @@ -216,13 +209,13 @@ void adapter::settle(const name& caller, const operation& operation, const metad
storage.nonce++;
_storage.set(storage, get_self());

name xerc20 = search_token_bytes->xerc20;
name xerc20 = registry_data.xerc20;
check(is_account(xerc20), "Not valid xerc20 name");
if (operation.amount.amount > 0) {
asset quantity(operation.amount.amount, search_token_bytes->xerc20_symbol);

if (operation.amount > 0) {
asset adj_operation_amount = adjust_precision(operation.amount, registry_data.token_symbol, registry_data.xerc20_symbol);
asset quantity(adj_operation_amount.amount, registry_data.xerc20_symbol);
lockbox_singleton _lockbox(xerc20, xerc20.value);
action_mint _mint(search_token_bytes->xerc20, {get_self(), "active"_n});
action_mint _mint(registry_data.xerc20, {get_self(), "active"_n});
if (_lockbox.exists()) {
// If the lockbox exists, we release the collateral
auto lockbox = _lockbox.get();
Expand Down Expand Up @@ -342,19 +335,18 @@ void adapter::ontransfer(const name& from, const name& to, const asset& quantity
check(quantity.amount > 0, "invalid amount");

registry_adapter _registry(get_self(), get_self().value);
auto search_token = _registry.find(quantity.symbol.code().raw());
auto idx = _registry.get_index<adapter_registry_idx_xtoken>();
auto search_xerc20 = idx.find(quantity.symbol.code().raw());
check(_registry.exists(), "contract not inizialized");
auto registry_data = _registry.get();

bool is_token_transfer = search_token != _registry.end();
bool is_xerc20_transfer = search_xerc20 != idx.end();
bool is_token_transfer = registry_data.token_symbol == quantity.symbol;
bool is_xerc20_transfer = registry_data.xerc20_symbol == quantity.symbol;

check(is_token_transfer || is_xerc20_transfer, "token not registered");
check(is_token_transfer || is_xerc20_transfer, "token not supported by this adapter");

auto xerc20 = is_token_transfer ? search_token->xerc20 : search_xerc20->xerc20;
auto xerc20_symbol = is_token_transfer ? search_token->xerc20_symbol : search_xerc20->xerc20_symbol;
auto token = is_token_transfer ? search_token->token : search_xerc20->token;
auto token_symbol = is_token_transfer ? search_token->token_symbol : search_xerc20->token_symbol;
auto xerc20 = is_token_transfer ? registry_data.xerc20 : registry_data.xerc20;
auto xerc20_symbol = is_token_transfer ? registry_data.xerc20_symbol : registry_data.xerc20_symbol;
auto token = is_token_transfer ? registry_data.token : registry_data.token;
auto token_symbol = is_token_transfer ? registry_data.token_symbol : registry_data.token_symbol;

if (is_token_transfer) check(quantity.symbol == token_symbol, "invalid token quantity symbol");
if (is_xerc20_transfer) check(quantity.symbol == xerc20_symbol, "invalid xerc20 quantity symbol");
Expand Down
7 changes: 1 addition & 6 deletions cpp/contracts/adapter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,13 +86,8 @@ namespace eosio {
typedef eosio::multi_index<"userdata"_n, user_data_table> user_data;
typedef eosio::multi_index<"pastevents"_n, adapter_past_events_table, adapter_past_events_byeventid> past_events;
typedef eosio::multi_index<"reglockbox"_n, lockbox_registry_table, lockbox_registry_byxtoken> registry_lockbox;
typedef eosio::multi_index<
"regadapter"_n,
adapter_registry_table,
adapter_registry_byxtoken,
adapter_registry_bytokenbytes
> registry_adapter;

using registry_adapter = singleton<"regadapter"_n, adapter_registry_table>;
using lockbox_singleton = singleton<"lockbox"_n, name>;
using storage = singleton<"storage"_n, global_storage_table>;

Expand Down
2 changes: 1 addition & 1 deletion cpp/contracts/operation.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ namespace eosio {
checksum256 token; // erc20 on EVM
bytes originChainId;
bytes destinationChainId;
asset amount;
uint128_t amount;
bytes sender;
name recipient;
bytes data;
Expand Down
2 changes: 1 addition & 1 deletion cpp/contracts/pam.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ namespace eosio {

bytes amount = extract_32bytes(event_data, offset);
uint128_t amount_num = bytes32_to_uint128(amount);
check(to_wei(operation.amount) == amount_num, "amount do not match");
check(operation.amount == amount_num, "amount do not match");
offset += 32;

bytes sender = extract_32bytes(event_data, offset);
Expand Down
37 changes: 7 additions & 30 deletions cpp/contracts/tables/adapter_registry.table.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,51 +11,28 @@ namespace eosio {
// native token is somewhere else (i.e. wETH on Ethereum)
//
// Example:
// wram.token => local chain is EOS, then adapter's registry is
// wram.token, '6,WRAM' => local chain is EOS, then adapter's registry is
//
// |_____token_____|_______token_bytes_____________|_____xerc20_____|
// | 'wram.token' | bytes32(WRAM.symbol.code.raw) | 'xwram.token' |
// |_____token____|___token_symbol___|_______token_bytes_____________|_____xerc20_____|
// | 'wram.token' | '6,WRAM' | bytes32(WRAM.symbol.code.raw) | 'xwram.token' |
//
// NOTE: on destination chains we will set each adapter's registry
// with bytes32(WRAM.symbol.code.raw) for WRAM
//
// Example:
// WETH ERC20 => local chain is Ethereum, then adapter's registry is
//
// |_____token_____|______token_bytes_______|_____xerc20_____|
// | '' | bytes32(address(WETH)) | 'xweth.token' |
// |_____token____|___token_symbol___|______token_bytes_______|_____xerc20_____|
// | '' | '18,XXX' | bytes32(address(WETH)) | 'xweth.token' |
//
// NOTE: for not local token_symbol = '<origin-chain-precision>,XXX'

TABLE adapter_registry_table {
name token;
symbol token_symbol;
checksum256 token_bytes;
name xerc20;
symbol xerc20_symbol;
asset min_fee;

uint64_t primary_key() const { return token_symbol.code().raw(); }
uint64_t secondary_key() const { return xerc20_symbol.code().raw(); }
const checksum256& ternary_key() const { return token_bytes; }
};

constexpr name adapter_registry_idx_xtoken = "byxtoken1"_n;
constexpr name adapter_registry_idx_token_bytes = "bytokenbytes"_n;

typedef indexed_by<
adapter_registry_idx_xtoken,
const_mem_fun<
adapter_registry_table,
uint64_t,
&adapter_registry_table::secondary_key
>
> adapter_registry_byxtoken;

typedef indexed_by<
adapter_registry_idx_token_bytes,
const_mem_fun<
adapter_registry_table,
const checksum256&,
&adapter_registry_table::ternary_key
>
> adapter_registry_bytokenbytes;
}
16 changes: 16 additions & 0 deletions cpp/contracts/utils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,22 @@ namespace eosio {
return asset(amount / powint(10, exp), sym);
}

asset adjust_precision(uint128_t amount, const symbol& from_symbol, const symbol& to_symbol) {
int16_t exp = from_symbol.precision() - to_symbol.precision();
uint128_t factor;
uint128_t adjusted_amount;
if (exp < 0) {
exp = -exp;
factor = powint(10, exp);
adjusted_amount = amount * factor;
} else {
print("WARNING: Operation precision exceeds destination symbol; amount will be floored to destination symbol precision.");
factor = powint(10, exp);
adjusted_amount = amount / factor;
}
return asset(adjusted_amount, to_symbol);
}

bytes extract_32bytes(const bytes& data, uint128_t offset) {
bytes _data(data.begin() + offset, data.begin() + offset + 32);
return _data;
Expand Down
2 changes: 1 addition & 1 deletion cpp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"test": "yarn build && mocha",
"lint": "./lint scripts/*.sh && npx prettier --check test/",
"prettier:fix": "npx prettier --check --write test/",
"prettier": "npx prettier --cache --check --ignore-path ../.prettierignore --config ../.prettierrc ./src ./test"
"prettier": "npx prettier --cache --check --ignore-path ../.prettierignore --config ../.prettierrc ./contracts ./test"
},
"devDependencies": {
"@eosnetwork/vert": "^1.0.0",
Expand Down
4 changes: 1 addition & 3 deletions cpp/test/adapter-local.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -170,9 +170,7 @@ describe('Adapter EOS -> ETH testing', () => {
.setfeemanagr([feemanager])
.send(active(adapter.account))

const row = adapter.contract.tables
.regadapter(getAccountCodeRaw(adapter.account))
.getTableRow(getSymbolCodeRaw(token.maxSupply))
const row = getSingletonInstance(adapter.contract, 'regadapter')

const storage = getSingletonInstance(adapter.contract, TABLE_STORAGE)

Expand Down
Loading

0 comments on commit 70efd6e

Please sign in to comment.