From c806fad5c88355d0242a0d7413dd316f729ee538 Mon Sep 17 00:00:00 2001 From: Jon Michael Aanes Date: Sat, 8 Jun 2024 16:12:57 +0200 Subject: [PATCH] WIP swap --- src/main/abi/LiquiditySwapContract.ts | 471 +++++++++++++++++++++++++ src/main/abi/SwapFactory.ts | 411 ++++++++++++++++++++++ src/main/abi/SwapRouter.ts | 480 ++++++++++++++++++++++++++ src/main/constant.ts | 32 +- src/main/swap/Main.ts | 40 +++ src/main/swap/index.html | 163 +++++++++ src/main/token/Main.ts | 6 +- webpack.config.js | 4 +- 8 files changed, 1598 insertions(+), 9 deletions(-) create mode 100644 src/main/abi/LiquiditySwapContract.ts create mode 100644 src/main/abi/SwapFactory.ts create mode 100644 src/main/abi/SwapRouter.ts create mode 100644 src/main/swap/Main.ts create mode 100644 src/main/swap/index.html diff --git a/src/main/abi/LiquiditySwapContract.ts b/src/main/abi/LiquiditySwapContract.ts new file mode 100644 index 0000000..c003f0f --- /dev/null +++ b/src/main/abi/LiquiditySwapContract.ts @@ -0,0 +1,471 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +import BN from "bn.js"; +import { + AbiParser, + AbstractBuilder, BigEndianReader, + FileAbi, FnKinds, FnRpcBuilder, RpcReader, + ScValue, + ScValueEnum, ScValueOption, + ScValueStruct, + StateReader, TypeIndex, + StateBytes, + BlockchainAddress +} from "@partisiablockchain/abi-client"; +import {BigEndianByteOutput} from "@secata-public/bitmanipulation-ts"; + +const fileAbi: FileAbi = new AbiParser(Buffer.from( + "5042434142490a020005040000000013010000000d4c69717569646974794c6f636b0000000400000009616d6f756e745f696e050000000a616d6f756e745f6f7574050000000d746f6b656e735f696e5f6f7574000a000000056f776e65720d010000000d4c6f636b4c69717569646974790000000200000008615f746f6b656e730a00000008625f746f6b656e730a010000000c5669727475616c5374617465000000030000000c6e6578745f6c6f636b5f6964000e000000056c6f636b7319000e00000000000e6c6f636b5f6c69717569646974790001010000001a4c697175696469747953776170436f6e7472616374537461746500000005000000147065726d697373696f6e5f6c6f636b5f73776170000b000000166c69717569646974795f706f6f6c5f616464726573730d00000012737761705f6665655f7065725f6d696c6c65020000000e746f6b656e5f62616c616e63657300090000000d7669727475616c5f737461746500020200000005546f6b656e000000030000050100060200070100000006546f6b656e41000000000100000006546f6b656e4200000000010000000e4c6971756964697479546f6b656e00000000010000000c546f6b656e42616c616e63650000000300000008615f746f6b656e730500000008625f746f6b656e7305000000106c69717569646974795f746f6b656e7305010000000d546f6b656e42616c616e6365730000000400000010746f6b656e5f6c705f616464726573730d0000000f746f6b656e5f615f616464726573730d0000000f746f6b656e5f625f616464726573730d0000000862616c616e636573190d0008010000000b546f6b656e73496e4f75740000000200000008746f6b656e5f696e000400000009746f6b656e5f6f75740004020000000a5065726d697373696f6e0000000200000c01000d0100000007416e79626f6479000000000100000008537065636966696300000001000000096164647265737365730e0d010000000f4c69717569646974794c6f636b496400000001000000067261775f696405010000000b536563726574566172496400000001000000067261775f69640301000000134576656e74537562736372697074696f6e496400000001000000067261775f696408010000000f45787465726e616c4576656e74496400000001000000067261775f69640801000000124465706c6f7961626c65436f6e7472616374000000030000000862797465636f64650e01000000036162690e010000000776657273696f6e040000000c010000000a696e697469616c697a65ffffffff0f000000040000000f746f6b656e5f615f616464726573730d0000000f746f6b656e5f625f616464726573730d00000012737761705f6665655f7065725f6d696c6c6502000000147065726d697373696f6e5f6c6f636b5f73776170000b02000000076465706f73697401000000020000000d746f6b656e5f616464726573730d00000006616d6f756e740503000000106465706f7369745f63616c6c6261636b100000000200000005746f6b656e000400000006616d6f756e7405020000000c696e7374616e745f73776170020000000300000008746f6b656e5f696e0d00000009616d6f756e745f696e0500000012616d6f756e745f6f75745f6d696e696d756d050200000008776974686472617703000000030000000d746f6b656e5f616464726573730d00000006616d6f756e740500000011776169745f666f725f63616c6c6261636b0c0300000016776169745f77697468647261775f63616c6c6261636b1500000000020000001170726f766964655f6c697175696469747904000000020000000d746f6b656e5f616464726573730d00000006616d6f756e740502000000117265636c61696d5f6c69717569646974790500000001000000166c69717569646974795f746f6b656e5f616d6f756e7405020000001970726f766964655f696e697469616c5f6c697175696469747906000000020000000e746f6b656e5f615f616d6f756e74050000000e746f6b656e5f625f616d6f756e74050200000011616371756972655f737761705f6c6f636b070000000300000008746f6b656e5f696e0d00000009616d6f756e745f696e0500000012616d6f756e745f6f75745f6d696e696d756d050200000011657865637574655f6c6f636b5f737761700800000001000000076c6f636b5f6964000e020000000b63616e63656c5f6c6f636b0900000001000000076c6f636b5f6964000e0003", + "hex" +)).parseAbi(); + +type Option = K | undefined; + +export interface LiquidityLock { + amountIn: BN; + amountOut: BN; + tokensInOut: TokensInOut; + owner: BlockchainAddress; +} + +export function newLiquidityLock(amountIn: BN, amountOut: BN, tokensInOut: TokensInOut, owner: BlockchainAddress): LiquidityLock { + return {amountIn, amountOut, tokensInOut, owner}; +} + +function fromScValueLiquidityLock(structValue: ScValueStruct): LiquidityLock { + return { + amountIn: structValue.getFieldValue("amount_in")!.asBN(), + amountOut: structValue.getFieldValue("amount_out")!.asBN(), + tokensInOut: fromScValueTokensInOut(structValue.getFieldValue("tokens_in_out")!.structValue()), + owner: BlockchainAddress.fromBuffer(structValue.getFieldValue("owner")!.addressValue().value), + }; +} + +export interface LockLiquidity { + aTokens: BN; + bTokens: BN; +} + +export function newLockLiquidity(aTokens: BN, bTokens: BN): LockLiquidity { + return {aTokens, bTokens}; +} + +function fromScValueLockLiquidity(structValue: ScValueStruct): LockLiquidity { + return { + aTokens: structValue.getFieldValue("a_tokens")!.asBN(), + bTokens: structValue.getFieldValue("b_tokens")!.asBN(), + }; +} + +export interface VirtualState { + nextLockId: LiquidityLockId; + locks: Option>; + lockLiquidity: LockLiquidity; +} + +export function newVirtualState(nextLockId: LiquidityLockId, locks: Option>, lockLiquidity: LockLiquidity): VirtualState { + return {nextLockId, locks, lockLiquidity}; +} + +function fromScValueVirtualState(structValue: ScValueStruct): VirtualState { + return { + nextLockId: fromScValueLiquidityLockId(structValue.getFieldValue("next_lock_id")!.structValue()), + locks: structValue.getFieldValue("locks")!.avlTreeMapValue().mapKeysValues((k1) => fromScValueLiquidityLockId(k1.structValue()), (v2) => fromScValueLiquidityLock(v2.structValue())), + lockLiquidity: fromScValueLockLiquidity(structValue.getFieldValue("lock_liquidity")!.structValue()), + }; +} + +export interface LiquiditySwapContractState { + permissionLockSwap: Permission; + liquidityPoolAddress: BlockchainAddress; + swapFeePerMille: number; + tokenBalances: TokenBalances; + virtualState: VirtualState; +} + +export function newLiquiditySwapContractState(permissionLockSwap: Permission, liquidityPoolAddress: BlockchainAddress, swapFeePerMille: number, tokenBalances: TokenBalances, virtualState: VirtualState): LiquiditySwapContractState { + return {permissionLockSwap, liquidityPoolAddress, swapFeePerMille, tokenBalances, virtualState}; +} + +function fromScValueLiquiditySwapContractState(structValue: ScValueStruct): LiquiditySwapContractState { + return { + permissionLockSwap: fromScValuePermission(structValue.getFieldValue("permission_lock_swap")!.enumValue()), + liquidityPoolAddress: BlockchainAddress.fromBuffer(structValue.getFieldValue("liquidity_pool_address")!.addressValue().value), + swapFeePerMille: structValue.getFieldValue("swap_fee_per_mille")!.asNumber(), + tokenBalances: fromScValueTokenBalances(structValue.getFieldValue("token_balances")!.structValue()), + virtualState: fromScValueVirtualState(structValue.getFieldValue("virtual_state")!.structValue()), + }; +} + +export function deserializeLiquiditySwapContractState(state: StateBytes): LiquiditySwapContractState { + const scValue = new StateReader(state.state, fileAbi.contract, state.avlTrees).readState(); + return fromScValueLiquiditySwapContractState(scValue); +} + +export type Token = + | TokenTokenA + | TokenTokenB + | TokenLiquidityToken; + +export enum TokenD { + TokenA = 0, + TokenB = 1, + LiquidityToken = 2 +} + +function buildRpcToken(val: Token, builder: AbstractBuilder) { + if (val.discriminant === TokenD.TokenA) { + buildRpcTokenTokenA(val, builder); + } + if (val.discriminant === TokenD.TokenB) { + buildRpcTokenTokenB(val, builder); + } + if (val.discriminant === TokenD.LiquidityToken) { + buildRpcTokenLiquidityToken(val, builder); + } +} + +function fromScValueToken(enumValue: ScValueEnum): Token { + const item = enumValue.item; + if (item.name === "TokenA") { + return fromScValueTokenTokenA(item); + } + if (item.name === "TokenB") { + return fromScValueTokenTokenB(item); + } + if (item.name === "LiquidityToken") { + return fromScValueTokenLiquidityToken(item); + } + throw Error("Should not happen"); +} + +export interface TokenTokenA { + discriminant: TokenD.TokenA; +} + +export function newTokenTokenA(): TokenTokenA { + return {discriminant: 0, }; +} + +function buildRpcTokenTokenA(value: TokenTokenA, builder: AbstractBuilder) { + const enumVariantBuilder = builder.addEnumVariant(TokenD.TokenA); +} + +function fromScValueTokenTokenA(structValue: ScValueStruct): TokenTokenA { + return { + discriminant: TokenD.TokenA, + }; +} + +export interface TokenTokenB { + discriminant: TokenD.TokenB; +} + +export function newTokenTokenB(): TokenTokenB { + return {discriminant: 1, }; +} + +function buildRpcTokenTokenB(value: TokenTokenB, builder: AbstractBuilder) { + const enumVariantBuilder = builder.addEnumVariant(TokenD.TokenB); +} + +function fromScValueTokenTokenB(structValue: ScValueStruct): TokenTokenB { + return { + discriminant: TokenD.TokenB, + }; +} + +export interface TokenLiquidityToken { + discriminant: TokenD.LiquidityToken; +} + +export function newTokenLiquidityToken(): TokenLiquidityToken { + return {discriminant: 2, }; +} + +function buildRpcTokenLiquidityToken(value: TokenLiquidityToken, builder: AbstractBuilder) { + const enumVariantBuilder = builder.addEnumVariant(TokenD.LiquidityToken); +} + +function fromScValueTokenLiquidityToken(structValue: ScValueStruct): TokenLiquidityToken { + return { + discriminant: TokenD.LiquidityToken, + }; +} + +export interface TokenBalance { + aTokens: BN; + bTokens: BN; + liquidityTokens: BN; +} + +export function newTokenBalance(aTokens: BN, bTokens: BN, liquidityTokens: BN): TokenBalance { + return {aTokens, bTokens, liquidityTokens}; +} + +function fromScValueTokenBalance(structValue: ScValueStruct): TokenBalance { + return { + aTokens: structValue.getFieldValue("a_tokens")!.asBN(), + bTokens: structValue.getFieldValue("b_tokens")!.asBN(), + liquidityTokens: structValue.getFieldValue("liquidity_tokens")!.asBN(), + }; +} + +export interface TokenBalances { + tokenLpAddress: BlockchainAddress; + tokenAAddress: BlockchainAddress; + tokenBAddress: BlockchainAddress; + balances: Option>; +} + +export function newTokenBalances(tokenLpAddress: BlockchainAddress, tokenAAddress: BlockchainAddress, tokenBAddress: BlockchainAddress, balances: Option>): TokenBalances { + return {tokenLpAddress, tokenAAddress, tokenBAddress, balances}; +} + +function fromScValueTokenBalances(structValue: ScValueStruct): TokenBalances { + return { + tokenLpAddress: BlockchainAddress.fromBuffer(structValue.getFieldValue("token_lp_address")!.addressValue().value), + tokenAAddress: BlockchainAddress.fromBuffer(structValue.getFieldValue("token_a_address")!.addressValue().value), + tokenBAddress: BlockchainAddress.fromBuffer(structValue.getFieldValue("token_b_address")!.addressValue().value), + balances: structValue.getFieldValue("balances")!.avlTreeMapValue().mapKeysValues((k3) => BlockchainAddress.fromBuffer(k3.addressValue().value), (v4) => fromScValueTokenBalance(v4.structValue())), + }; +} + +export interface TokensInOut { + tokenIn: Token; + tokenOut: Token; +} + +export function newTokensInOut(tokenIn: Token, tokenOut: Token): TokensInOut { + return {tokenIn, tokenOut}; +} + +function fromScValueTokensInOut(structValue: ScValueStruct): TokensInOut { + return { + tokenIn: fromScValueToken(structValue.getFieldValue("token_in")!.enumValue()), + tokenOut: fromScValueToken(structValue.getFieldValue("token_out")!.enumValue()), + }; +} + +export type Permission = + | PermissionAnybody + | PermissionSpecific; + +export enum PermissionD { + Anybody = 0, + Specific = 1 +} + +function buildRpcPermission(val: Permission, builder: AbstractBuilder) { + if (val.discriminant === PermissionD.Anybody) { + buildRpcPermissionAnybody(val, builder); + } + if (val.discriminant === PermissionD.Specific) { + buildRpcPermissionSpecific(val, builder); + } +} + +function fromScValuePermission(enumValue: ScValueEnum): Permission { + const item = enumValue.item; + if (item.name === "Anybody") { + return fromScValuePermissionAnybody(item); + } + if (item.name === "Specific") { + return fromScValuePermissionSpecific(item); + } + throw Error("Should not happen"); +} + +export interface PermissionAnybody { + discriminant: PermissionD.Anybody; +} + +export function newPermissionAnybody(): PermissionAnybody { + return {discriminant: 0, }; +} + +function buildRpcPermissionAnybody(value: PermissionAnybody, builder: AbstractBuilder) { + const enumVariantBuilder = builder.addEnumVariant(PermissionD.Anybody); +} + +function fromScValuePermissionAnybody(structValue: ScValueStruct): PermissionAnybody { + return { + discriminant: PermissionD.Anybody, + }; +} + +export interface PermissionSpecific { + discriminant: PermissionD.Specific; + addresses: BlockchainAddress[]; +} + +export function newPermissionSpecific(addresses: BlockchainAddress[]): PermissionSpecific { + return {discriminant: 1, addresses, }; +} + +function buildRpcPermissionSpecific(value: PermissionSpecific, builder: AbstractBuilder) { + const enumVariantBuilder = builder.addEnumVariant(PermissionD.Specific); + const vecBuilder5 = enumVariantBuilder.addVec(); + for (const vecEntry6 of value.addresses) { + vecBuilder5.addAddress(vecEntry6.asBuffer()); + } +} + +function fromScValuePermissionSpecific(structValue: ScValueStruct): PermissionSpecific { + return { + discriminant: PermissionD.Specific, + addresses: structValue.getFieldValue("addresses")!.vecValue().values().map((sc7) => BlockchainAddress.fromBuffer(sc7.addressValue().value)), + }; +} + +export interface LiquidityLockId { + rawId: BN; +} + +export function newLiquidityLockId(rawId: BN): LiquidityLockId { + return {rawId}; +} + +function fromScValueLiquidityLockId(structValue: ScValueStruct): LiquidityLockId { + return { + rawId: structValue.getFieldValue("raw_id")!.asBN(), + }; +} + +function buildRpcLiquidityLockId(value: LiquidityLockId, builder: AbstractBuilder) { + const structBuilder = builder.addStruct(); + structBuilder.addU128(value.rawId); +} + +export interface SecretVarId { + rawId: number; +} + +export function newSecretVarId(rawId: number): SecretVarId { + return {rawId}; +} + +function fromScValueSecretVarId(structValue: ScValueStruct): SecretVarId { + return { + rawId: structValue.getFieldValue("raw_id")!.asNumber(), + }; +} + +export interface EventSubscriptionId { + rawId: number; +} + +export function newEventSubscriptionId(rawId: number): EventSubscriptionId { + return {rawId}; +} + +function fromScValueEventSubscriptionId(structValue: ScValueStruct): EventSubscriptionId { + return { + rawId: structValue.getFieldValue("raw_id")!.asNumber(), + }; +} + +export interface ExternalEventId { + rawId: number; +} + +export function newExternalEventId(rawId: number): ExternalEventId { + return {rawId}; +} + +function fromScValueExternalEventId(structValue: ScValueStruct): ExternalEventId { + return { + rawId: structValue.getFieldValue("raw_id")!.asNumber(), + }; +} + +export interface DeployableContract { + bytecode: Buffer; + abi: Buffer; + version: BN; +} + +export function newDeployableContract(bytecode: Buffer, abi: Buffer, version: BN): DeployableContract { + return {bytecode, abi, version}; +} + +function fromScValueDeployableContract(structValue: ScValueStruct): DeployableContract { + return { + bytecode: structValue.getFieldValue("bytecode")!.vecU8Value(), + abi: structValue.getFieldValue("abi")!.vecU8Value(), + version: structValue.getFieldValue("version")!.asBN(), + }; +} + +export function initialize(tokenAAddress: BlockchainAddress, tokenBAddress: BlockchainAddress, swapFeePerMille: number, permissionLockSwap: Permission): Buffer { + const fnBuilder = new FnRpcBuilder("initialize", fileAbi.contract); + fnBuilder.addAddress(tokenAAddress.asBuffer()); + fnBuilder.addAddress(tokenBAddress.asBuffer()); + fnBuilder.addU16(swapFeePerMille); + buildRpcPermission(permissionLockSwap, fnBuilder); + return fnBuilder.getBytes(); +} + +export function deposit(tokenAddress: BlockchainAddress, amount: BN): Buffer { + const fnBuilder = new FnRpcBuilder("deposit", fileAbi.contract); + fnBuilder.addAddress(tokenAddress.asBuffer()); + fnBuilder.addU128(amount); + return fnBuilder.getBytes(); +} + +export function instantSwap(tokenIn: BlockchainAddress, amountIn: BN, amountOutMinimum: BN): Buffer { + const fnBuilder = new FnRpcBuilder("instant_swap", fileAbi.contract); + fnBuilder.addAddress(tokenIn.asBuffer()); + fnBuilder.addU128(amountIn); + fnBuilder.addU128(amountOutMinimum); + return fnBuilder.getBytes(); +} + +export function withdraw(tokenAddress: BlockchainAddress, amount: BN, waitForCallback: boolean): Buffer { + const fnBuilder = new FnRpcBuilder("withdraw", fileAbi.contract); + fnBuilder.addAddress(tokenAddress.asBuffer()); + fnBuilder.addU128(amount); + fnBuilder.addBool(waitForCallback); + return fnBuilder.getBytes(); +} + +export function provideLiquidity(tokenAddress: BlockchainAddress, amount: BN): Buffer { + const fnBuilder = new FnRpcBuilder("provide_liquidity", fileAbi.contract); + fnBuilder.addAddress(tokenAddress.asBuffer()); + fnBuilder.addU128(amount); + return fnBuilder.getBytes(); +} + +export function reclaimLiquidity(liquidityTokenAmount: BN): Buffer { + const fnBuilder = new FnRpcBuilder("reclaim_liquidity", fileAbi.contract); + fnBuilder.addU128(liquidityTokenAmount); + return fnBuilder.getBytes(); +} + +export function provideInitialLiquidity(tokenAAmount: BN, tokenBAmount: BN): Buffer { + const fnBuilder = new FnRpcBuilder("provide_initial_liquidity", fileAbi.contract); + fnBuilder.addU128(tokenAAmount); + fnBuilder.addU128(tokenBAmount); + return fnBuilder.getBytes(); +} + +export function acquireSwapLock(tokenIn: BlockchainAddress, amountIn: BN, amountOutMinimum: BN): Buffer { + const fnBuilder = new FnRpcBuilder("acquire_swap_lock", fileAbi.contract); + fnBuilder.addAddress(tokenIn.asBuffer()); + fnBuilder.addU128(amountIn); + fnBuilder.addU128(amountOutMinimum); + return fnBuilder.getBytes(); +} + +export function executeLockSwap(lockId: LiquidityLockId): Buffer { + const fnBuilder = new FnRpcBuilder("execute_lock_swap", fileAbi.contract); + buildRpcLiquidityLockId(lockId, fnBuilder); + return fnBuilder.getBytes(); +} + +export function cancelLock(lockId: LiquidityLockId): Buffer { + const fnBuilder = new FnRpcBuilder("cancel_lock", fileAbi.contract); + buildRpcLiquidityLockId(lockId, fnBuilder); + return fnBuilder.getBytes(); +} + diff --git a/src/main/abi/SwapFactory.ts b/src/main/abi/SwapFactory.ts new file mode 100644 index 0000000..63b3dce --- /dev/null +++ b/src/main/abi/SwapFactory.ts @@ -0,0 +1,411 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +import BN from "bn.js"; +import { + AbiParser, + AbstractBuilder, BigEndianReader, + FileAbi, FnKinds, FnRpcBuilder, RpcReader, + ScValue, + ScValueEnum, ScValueOption, + ScValueStruct, + StateReader, TypeIndex, + StateBytes, + BlockchainAddress +} from "@partisiablockchain/abi-client"; +import {BigEndianByteOutput} from "@secata-public/bitmanipulation-ts"; + +const fileAbi: FileAbi = new AbiParser(Buffer.from( + "5042434142490a0200050400000000130100000009546f6b656e50616972000000020000000f746f6b656e5f615f616464726573730d0000000f746f6b656e5f625f616464726573730d010000001053776170436f6e7472616374496e666f0000000400000010636f6e74726163745f76657273696f6e040000000a746f6b656e5f706169720000000000157375636365737366756c6c795f6465706c6f7965640c0000000e737570706f7274735f6c6f636b730c010000001353776170436f6e7472616374496e69744d7367000000030000000f746f6b656e5f615f616464726573730d0000000f746f6b656e5f625f616464726573730d00000012737761705f6665655f7065725f6d696c6c6502010000001053776170466163746f7279537461746500000005000000167065726d697373696f6e5f7570646174655f737761700005000000167065726d697373696f6e5f6465706c6f795f7377617000050000000e737761705f636f6e747261637473190d000100000014737761705f636f6e74726163745f62696e61727912000400000012737761705f6665655f7065725f6d696c6c650201000000124465706c6f7961626c65436f6e7472616374000000030000000862797465636f64650e01000000036162690e010000000776657273696f6e04020000000a5065726d697373696f6e000000020000060100070100000007416e79626f6479000000000100000008537065636966696300000001000000096164647265737365730e0d0200000005546f6b656e0000000300000901000a02000b0100000006546f6b656e41000000000100000006546f6b656e4200000000010000000e4c6971756964697479546f6b656e00000000010000000c546f6b656e42616c616e63650000000300000008615f746f6b656e730500000008625f746f6b656e7305000000106c69717569646974795f746f6b656e7305010000000d546f6b656e42616c616e6365730000000400000010746f6b656e5f6c705f616464726573730d0000000f746f6b656e5f615f616464726573730d0000000f746f6b656e5f625f616464726573730d0000000862616c616e636573190d000c010000000b546f6b656e73496e4f75740000000200000008746f6b656e5f696e000800000009746f6b656e5f6f75740008010000000b536563726574566172496400000001000000067261775f69640301000000134576656e74537562736372697074696f6e496400000001000000067261775f696408010000000f45787465726e616c4576656e74496400000001000000067261775f696408010000000f4c69717569646974794c6f636b496400000001000000067261775f69640500000007010000000a696e697469616c697a65ffffffff0f00000003000000167065726d697373696f6e5f7570646174655f737761700005000000167065726d697373696f6e5f6465706c6f795f73776170000500000012737761705f6665655f7065725f6d696c6c650202000000127570646174655f737761705f62696e617279100000000300000014737761705f636f6e74726163745f62696e6172790e0100000011737761705f636f6e74726163745f6162690e0100000015737761705f636f6e74726163745f76657273696f6e0402000000146465706c6f795f737761705f636f6e747261637401000000010000000a746f6b656e5f70616972000002000000196465706c6f795f737761705f6c6f636b5f636f6e747261637403000000020000000a746f6b656e5f7061697200000000000f6c6f636b5f7065726d697373696f6e0005030000001d6465706c6f795f737761705f636f6e74726163745f63616c6c6261636b01000000010000000c737761705f616464726573730d030000001d737761705f636f6e74726163745f6578697374735f63616c6c6261636b02000000010000000c737761705f616464726573730d020000001464656c6973745f737761705f636f6e7472616374020000000100000007616464726573730d0003", + "hex" +)).parseAbi(); + +type Option = K | undefined; + +export interface TokenPair { + tokenAAddress: BlockchainAddress; + tokenBAddress: BlockchainAddress; +} + +export function newTokenPair(tokenAAddress: BlockchainAddress, tokenBAddress: BlockchainAddress): TokenPair { + return {tokenAAddress, tokenBAddress}; +} + +function fromScValueTokenPair(structValue: ScValueStruct): TokenPair { + return { + tokenAAddress: BlockchainAddress.fromBuffer(structValue.getFieldValue("token_a_address")!.addressValue().value), + tokenBAddress: BlockchainAddress.fromBuffer(structValue.getFieldValue("token_b_address")!.addressValue().value), + }; +} + +function buildRpcTokenPair(value: TokenPair, builder: AbstractBuilder) { + const structBuilder = builder.addStruct(); + structBuilder.addAddress(value.tokenAAddress.asBuffer()); + structBuilder.addAddress(value.tokenBAddress.asBuffer()); +} + +export interface SwapContractInfo { + contractVersion: BN; + tokenPair: TokenPair; + successfullyDeployed: boolean; + supportsLocks: boolean; +} + +export function newSwapContractInfo(contractVersion: BN, tokenPair: TokenPair, successfullyDeployed: boolean, supportsLocks: boolean): SwapContractInfo { + return {contractVersion, tokenPair, successfullyDeployed, supportsLocks}; +} + +function fromScValueSwapContractInfo(structValue: ScValueStruct): SwapContractInfo { + return { + contractVersion: structValue.getFieldValue("contract_version")!.asBN(), + tokenPair: fromScValueTokenPair(structValue.getFieldValue("token_pair")!.structValue()), + successfullyDeployed: structValue.getFieldValue("successfully_deployed")!.boolValue(), + supportsLocks: structValue.getFieldValue("supports_locks")!.boolValue(), + }; +} + +export interface SwapContractInitMsg { + tokenAAddress: BlockchainAddress; + tokenBAddress: BlockchainAddress; + swapFeePerMille: number; +} + +export function newSwapContractInitMsg(tokenAAddress: BlockchainAddress, tokenBAddress: BlockchainAddress, swapFeePerMille: number): SwapContractInitMsg { + return {tokenAAddress, tokenBAddress, swapFeePerMille}; +} + +function fromScValueSwapContractInitMsg(structValue: ScValueStruct): SwapContractInitMsg { + return { + tokenAAddress: BlockchainAddress.fromBuffer(structValue.getFieldValue("token_a_address")!.addressValue().value), + tokenBAddress: BlockchainAddress.fromBuffer(structValue.getFieldValue("token_b_address")!.addressValue().value), + swapFeePerMille: structValue.getFieldValue("swap_fee_per_mille")!.asNumber(), + }; +} + +export interface SwapFactoryState { + permissionUpdateSwap: Permission; + permissionDeploySwap: Permission; + swapContracts: Option>; + swapContractBinary: Option; + swapFeePerMille: number; +} + +export function newSwapFactoryState(permissionUpdateSwap: Permission, permissionDeploySwap: Permission, swapContracts: Option>, swapContractBinary: Option, swapFeePerMille: number): SwapFactoryState { + return {permissionUpdateSwap, permissionDeploySwap, swapContracts, swapContractBinary, swapFeePerMille}; +} + +function fromScValueSwapFactoryState(structValue: ScValueStruct): SwapFactoryState { + return { + permissionUpdateSwap: fromScValuePermission(structValue.getFieldValue("permission_update_swap")!.enumValue()), + permissionDeploySwap: fromScValuePermission(structValue.getFieldValue("permission_deploy_swap")!.enumValue()), + swapContracts: structValue.getFieldValue("swap_contracts")!.avlTreeMapValue().mapKeysValues((k1) => BlockchainAddress.fromBuffer(k1.addressValue().value), (v2) => fromScValueSwapContractInfo(v2.structValue())), + swapContractBinary: structValue.getFieldValue("swap_contract_binary")!.optionValue().valueOrUndefined((sc3) => fromScValueDeployableContract(sc3.structValue())), + swapFeePerMille: structValue.getFieldValue("swap_fee_per_mille")!.asNumber(), + }; +} + +export function deserializeSwapFactoryState(state: StateBytes): SwapFactoryState { + const scValue = new StateReader(state.state, fileAbi.contract, state.avlTrees).readState(); + return fromScValueSwapFactoryState(scValue); +} + +export interface DeployableContract { + bytecode: Buffer; + abi: Buffer; + version: BN; +} + +export function newDeployableContract(bytecode: Buffer, abi: Buffer, version: BN): DeployableContract { + return {bytecode, abi, version}; +} + +function fromScValueDeployableContract(structValue: ScValueStruct): DeployableContract { + return { + bytecode: structValue.getFieldValue("bytecode")!.vecU8Value(), + abi: structValue.getFieldValue("abi")!.vecU8Value(), + version: structValue.getFieldValue("version")!.asBN(), + }; +} + +export type Permission = + | PermissionAnybody + | PermissionSpecific; + +export enum PermissionD { + Anybody = 0, + Specific = 1 +} + +function buildRpcPermission(val: Permission, builder: AbstractBuilder) { + if (val.discriminant === PermissionD.Anybody) { + buildRpcPermissionAnybody(val, builder); + } + if (val.discriminant === PermissionD.Specific) { + buildRpcPermissionSpecific(val, builder); + } +} + +function fromScValuePermission(enumValue: ScValueEnum): Permission { + const item = enumValue.item; + if (item.name === "Anybody") { + return fromScValuePermissionAnybody(item); + } + if (item.name === "Specific") { + return fromScValuePermissionSpecific(item); + } + throw Error("Should not happen"); +} + +export interface PermissionAnybody { + discriminant: PermissionD.Anybody; +} + +export function newPermissionAnybody(): PermissionAnybody { + return {discriminant: 0, }; +} + +function buildRpcPermissionAnybody(value: PermissionAnybody, builder: AbstractBuilder) { + const enumVariantBuilder = builder.addEnumVariant(PermissionD.Anybody); +} + +function fromScValuePermissionAnybody(structValue: ScValueStruct): PermissionAnybody { + return { + discriminant: PermissionD.Anybody, + }; +} + +export interface PermissionSpecific { + discriminant: PermissionD.Specific; + addresses: BlockchainAddress[]; +} + +export function newPermissionSpecific(addresses: BlockchainAddress[]): PermissionSpecific { + return {discriminant: 1, addresses, }; +} + +function buildRpcPermissionSpecific(value: PermissionSpecific, builder: AbstractBuilder) { + const enumVariantBuilder = builder.addEnumVariant(PermissionD.Specific); + const vecBuilder4 = enumVariantBuilder.addVec(); + for (const vecEntry5 of value.addresses) { + vecBuilder4.addAddress(vecEntry5.asBuffer()); + } +} + +function fromScValuePermissionSpecific(structValue: ScValueStruct): PermissionSpecific { + return { + discriminant: PermissionD.Specific, + addresses: structValue.getFieldValue("addresses")!.vecValue().values().map((sc6) => BlockchainAddress.fromBuffer(sc6.addressValue().value)), + }; +} + +export type Token = + | TokenTokenA + | TokenTokenB + | TokenLiquidityToken; + +export enum TokenD { + TokenA = 0, + TokenB = 1, + LiquidityToken = 2 +} + +function fromScValueToken(enumValue: ScValueEnum): Token { + const item = enumValue.item; + if (item.name === "TokenA") { + return fromScValueTokenTokenA(item); + } + if (item.name === "TokenB") { + return fromScValueTokenTokenB(item); + } + if (item.name === "LiquidityToken") { + return fromScValueTokenLiquidityToken(item); + } + throw Error("Should not happen"); +} + +export interface TokenTokenA { + discriminant: TokenD.TokenA; +} + +export function newTokenTokenA(): TokenTokenA { + return {discriminant: 0, }; +} + +function fromScValueTokenTokenA(structValue: ScValueStruct): TokenTokenA { + return { + discriminant: TokenD.TokenA, + }; +} + +export interface TokenTokenB { + discriminant: TokenD.TokenB; +} + +export function newTokenTokenB(): TokenTokenB { + return {discriminant: 1, }; +} + +function fromScValueTokenTokenB(structValue: ScValueStruct): TokenTokenB { + return { + discriminant: TokenD.TokenB, + }; +} + +export interface TokenLiquidityToken { + discriminant: TokenD.LiquidityToken; +} + +export function newTokenLiquidityToken(): TokenLiquidityToken { + return {discriminant: 2, }; +} + +function fromScValueTokenLiquidityToken(structValue: ScValueStruct): TokenLiquidityToken { + return { + discriminant: TokenD.LiquidityToken, + }; +} + +export interface TokenBalance { + aTokens: BN; + bTokens: BN; + liquidityTokens: BN; +} + +export function newTokenBalance(aTokens: BN, bTokens: BN, liquidityTokens: BN): TokenBalance { + return {aTokens, bTokens, liquidityTokens}; +} + +function fromScValueTokenBalance(structValue: ScValueStruct): TokenBalance { + return { + aTokens: structValue.getFieldValue("a_tokens")!.asBN(), + bTokens: structValue.getFieldValue("b_tokens")!.asBN(), + liquidityTokens: structValue.getFieldValue("liquidity_tokens")!.asBN(), + }; +} + +export interface TokenBalances { + tokenLpAddress: BlockchainAddress; + tokenAAddress: BlockchainAddress; + tokenBAddress: BlockchainAddress; + balances: Option>; +} + +export function newTokenBalances(tokenLpAddress: BlockchainAddress, tokenAAddress: BlockchainAddress, tokenBAddress: BlockchainAddress, balances: Option>): TokenBalances { + return {tokenLpAddress, tokenAAddress, tokenBAddress, balances}; +} + +function fromScValueTokenBalances(structValue: ScValueStruct): TokenBalances { + return { + tokenLpAddress: BlockchainAddress.fromBuffer(structValue.getFieldValue("token_lp_address")!.addressValue().value), + tokenAAddress: BlockchainAddress.fromBuffer(structValue.getFieldValue("token_a_address")!.addressValue().value), + tokenBAddress: BlockchainAddress.fromBuffer(structValue.getFieldValue("token_b_address")!.addressValue().value), + balances: structValue.getFieldValue("balances")!.avlTreeMapValue().mapKeysValues((k7) => BlockchainAddress.fromBuffer(k7.addressValue().value), (v8) => fromScValueTokenBalance(v8.structValue())), + }; +} + +export interface TokensInOut { + tokenIn: Token; + tokenOut: Token; +} + +export function newTokensInOut(tokenIn: Token, tokenOut: Token): TokensInOut { + return {tokenIn, tokenOut}; +} + +function fromScValueTokensInOut(structValue: ScValueStruct): TokensInOut { + return { + tokenIn: fromScValueToken(structValue.getFieldValue("token_in")!.enumValue()), + tokenOut: fromScValueToken(structValue.getFieldValue("token_out")!.enumValue()), + }; +} + +export interface SecretVarId { + rawId: number; +} + +export function newSecretVarId(rawId: number): SecretVarId { + return {rawId}; +} + +function fromScValueSecretVarId(structValue: ScValueStruct): SecretVarId { + return { + rawId: structValue.getFieldValue("raw_id")!.asNumber(), + }; +} + +export interface EventSubscriptionId { + rawId: number; +} + +export function newEventSubscriptionId(rawId: number): EventSubscriptionId { + return {rawId}; +} + +function fromScValueEventSubscriptionId(structValue: ScValueStruct): EventSubscriptionId { + return { + rawId: structValue.getFieldValue("raw_id")!.asNumber(), + }; +} + +export interface ExternalEventId { + rawId: number; +} + +export function newExternalEventId(rawId: number): ExternalEventId { + return {rawId}; +} + +function fromScValueExternalEventId(structValue: ScValueStruct): ExternalEventId { + return { + rawId: structValue.getFieldValue("raw_id")!.asNumber(), + }; +} + +export interface LiquidityLockId { + rawId: BN; +} + +export function newLiquidityLockId(rawId: BN): LiquidityLockId { + return {rawId}; +} + +function fromScValueLiquidityLockId(structValue: ScValueStruct): LiquidityLockId { + return { + rawId: structValue.getFieldValue("raw_id")!.asBN(), + }; +} + +export function initialize(permissionUpdateSwap: Permission, permissionDeploySwap: Permission, swapFeePerMille: number): Buffer { + const fnBuilder = new FnRpcBuilder("initialize", fileAbi.contract); + buildRpcPermission(permissionUpdateSwap, fnBuilder); + buildRpcPermission(permissionDeploySwap, fnBuilder); + fnBuilder.addU16(swapFeePerMille); + return fnBuilder.getBytes(); +} + +export function updateSwapBinary(swapContractBinary: Buffer, swapContractAbi: Buffer, swapContractVersion: BN): Buffer { + const fnBuilder = new FnRpcBuilder("update_swap_binary", fileAbi.contract); + fnBuilder.addVecU8(swapContractBinary); + fnBuilder.addVecU8(swapContractAbi); + fnBuilder.addU64(swapContractVersion); + return fnBuilder.getBytes(); +} + +export function deploySwapContract(tokenPair: TokenPair): Buffer { + const fnBuilder = new FnRpcBuilder("deploy_swap_contract", fileAbi.contract); + buildRpcTokenPair(tokenPair, fnBuilder); + return fnBuilder.getBytes(); +} + +export function deploySwapLockContract(tokenPair: TokenPair, lockPermission: Permission): Buffer { + const fnBuilder = new FnRpcBuilder("deploy_swap_lock_contract", fileAbi.contract); + buildRpcTokenPair(tokenPair, fnBuilder); + buildRpcPermission(lockPermission, fnBuilder); + return fnBuilder.getBytes(); +} + +export function delistSwapContract(address: BlockchainAddress): Buffer { + const fnBuilder = new FnRpcBuilder("delist_swap_contract", fileAbi.contract); + fnBuilder.addAddress(address.asBuffer()); + return fnBuilder.getBytes(); +} + diff --git a/src/main/abi/SwapRouter.ts b/src/main/abi/SwapRouter.ts new file mode 100644 index 0000000..119d179 --- /dev/null +++ b/src/main/abi/SwapRouter.ts @@ -0,0 +1,480 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +import BN from "bn.js"; +import { + AbiParser, + AbstractBuilder, BigEndianReader, + FileAbi, FnKinds, FnRpcBuilder, RpcReader, + ScValue, + ScValueEnum, ScValueOption, + ScValueStruct, + StateReader, TypeIndex, + StateBytes, + BlockchainAddress +} from "@partisiablockchain/abi-client"; +import {BigEndianByteOutput} from "@secata-public/bitmanipulation-ts"; + +const fileAbi: FileAbi = new AbiParser(Buffer.from( + "5042434142490a020005040000000017010000000f53776170496e666f726d6174696f6e000000030000000c737761705f616464726573730d00000008746f6b656e5f696e0d00000009746f6b656e5f6f75740d010000000e57616e7465644c6f636b496e666f0000000300000009737761705f696e666f000000000009616d6f756e745f696e0500000012616d6f756e745f6f75745f6d696e696d756d05010000001041637175697265644c6f636b496e666f0000000200000009737761705f696e666f0000000000076c6f636b5f6964000b010000001350656e64696e675769746864726177496e666f000000020000000c737761705f616464726573730d0000000e77697468647261775f746f6b656e0d010000001053776170436f6e7472616374496e666f000000030000000c737761705f616464726573730d0000000f746f6b656e5f615f616464726573730d0000000f746f6b656e5f625f616464726573730d0100000010526f757465496e666f726d6174696f6e0000000800000004757365720d00000011696e697469616c5f616d6f756e745f696e0500000010696e697469616c5f746f6b656e5f696e0d0000001566696e616c5f72656365697665645f616d6f756e74050000000f66696e616c5f746f6b656e5f6f75740d0000000c6c6f636b735f77616e7465640e00010000001b6c6f636b735f77616974696e675f666f725f657865637574696f6e0e00020000001070656e64696e675f7769746864726177120003010000000c526f757465547261636b6572000000020000000d6e6578745f726f7574655f6964050000000d6163746976655f726f7574657319050005010000000b526f75746572537461746500000003000000137065726d697373696f6e5f6164645f7377617000080000000e737761705f636f6e7472616374730e00040000000d726f7574655f747261636b65720006020000000a5065726d697373696f6e0000000200000901000a0100000007416e79626f6479000000000100000008537065636966696300000001000000096164647265737365730e0d010000000f4c69717569646974794c6f636b496400000001000000067261775f6964050200000005546f6b656e0000000300000d01000e02000f0100000006546f6b656e41000000000100000006546f6b656e4200000000010000000e4c6971756964697479546f6b656e00000000010000000c546f6b656e42616c616e63650000000300000008615f746f6b656e730500000008625f746f6b656e7305000000106c69717569646974795f746f6b656e7305010000000d546f6b656e42616c616e6365730000000400000010746f6b656e5f6c705f616464726573730d0000000f746f6b656e5f615f616464726573730d0000000f746f6b656e5f625f616464726573730d0000000862616c616e636573190d0010010000000b546f6b656e73496e4f75740000000200000008746f6b656e5f696e000c00000009746f6b656e5f6f7574000c010000000b536563726574566172496400000001000000067261775f69640301000000134576656e74537562736372697074696f6e496400000001000000067261775f696408010000000f45787465726e616c4576656e74496400000001000000067261775f69640801000000124465706c6f7961626c65436f6e7472616374000000030000000862797465636f64650e01000000036162690e010000000776657273696f6e040000000a010000000a696e697469616c697a65ffffffff0f00000002000000137065726d697373696f6e5f6164645f7377617000080000000e737761705f636f6e7472616374730e0004020000000a726f7574655f7377617001000000050000000a737761705f726f7574650e0d00000008746f6b656e5f696e0d00000009746f6b656e5f6f75740d00000009616d6f756e745f696e0500000012616d6f756e745f6f75745f6d696e696d756d05030000001973746172745f6c6f636b5f636861696e5f63616c6c6261636b200000000100000008726f7574655f69640503000000136c6f636b5f726f7574655f63616c6c6261636b030000000100000008726f7574655f6964050300000016657865637574655f726f7574655f63616c6c6261636b040000000200000008726f7574655f6964050000000b6c6173745f6f7574707574050300000010617070726f76655f63616c6c6261636b150000000200000008726f7574655f6964050000000b6c6173745f6f75747075740503000000106465706f7369745f63616c6c6261636b160000000100000008726f7574655f696405030000001e726563656976655f6f75747075745f616d6f756e745f63616c6c6261636b050000000100000008726f7574655f696405030000001c636f756c645f6e6f745f616371756972655f6c6f636b5f6572726f72070000000002000000116164645f737761705f636f6e747261637408000000030000000c737761705f616464726573730d0000000f746f6b656e5f615f616464726573730d0000000f746f6b656e5f625f616464726573730d0007", + "hex" +)).parseAbi(); + +type Option = K | undefined; + +export interface SwapInformation { + swapAddress: BlockchainAddress; + tokenIn: BlockchainAddress; + tokenOut: BlockchainAddress; +} + +export function newSwapInformation(swapAddress: BlockchainAddress, tokenIn: BlockchainAddress, tokenOut: BlockchainAddress): SwapInformation { + return {swapAddress, tokenIn, tokenOut}; +} + +function fromScValueSwapInformation(structValue: ScValueStruct): SwapInformation { + return { + swapAddress: BlockchainAddress.fromBuffer(structValue.getFieldValue("swap_address")!.addressValue().value), + tokenIn: BlockchainAddress.fromBuffer(structValue.getFieldValue("token_in")!.addressValue().value), + tokenOut: BlockchainAddress.fromBuffer(structValue.getFieldValue("token_out")!.addressValue().value), + }; +} + +export interface WantedLockInfo { + swapInfo: SwapInformation; + amountIn: BN; + amountOutMinimum: BN; +} + +export function newWantedLockInfo(swapInfo: SwapInformation, amountIn: BN, amountOutMinimum: BN): WantedLockInfo { + return {swapInfo, amountIn, amountOutMinimum}; +} + +function fromScValueWantedLockInfo(structValue: ScValueStruct): WantedLockInfo { + return { + swapInfo: fromScValueSwapInformation(structValue.getFieldValue("swap_info")!.structValue()), + amountIn: structValue.getFieldValue("amount_in")!.asBN(), + amountOutMinimum: structValue.getFieldValue("amount_out_minimum")!.asBN(), + }; +} + +export interface AcquiredLockInfo { + swapInfo: SwapInformation; + lockId: LiquidityLockId; +} + +export function newAcquiredLockInfo(swapInfo: SwapInformation, lockId: LiquidityLockId): AcquiredLockInfo { + return {swapInfo, lockId}; +} + +function fromScValueAcquiredLockInfo(structValue: ScValueStruct): AcquiredLockInfo { + return { + swapInfo: fromScValueSwapInformation(structValue.getFieldValue("swap_info")!.structValue()), + lockId: fromScValueLiquidityLockId(structValue.getFieldValue("lock_id")!.structValue()), + }; +} + +export interface PendingWithdrawInfo { + swapAddress: BlockchainAddress; + withdrawToken: BlockchainAddress; +} + +export function newPendingWithdrawInfo(swapAddress: BlockchainAddress, withdrawToken: BlockchainAddress): PendingWithdrawInfo { + return {swapAddress, withdrawToken}; +} + +function fromScValuePendingWithdrawInfo(structValue: ScValueStruct): PendingWithdrawInfo { + return { + swapAddress: BlockchainAddress.fromBuffer(structValue.getFieldValue("swap_address")!.addressValue().value), + withdrawToken: BlockchainAddress.fromBuffer(structValue.getFieldValue("withdraw_token")!.addressValue().value), + }; +} + +export interface SwapContractInfo { + swapAddress: BlockchainAddress; + tokenAAddress: BlockchainAddress; + tokenBAddress: BlockchainAddress; +} + +export function newSwapContractInfo(swapAddress: BlockchainAddress, tokenAAddress: BlockchainAddress, tokenBAddress: BlockchainAddress): SwapContractInfo { + return {swapAddress, tokenAAddress, tokenBAddress}; +} + +function fromScValueSwapContractInfo(structValue: ScValueStruct): SwapContractInfo { + return { + swapAddress: BlockchainAddress.fromBuffer(structValue.getFieldValue("swap_address")!.addressValue().value), + tokenAAddress: BlockchainAddress.fromBuffer(structValue.getFieldValue("token_a_address")!.addressValue().value), + tokenBAddress: BlockchainAddress.fromBuffer(structValue.getFieldValue("token_b_address")!.addressValue().value), + }; +} + +function buildRpcSwapContractInfo(value: SwapContractInfo, builder: AbstractBuilder) { + const structBuilder = builder.addStruct(); + structBuilder.addAddress(value.swapAddress.asBuffer()); + structBuilder.addAddress(value.tokenAAddress.asBuffer()); + structBuilder.addAddress(value.tokenBAddress.asBuffer()); +} + +export interface RouteInformation { + user: BlockchainAddress; + initialAmountIn: BN; + initialTokenIn: BlockchainAddress; + finalReceivedAmount: BN; + finalTokenOut: BlockchainAddress; + locksWanted: WantedLockInfo[]; + locksWaitingForExecution: AcquiredLockInfo[]; + pendingWithdraw: Option; +} + +export function newRouteInformation(user: BlockchainAddress, initialAmountIn: BN, initialTokenIn: BlockchainAddress, finalReceivedAmount: BN, finalTokenOut: BlockchainAddress, locksWanted: WantedLockInfo[], locksWaitingForExecution: AcquiredLockInfo[], pendingWithdraw: Option): RouteInformation { + return {user, initialAmountIn, initialTokenIn, finalReceivedAmount, finalTokenOut, locksWanted, locksWaitingForExecution, pendingWithdraw}; +} + +function fromScValueRouteInformation(structValue: ScValueStruct): RouteInformation { + return { + user: BlockchainAddress.fromBuffer(structValue.getFieldValue("user")!.addressValue().value), + initialAmountIn: structValue.getFieldValue("initial_amount_in")!.asBN(), + initialTokenIn: BlockchainAddress.fromBuffer(structValue.getFieldValue("initial_token_in")!.addressValue().value), + finalReceivedAmount: structValue.getFieldValue("final_received_amount")!.asBN(), + finalTokenOut: BlockchainAddress.fromBuffer(structValue.getFieldValue("final_token_out")!.addressValue().value), + locksWanted: structValue.getFieldValue("locks_wanted")!.vecValue().values().map((sc1) => fromScValueWantedLockInfo(sc1.structValue())), + locksWaitingForExecution: structValue.getFieldValue("locks_waiting_for_execution")!.vecValue().values().map((sc2) => fromScValueAcquiredLockInfo(sc2.structValue())), + pendingWithdraw: structValue.getFieldValue("pending_withdraw")!.optionValue().valueOrUndefined((sc3) => fromScValuePendingWithdrawInfo(sc3.structValue())), + }; +} + +export interface RouteTracker { + nextRouteId: BN; + activeRoutes: Option>; +} + +export function newRouteTracker(nextRouteId: BN, activeRoutes: Option>): RouteTracker { + return {nextRouteId, activeRoutes}; +} + +function fromScValueRouteTracker(structValue: ScValueStruct): RouteTracker { + return { + nextRouteId: structValue.getFieldValue("next_route_id")!.asBN(), + activeRoutes: structValue.getFieldValue("active_routes")!.avlTreeMapValue().mapKeysValues((k4) => k4.asBN(), (v5) => fromScValueRouteInformation(v5.structValue())), + }; +} + +export interface RouterState { + permissionAddSwap: Permission; + swapContracts: SwapContractInfo[]; + routeTracker: RouteTracker; +} + +export function newRouterState(permissionAddSwap: Permission, swapContracts: SwapContractInfo[], routeTracker: RouteTracker): RouterState { + return {permissionAddSwap, swapContracts, routeTracker}; +} + +function fromScValueRouterState(structValue: ScValueStruct): RouterState { + return { + permissionAddSwap: fromScValuePermission(structValue.getFieldValue("permission_add_swap")!.enumValue()), + swapContracts: structValue.getFieldValue("swap_contracts")!.vecValue().values().map((sc6) => fromScValueSwapContractInfo(sc6.structValue())), + routeTracker: fromScValueRouteTracker(structValue.getFieldValue("route_tracker")!.structValue()), + }; +} + +export function deserializeRouterState(state: StateBytes): RouterState { + const scValue = new StateReader(state.state, fileAbi.contract, state.avlTrees).readState(); + return fromScValueRouterState(scValue); +} + +export type Permission = + | PermissionAnybody + | PermissionSpecific; + +export enum PermissionD { + Anybody = 0, + Specific = 1 +} + +function buildRpcPermission(val: Permission, builder: AbstractBuilder) { + if (val.discriminant === PermissionD.Anybody) { + buildRpcPermissionAnybody(val, builder); + } + if (val.discriminant === PermissionD.Specific) { + buildRpcPermissionSpecific(val, builder); + } +} + +function fromScValuePermission(enumValue: ScValueEnum): Permission { + const item = enumValue.item; + if (item.name === "Anybody") { + return fromScValuePermissionAnybody(item); + } + if (item.name === "Specific") { + return fromScValuePermissionSpecific(item); + } + throw Error("Should not happen"); +} + +export interface PermissionAnybody { + discriminant: PermissionD.Anybody; +} + +export function newPermissionAnybody(): PermissionAnybody { + return {discriminant: 0, }; +} + +function buildRpcPermissionAnybody(value: PermissionAnybody, builder: AbstractBuilder) { + const enumVariantBuilder = builder.addEnumVariant(PermissionD.Anybody); +} + +function fromScValuePermissionAnybody(structValue: ScValueStruct): PermissionAnybody { + return { + discriminant: PermissionD.Anybody, + }; +} + +export interface PermissionSpecific { + discriminant: PermissionD.Specific; + addresses: BlockchainAddress[]; +} + +export function newPermissionSpecific(addresses: BlockchainAddress[]): PermissionSpecific { + return {discriminant: 1, addresses, }; +} + +function buildRpcPermissionSpecific(value: PermissionSpecific, builder: AbstractBuilder) { + const enumVariantBuilder = builder.addEnumVariant(PermissionD.Specific); + const vecBuilder7 = enumVariantBuilder.addVec(); + for (const vecEntry8 of value.addresses) { + vecBuilder7.addAddress(vecEntry8.asBuffer()); + } +} + +function fromScValuePermissionSpecific(structValue: ScValueStruct): PermissionSpecific { + return { + discriminant: PermissionD.Specific, + addresses: structValue.getFieldValue("addresses")!.vecValue().values().map((sc9) => BlockchainAddress.fromBuffer(sc9.addressValue().value)), + }; +} + +export interface LiquidityLockId { + rawId: BN; +} + +export function newLiquidityLockId(rawId: BN): LiquidityLockId { + return {rawId}; +} + +function fromScValueLiquidityLockId(structValue: ScValueStruct): LiquidityLockId { + return { + rawId: structValue.getFieldValue("raw_id")!.asBN(), + }; +} + +export type Token = + | TokenTokenA + | TokenTokenB + | TokenLiquidityToken; + +export enum TokenD { + TokenA = 0, + TokenB = 1, + LiquidityToken = 2 +} + +function fromScValueToken(enumValue: ScValueEnum): Token { + const item = enumValue.item; + if (item.name === "TokenA") { + return fromScValueTokenTokenA(item); + } + if (item.name === "TokenB") { + return fromScValueTokenTokenB(item); + } + if (item.name === "LiquidityToken") { + return fromScValueTokenLiquidityToken(item); + } + throw Error("Should not happen"); +} + +export interface TokenTokenA { + discriminant: TokenD.TokenA; +} + +export function newTokenTokenA(): TokenTokenA { + return {discriminant: 0, }; +} + +function fromScValueTokenTokenA(structValue: ScValueStruct): TokenTokenA { + return { + discriminant: TokenD.TokenA, + }; +} + +export interface TokenTokenB { + discriminant: TokenD.TokenB; +} + +export function newTokenTokenB(): TokenTokenB { + return {discriminant: 1, }; +} + +function fromScValueTokenTokenB(structValue: ScValueStruct): TokenTokenB { + return { + discriminant: TokenD.TokenB, + }; +} + +export interface TokenLiquidityToken { + discriminant: TokenD.LiquidityToken; +} + +export function newTokenLiquidityToken(): TokenLiquidityToken { + return {discriminant: 2, }; +} + +function fromScValueTokenLiquidityToken(structValue: ScValueStruct): TokenLiquidityToken { + return { + discriminant: TokenD.LiquidityToken, + }; +} + +export interface TokenBalance { + aTokens: BN; + bTokens: BN; + liquidityTokens: BN; +} + +export function newTokenBalance(aTokens: BN, bTokens: BN, liquidityTokens: BN): TokenBalance { + return {aTokens, bTokens, liquidityTokens}; +} + +function fromScValueTokenBalance(structValue: ScValueStruct): TokenBalance { + return { + aTokens: structValue.getFieldValue("a_tokens")!.asBN(), + bTokens: structValue.getFieldValue("b_tokens")!.asBN(), + liquidityTokens: structValue.getFieldValue("liquidity_tokens")!.asBN(), + }; +} + +export interface TokenBalances { + tokenLpAddress: BlockchainAddress; + tokenAAddress: BlockchainAddress; + tokenBAddress: BlockchainAddress; + balances: Option>; +} + +export function newTokenBalances(tokenLpAddress: BlockchainAddress, tokenAAddress: BlockchainAddress, tokenBAddress: BlockchainAddress, balances: Option>): TokenBalances { + return {tokenLpAddress, tokenAAddress, tokenBAddress, balances}; +} + +function fromScValueTokenBalances(structValue: ScValueStruct): TokenBalances { + return { + tokenLpAddress: BlockchainAddress.fromBuffer(structValue.getFieldValue("token_lp_address")!.addressValue().value), + tokenAAddress: BlockchainAddress.fromBuffer(structValue.getFieldValue("token_a_address")!.addressValue().value), + tokenBAddress: BlockchainAddress.fromBuffer(structValue.getFieldValue("token_b_address")!.addressValue().value), + balances: structValue.getFieldValue("balances")!.avlTreeMapValue().mapKeysValues((k10) => BlockchainAddress.fromBuffer(k10.addressValue().value), (v11) => fromScValueTokenBalance(v11.structValue())), + }; +} + +export interface TokensInOut { + tokenIn: Token; + tokenOut: Token; +} + +export function newTokensInOut(tokenIn: Token, tokenOut: Token): TokensInOut { + return {tokenIn, tokenOut}; +} + +function fromScValueTokensInOut(structValue: ScValueStruct): TokensInOut { + return { + tokenIn: fromScValueToken(structValue.getFieldValue("token_in")!.enumValue()), + tokenOut: fromScValueToken(structValue.getFieldValue("token_out")!.enumValue()), + }; +} + +export interface SecretVarId { + rawId: number; +} + +export function newSecretVarId(rawId: number): SecretVarId { + return {rawId}; +} + +function fromScValueSecretVarId(structValue: ScValueStruct): SecretVarId { + return { + rawId: structValue.getFieldValue("raw_id")!.asNumber(), + }; +} + +export interface EventSubscriptionId { + rawId: number; +} + +export function newEventSubscriptionId(rawId: number): EventSubscriptionId { + return {rawId}; +} + +function fromScValueEventSubscriptionId(structValue: ScValueStruct): EventSubscriptionId { + return { + rawId: structValue.getFieldValue("raw_id")!.asNumber(), + }; +} + +export interface ExternalEventId { + rawId: number; +} + +export function newExternalEventId(rawId: number): ExternalEventId { + return {rawId}; +} + +function fromScValueExternalEventId(structValue: ScValueStruct): ExternalEventId { + return { + rawId: structValue.getFieldValue("raw_id")!.asNumber(), + }; +} + +export interface DeployableContract { + bytecode: Buffer; + abi: Buffer; + version: BN; +} + +export function newDeployableContract(bytecode: Buffer, abi: Buffer, version: BN): DeployableContract { + return {bytecode, abi, version}; +} + +function fromScValueDeployableContract(structValue: ScValueStruct): DeployableContract { + return { + bytecode: structValue.getFieldValue("bytecode")!.vecU8Value(), + abi: structValue.getFieldValue("abi")!.vecU8Value(), + version: structValue.getFieldValue("version")!.asBN(), + }; +} + +export function initialize(permissionAddSwap: Permission, swapContracts: SwapContractInfo[]): Buffer { + const fnBuilder = new FnRpcBuilder("initialize", fileAbi.contract); + buildRpcPermission(permissionAddSwap, fnBuilder); + const vecBuilder12 = fnBuilder.addVec(); + for (const vecEntry13 of swapContracts) { + buildRpcSwapContractInfo(vecEntry13, vecBuilder12); + } + return fnBuilder.getBytes(); +} + +export function routeSwap(swapRoute: BlockchainAddress[], tokenIn: BlockchainAddress, tokenOut: BlockchainAddress, amountIn: BN, amountOutMinimum: BN): Buffer { + const fnBuilder = new FnRpcBuilder("route_swap", fileAbi.contract); + const vecBuilder14 = fnBuilder.addVec(); + for (const vecEntry15 of swapRoute) { + vecBuilder14.addAddress(vecEntry15.asBuffer()); + } + fnBuilder.addAddress(tokenIn.asBuffer()); + fnBuilder.addAddress(tokenOut.asBuffer()); + fnBuilder.addU128(amountIn); + fnBuilder.addU128(amountOutMinimum); + return fnBuilder.getBytes(); +} + +export function addSwapContract(swapAddress: BlockchainAddress, tokenAAddress: BlockchainAddress, tokenBAddress: BlockchainAddress): Buffer { + const fnBuilder = new FnRpcBuilder("add_swap_contract", fileAbi.contract); + fnBuilder.addAddress(swapAddress.asBuffer()); + fnBuilder.addAddress(tokenAAddress.asBuffer()); + fnBuilder.addAddress(tokenBAddress.asBuffer()); + return fnBuilder.getBytes(); +} + diff --git a/src/main/constant.ts b/src/main/constant.ts index 996604c..33601d6 100644 --- a/src/main/constant.ts +++ b/src/main/constant.ts @@ -1,4 +1,28 @@ -export const NETWORK_ID: string = "Partisia Blockchain"; -export const NETWORK_SHARDS: string[] = [ "Shard0", "Shard1", "Shard2" ]; -export const NODE_BASE_URL: string = "https://reader.partisiablockchain.com"; -export const BROWSER_BASE_URL: string = "https://browser.partisiablockchain.com"; +/* + * Copyright (C) 2024 Jon Michael Aanes + */ + + +export interface Network { + network_id: string; + network_shards: string[]; + node_base_url: string; + browser_base_url: string; +} + +export const MAINNET: Network = { + network_id: "Partisia Blockchain", + network_shards: [ "Shard0", "Shard1", "Shard2" ], + node_base_url: "https://reader.partisiablockchain.com", + browser_base_url: "https://browser.partisiablockchain.com", +} + + +export const TESTNET: Network = { + network_id: "Partisia Blockchain Testnet", + network_shards: [ "Shard0", "Shard1", "Shard2" ], + node_base_url: "https://node1.testnet.partisiablockchain.com", + browser_base_url: "https://browser.testnet.partisiablockchain.com", +} + +export const NETWORK = TESTNET; diff --git a/src/main/swap/Main.ts b/src/main/swap/Main.ts new file mode 100644 index 0000000..75b1fb5 --- /dev/null +++ b/src/main/swap/Main.ts @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2024 Jon Michael Aanes + */ + +import BN from "bn.js"; +import { BlockchainAddress } from "@partisiablockchain/abi-client"; +import { TransactionFailedError } from "../client/TransactionApi"; +import { PutTransactionWasSuccessful } from "../client/TransactionData"; + +import { transfer, TokenState, deserializeTokenState } from "../abi/SwapRouter"; + +const ROUTERS: BlockchainAddress[] = [BlockchainAddress.fromString("02f8eb18e09dfe6797880c952527747202560338bf")]; + +const TOKENS: BlockchainAddress[] = []; + +const SWAPS: BlockchainAddress[] = []; + +function get_router_state(contractAddress: BlockchainAddress): Promise { +return this.shardedClient + .getContractData(contractAddress.asString()) + .then((contract) => { + if (contract == null) { + throw new Error("Could not find data for contract"); + } + + // Reads the state of the contract + const stateBuffer = Buffer.from(contract.serializedContract.state.data, "base64"); + + return deserializeTokenState({ state: stateBuffer }); + }); +} + + +function setup() { + for (let router of ROUTERS) { + console.log(router); + } +} + +setup(); diff --git a/src/main/swap/index.html b/src/main/swap/index.html new file mode 100644 index 0000000..d212628 --- /dev/null +++ b/src/main/swap/index.html @@ -0,0 +1,163 @@ + + + + MPC20-v2 + + + + + + + + +
+
+

Dumb Defi UI

+ +
+

Token Overview

+
+ +
+ +
+
+ +
+ +
+
+ + + + + Owned + +
+
+ +
+

Account

+

+ Currently not logged in + +

+
+
+ +
+
+ +
+
+ + +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ + +
+
+
+ + diff --git a/src/main/token/Main.ts b/src/main/token/Main.ts index 5fb60d1..1aacd1e 100644 --- a/src/main/token/Main.ts +++ b/src/main/token/Main.ts @@ -41,7 +41,7 @@ import BN from "bn.js"; import { BlockchainAddress } from "@partisiablockchain/abi-client"; import { TransactionFailedError } from "../client/TransactionApi"; import { PutTransactionWasSuccessful } from "../client/TransactionData"; -import { BROWSER_BASE_URL } from "../constant"; +import { NETWORK } from "../constant"; // Setup event listener to connect to the MPC wallet browser extension const connectWallet = document.querySelector("#wallet-connect-btn"); @@ -122,7 +122,7 @@ function setContractAddressUI(address: BlockchainAddress) { const inputAddress = document.querySelector("#address-value"); currentAddress.innerText = address.asString(); - currentAddress.href = `${BROWSER_BASE_URL}/contracts/${address.asString()}`; + currentAddress.href = `${NETWORK.browser_base_url}/contracts/${address.asString()}`; inputAddress.value = address.asString(); setContractAddress(address); updateInteractionVisibility(); @@ -151,7 +151,7 @@ const transactionLinkElement = document.querySelector("#sign-t function setTransactionLink(transaction: PutTransactionWasSuccessful) { const transactionLinkElement = document.querySelector("#sign-transaction-link"); - transactionLinkElement.innerHTML = `Transaction link in browser`; + transactionLinkElement.innerHTML = `Transaction link in browser`; transactionErrorMessage.innerText = ""; } diff --git a/webpack.config.js b/webpack.config.js index 45fd307..070b08e 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -22,7 +22,7 @@ module.exports = (env) => { return merge(configuration, { entry: { - token: path("main/token/Main"), + swap: path("main/swap/Main"), }, resolve: { alias: { @@ -64,7 +64,7 @@ module.exports = (env) => { }, }), new HtmlWebpackPlugin({ filename: 'index.html', template: path("main/index.html"), chunks: [] }), - new HtmlWebpackPlugin({ filename: 'token/index.html',template: path("main/token/index.html"), chunks:["token"] }), + new HtmlWebpackPlugin({ filename: 'swap/index.html',template: path("main/swap/index.html"), chunks:["swap"] }), new webpackConfig.ProvidePlugin({ Buffer: ["buffer", "Buffer"], process: "process/browser" }) ].filter(Boolean) });