Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add basic invariant functions to sdk #50

Merged
merged 2 commits into from
Jun 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 10 additions & 6 deletions sdk/build.sh
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
set -e
#!/bin/bash
npm install &&
npm run lint &&
npm run wasm:build &&
npm install
npm run lint
npm run wasm:build
./package-wasm.sh
# second install for wasm to be added
npm install &&
npm run contract:build &&
npm run erc-20:generate &&
npm install
npm run contract:build
npm run erc-20:generate
npm run invariant:generate
npm run fix-generate
npm run build
5 changes: 0 additions & 5 deletions sdk/contracts/gear_erc20/gear_erc20.idl
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,6 @@ service Erc20 {
query Name : () -> str;
query Symbol : () -> str;
query TotalSupply : () -> u256;

events {
Approval: struct { owner: actor_id, spender: actor_id, value: u256 };
Transfer: struct { from: actor_id, to: actor_id, value: u256 };
}
};

service Pausable {
Expand Down
Binary file modified sdk/contracts/gear_erc20/gear_erc20.opt.wasm
Binary file not shown.
172 changes: 172 additions & 0 deletions sdk/contracts/invariant/invariant.idl
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
type InvariantConfig = struct {
admin: actor_id,
protocol_fee: Percentage,
};

type Percentage = struct {
u64,
};

type FeeTier = struct {
fee: Percentage,
tick_spacing: u16,
};

type PoolKey = struct {
token_x: actor_id,
token_y: actor_id,
fee_tier: FeeTier,
};

type TokenAmount = struct {
u128,
};

type SqrtPrice = struct {
u128,
};

type Liquidity = struct {
u128,
};

type Position = struct {
pool_key: PoolKey,
liquidity: Liquidity,
lower_tick_index: i32,
upper_tick_index: i32,
fee_growth_inside_x: FeeGrowth,
fee_growth_inside_y: FeeGrowth,
last_block_number: u64,
tokens_owed_x: TokenAmount,
tokens_owed_y: TokenAmount,
};

type FeeGrowth = struct {
u128,
};

type CalculateSwapResult = struct {
amount_in: TokenAmount,
amount_out: TokenAmount,
start_sqrt_price: SqrtPrice,
target_sqrt_price: SqrtPrice,
fee: TokenAmount,
pool: Pool,
ticks: vec Tick,
};

type Pool = struct {
liquidity: Liquidity,
sqrt_price: SqrtPrice,
current_tick_index: i32,
fee_growth_global_x: FeeGrowth,
fee_growth_global_y: FeeGrowth,
fee_protocol_token_x: TokenAmount,
fee_protocol_token_y: TokenAmount,
start_timestamp: u64,
last_timestamp: u64,
fee_receiver: actor_id,
};

type Tick = struct {
index: i32,
sign: bool,
liquidity_change: Liquidity,
liquidity_gross: Liquidity,
sqrt_price: SqrtPrice,
fee_growth_outside_x: FeeGrowth,
fee_growth_outside_y: FeeGrowth,
seconds_outside: u64,
};

type InvariantError = enum {
NotAdmin,
NotFeeReceiver,
PoolAlreadyExist,
PoolNotFound,
TickAlreadyExist,
InvalidTickIndexOrTickSpacing,
PositionNotFound,
TickNotFound,
FeeTierNotFound,
PoolKeyNotFound,
AmountIsZero,
WrongLimit,
PriceLimitReached,
NoGainSwap,
InvalidTickSpacing,
FeeTierAlreadyExist,
PoolKeyAlreadyExist,
UnauthorizedFeeReceiver,
ZeroLiquidity,
RecoverableTransferError,
UnrecoverableTransferError,
TransferError,
TokensAreSame,
AmountUnderMinimumAmountOut,
InvalidFee,
NotEmptyTickDeinitialization,
InvalidInitTick,
InvalidInitSqrtPrice,
NotEnoughGasToExecute,
TickLimitReached,
InvalidTickIndex,
NoBalanceForTheToken,
FailedToChangeTokenBalance,
ReplyHandlingFailed,
};

type QuoteResult = struct {
amount_in: TokenAmount,
amount_out: TokenAmount,
target_sqrt_price: SqrtPrice,
ticks: vec Tick,
};

type SwapHop = struct {
pool_key: PoolKey,
x_to_y: bool,
};

constructor {
New : (config: InvariantConfig);
};

service Service {
AddFeeTier : (fee_tier: FeeTier) -> FeeTier;
ChangeFeeReceiver : (pool_key: PoolKey, fee_receiver: actor_id) -> null;
ChangeProtocolFee : (protocol_fee: Percentage) -> Percentage;
ClaimFee : (index: u32) -> struct { TokenAmount, TokenAmount };
CreatePool : (token_x: actor_id, token_y: actor_id, fee_tier: FeeTier, init_sqrt_price: SqrtPrice, init_tick: i32) -> null;
CreatePosition : (pool_key: PoolKey, lower_tick: i32, upper_tick: i32, liquidity_delta: Liquidity, slippage_limit_lower: SqrtPrice, slippage_limit_upper: SqrtPrice) -> Position;
DepositSingleToken : (token: actor_id, amount: TokenAmount) -> TokenAmount;
DepositTokenPair : (token_x: struct { actor_id, TokenAmount }, token_y: struct { actor_id, TokenAmount }) -> struct { TokenAmount, TokenAmount };
RemoveFeeTier : (fee_tier: FeeTier) -> FeeTier;
RemovePosition : (index: u32) -> struct { TokenAmount, TokenAmount };
Swap : (pool_key: PoolKey, x_to_y: bool, amount: TokenAmount, by_amount_in: bool, sqrt_price_limit: SqrtPrice) -> CalculateSwapResult;
TransferPosition : (index: u32, receiver: actor_id) -> null;
WithdrawProtocolFee : (pool_key: PoolKey) -> null;
WithdrawSingleToken : (token: actor_id, amount: opt TokenAmount) -> TokenAmount;
WithdrawTokenPair : (token_x: struct { actor_id, opt TokenAmount }, token_y: struct { actor_id, opt TokenAmount }) -> struct { TokenAmount, TokenAmount };
query FeeTierExists : (fee_tier: FeeTier) -> bool;
query GetAllPositions : (owner_id: actor_id) -> vec Position;
query GetFeeTiers : () -> vec FeeTier;
query GetPool : (token_x: actor_id, token_y: actor_id, fee_tier: FeeTier) -> result (Pool, InvariantError);
query GetPools : (size: u8, offset: u16) -> result (vec PoolKey, InvariantError);
query GetPosition : (owner_id: actor_id, index: u32) -> result (Position, InvariantError);
query GetProtocolFee : () -> Percentage;
query GetTick : (key: PoolKey, index: i32) -> result (Tick, InvariantError);
query GetUserBalances : (user: actor_id) -> vec struct { actor_id, TokenAmount };
query IsTickInitialized : (key: PoolKey, index: i32) -> bool;
query Quote : (pool_key: PoolKey, x_to_y: bool, amount: TokenAmount, by_amount_in: bool, sqrt_price_limit: SqrtPrice) -> result (QuoteResult, InvariantError);
query QuoteRoute : (amount_in: TokenAmount, swaps: vec SwapHop) -> result (TokenAmount, InvariantError);

events {
PositionCreatedEvent: struct { block_timestamp: u64, address: actor_id, pool_key: PoolKey, liquidity_delta: Liquidity, lower_tick: i32, upper_tick: i32, current_sqrt_price: SqrtPrice };
PositionRemovedEvent: struct { block_timestamp: u64, caller: actor_id, pool_key: PoolKey, liquidity: Liquidity, lower_tick_index: i32, upper_tick_index: i32, sqrt_price: SqrtPrice };
CrossTickEvent: struct { timestamp: u64, address: actor_id, pool: PoolKey, indexes: vec i32 };
SwapEvent: struct { timestamp: u64, address: actor_id, pool: PoolKey, amount_in: TokenAmount, amount_out: TokenAmount, fee: TokenAmount, start_sqrt_price: SqrtPrice, target_sqrt_price: SqrtPrice, x_to_y: bool };
}
};

Binary file modified sdk/contracts/invariant/invariant.opt.wasm
Binary file not shown.
68 changes: 53 additions & 15 deletions sdk/fix-generate.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,32 +6,70 @@ String.prototype.replaceAt = function (index, replacement, length) {
return this.substring(0, index) + replacement + this.substring(index + length)
}

const snakeToCamel = str => {
return str
.toLowerCase()
.replace(/([-_][a-z])/g, group => group.toUpperCase().replace('-', '').replace('_', ''))
}

for (const path of filesToModify) {
let textBuff = fs
.readFileSync(path, 'utf8')
// fix missing invalid || null for v | undefined variable
.replaceAll(`at: atBlock || null`, `at: atBlock`)
// fix missing null check
.replaceAll(
').toHex();\n const reply = await this._program.api.message.calculateReply',
").toHex();\n if (!this._program.programId) throw new Error('Program ID is not set');\n const reply = await this._program.api.message.calculateReply"
)
// matching no parameter queries
let regex = /\'\(String, String\)\', '\[(.+?), (.+?)\]'/g
let matches = textBuff.match(regex)
matches?.map(val => {
let match = val.replaceAll("'[", "['").replaceAll("]'", "']")

let matchCount = 0
for (let j = 0; j < match.length; j++) {
if (match[j] === ',') {
matchCount++
if (matchCount === 3) {
match = match.replaceAt(j, "', '", 2)
break
// fix no parameter queries
{
let regex = /\'\(String, String\)\', '\[(.+?), (.+?)\]'/g
let matches = textBuff.match(regex)
matches?.map(val => {
let match = val.replaceAll("'[", "['").replaceAll("]'", "']")

let matchCount = 0
for (let j = 0; j < match.length; j++) {
if (match[j] === ',') {
matchCount++
if (matchCount === 3) {
match = match.replaceAt(j, "', '", 2)
break
}
}
}
}
textBuff = textBuff.replace(val, match)
})
textBuff = textBuff.replace(val, match)
})
}
// fix transactions by replacing wrapper types to actual types
const typesToReplace = [
['Percentage', 'u64'],
['TokenAmount', 'u128'],
['Liquidity', 'u128'],
['SqrtPrice', 'u128'],
['FeeGrowth', 'u128']
]
{
let transactionRegex =
/>\(\s*this._program.api,\s*this._program.registry,\s*'send_message',\s*^[^\}]*/gm
textBuff.match(transactionRegex)?.map(val => {
let match = val
typesToReplace.map(type => {
match = match.replaceAll(type[0], type[1])
})
textBuff = textBuff.replace(val, match)
})
}
// camelize snake case JSON keys for easier use with invariant-vara-wasm types
{
let snakeCaseRegex = /"(?!_)[a-z_]*":/g
textBuff.match(snakeCaseRegex)?.map(val => {
textBuff = textBuff.replace(val, snakeToCamel(val))
})
}
// fix missing any cast
textBuff = textBuff.replaceAll(
'message.payload)[2].toJSON() as {',
'message.payload)[2].toJSON() as any as {'
Expand Down
1 change: 0 additions & 1 deletion sdk/mocharc.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
{
"spec": "tests/*.test.ts",
"node-option": [
"experimental-specifier-resolution=node",
"loader=ts-node/esm",
Expand Down
Loading
Loading