Basic state loading
This commit is contained in:
parent
4715e44373
commit
9344332788
|
@ -102,7 +102,7 @@ export function deserializeLiquiditySwapContractState(state: StateBytes): Liquid
|
||||||
return fromScValueLiquiditySwapContractState(scValue);
|
return fromScValueLiquiditySwapContractState(scValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Token =
|
export type Token =
|
||||||
| TokenTokenA
|
| TokenTokenA
|
||||||
| TokenTokenB
|
| TokenTokenB
|
||||||
| TokenLiquidityToken;
|
| TokenLiquidityToken;
|
||||||
|
@ -247,7 +247,7 @@ function fromScValueTokensInOut(structValue: ScValueStruct): TokensInOut {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Permission =
|
export type Permission =
|
||||||
| PermissionAnybody
|
| PermissionAnybody
|
||||||
| PermissionSpecific;
|
| PermissionSpecific;
|
||||||
|
|
|
@ -3,17 +3,31 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import BN from "bn.js";
|
import BN from "bn.js";
|
||||||
|
import { LittleEndianByteInput } from "@secata-public/bitmanipulation-ts";
|
||||||
import { BlockchainAddress, StateBytes } from "@partisiablockchain/abi-client";
|
import { BlockchainAddress, StateBytes } from "@partisiablockchain/abi-client";
|
||||||
import { TransactionFailedError } from "../client/TransactionApi";
|
import { TransactionFailedError } from "../client/TransactionApi";
|
||||||
import { PutTransactionWasSuccessful } from "../client/TransactionData";
|
import { PutTransactionWasSuccessful } from "../client/TransactionData";
|
||||||
import { ShardedClient } from "../client/ShardedClient";
|
import { ShardedClient } from "../client/ShardedClient";
|
||||||
import { RouterState, deserializeRouterState } from "../abi/SwapRouter";
|
import { RouterState, deserializeRouterState } from "../abi/SwapRouter";
|
||||||
import { LiquiditySwapContractState, deserializeLiquiditySwapContractState } from "../abi/LiquiditySwapContract";
|
import { TokenBalance, LiquiditySwapContractState, deserializeLiquiditySwapContractState } from "../abi/LiquiditySwapLock";
|
||||||
import { TokenState, deserializeTokenState } from "../abi/TokenV1";
|
import { TokenState as TokenStateV1, deserializeTokenState as deserializeTokenStateV1 } from "../abi/TokenV1";
|
||||||
|
import { TokenState as TokenStateV2, deserializeTokenState as deserializeTokenStateV2 } from "../abi/TokenV2";
|
||||||
import { NETWORK } from "../constant";
|
import { NETWORK } from "../constant";
|
||||||
|
import { AvlClient } from "../client/AvlClient";
|
||||||
|
|
||||||
|
// UI constants
|
||||||
|
const TOKEN_LIST = <Element>document.querySelector("#token-list");
|
||||||
|
const EXCHANGE_RATE_LIST = <Element>document.querySelector("#rate-list");
|
||||||
|
|
||||||
|
|
||||||
|
// Logic
|
||||||
|
|
||||||
|
type ContractType = "token_v1" | "token_v2" | "swap_lock_v1"
|
||||||
|
|
||||||
interface ContractState {
|
interface ContractState {
|
||||||
latest_state: TokenState | LiquiditySwapContractState | null;
|
latest_state: TokenStateV1 | TokenStateV2 | null;
|
||||||
|
swaps: BlockchainAddress[] | null,
|
||||||
|
type: ContractType | null,
|
||||||
}
|
}
|
||||||
|
|
||||||
const ROUTERS: BlockchainAddress[] = [BlockchainAddress.fromString("02f8eb18e09dfe6797880c952527747202560338bf")];
|
const ROUTERS: BlockchainAddress[] = [BlockchainAddress.fromString("02f8eb18e09dfe6797880c952527747202560338bf")];
|
||||||
|
@ -25,26 +39,132 @@ const TOKENS: Record<BlockchainAddressKey, ContractState> = {};
|
||||||
const SWAPS: Record<BlockchainAddressKey, ContractState> = {};
|
const SWAPS: Record<BlockchainAddressKey, ContractState> = {};
|
||||||
|
|
||||||
const SHARDED_CLIENT: ShardedClient = new ShardedClient(NETWORK.node_base_url, NETWORK.network_shards);
|
const SHARDED_CLIENT: ShardedClient = new ShardedClient(NETWORK.node_base_url, NETWORK.network_shards);
|
||||||
|
const AVL_CLIENT:AvlClient = new AvlClient(NETWORK .node_base_url, NETWORK.network_shards);;
|
||||||
|
|
||||||
interface RawContractData {
|
interface RawContractData {
|
||||||
state: { data: string };
|
state: { data: string };
|
||||||
}
|
}
|
||||||
|
|
||||||
function get_contract_state<T>(contractAddress: BlockchainAddress, deserialize: (state_bytes: StateBytes) => T): Promise<T> {
|
async function get_contract_state<T>(contractAddress: BlockchainAddress, deserialize: (state_bytes: StateBytes) => T): Promise<T> {
|
||||||
return SHARDED_CLIENT
|
const contract = await SHARDED_CLIENT.getContractData<RawContractData>(contractAddress.asString());
|
||||||
.getContractData<RawContractData>(contractAddress.asString())
|
|
||||||
.then((contract) => {
|
|
||||||
if (contract == null) {
|
|
||||||
throw new Error("Could not find data for contract");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reads the state of the contract
|
if (contract == null) {
|
||||||
const stateBuffer = Buffer.from(contract.serializedContract.state.data, "base64");
|
throw new Error("Could not find data for contract");
|
||||||
|
}
|
||||||
|
|
||||||
return deserialize({ state: stateBuffer });
|
// Reads the state of the contract
|
||||||
});
|
const stateBuffer = Buffer.from(contract.serializedContract.state.data, "base64");
|
||||||
|
|
||||||
|
return deserialize({ state: stateBuffer });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function address_to_url(address: BlockchainAddress): string {
|
||||||
|
return `${NETWORK.browser_base_url}/contracts/${address.asString()}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ui_add_token(tokenState: TokenStateV1 | TokenStateV2, address: BlockchainAddress) {
|
||||||
|
const spanFrom = document.createElement("div");
|
||||||
|
spanFrom.innerHTML = `
|
||||||
|
<form class="pure-form" onSubmit="return false;">
|
||||||
|
<input
|
||||||
|
class="pure-button pure-button-primary"
|
||||||
|
id="private-key-connect-btn"
|
||||||
|
type="submit"
|
||||||
|
value="From" />
|
||||||
|
</form>
|
||||||
|
`;
|
||||||
|
const spanTo = document.createElement("div");
|
||||||
|
spanTo.innerHTML = `
|
||||||
|
<form class="pure-form" onSubmit="return false;">
|
||||||
|
<input
|
||||||
|
class="pure-button pure-button-primary"
|
||||||
|
id="private-key-connect-btn"
|
||||||
|
type="submit"
|
||||||
|
value="To" />
|
||||||
|
</form>
|
||||||
|
`;
|
||||||
|
const spanSymbol= document.createElement("a");
|
||||||
|
spanSymbol.innerText = tokenState.symbol;
|
||||||
|
spanSymbol.title = tokenState.name;
|
||||||
|
spanSymbol.href = address_to_url(address);
|
||||||
|
const spanAmount = document.createElement("div");
|
||||||
|
spanAmount.innerText = "Login to view";
|
||||||
|
TOKEN_LIST.append(spanFrom);
|
||||||
|
TOKEN_LIST.append(spanTo);
|
||||||
|
TOKEN_LIST.append(spanSymbol);
|
||||||
|
TOKEN_LIST.append(spanAmount);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function get_current_liquidity(swapAddress: BlockchainAddress): Promise<TokenBalance> {
|
||||||
|
const SWAP_CONTRACT_BALANCES_TREE_ID = 0;
|
||||||
|
const dataBuffer = await AVL_CLIENT.getContractStateAvlValue(
|
||||||
|
swapAddress.asString(),
|
||||||
|
SWAP_CONTRACT_BALANCES_TREE_ID,
|
||||||
|
swapAddress.asBuffer(),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (dataBuffer === undefined) {
|
||||||
|
throw new Error("Contract was not correctly initialized");
|
||||||
|
}
|
||||||
|
|
||||||
|
const reader = new LittleEndianByteInput(dataBuffer);
|
||||||
|
|
||||||
|
return {
|
||||||
|
aTokens: reader.readUnsignedBigInteger(16),
|
||||||
|
bTokens: reader.readUnsignedBigInteger(16),
|
||||||
|
liquidityTokens: reader.readUnsignedBigInteger(16),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const RATE_DECIMALS = new BN(10000);
|
||||||
|
|
||||||
|
async function get_current_exchange_rate(swapAddress: BlockchainAddress): Promise<BN> {
|
||||||
|
const liquidity: TokenBalance = await get_current_liquidity(swapAddress);
|
||||||
|
const result = liquidity.aTokens.mul(RATE_DECIMALS).div(liquidity.bTokens);
|
||||||
|
console.log(`${liquidity.aTokens}.mul(${RATE_DECIMALS}).div(${liquidity.bTokens}) = ${result}`);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ui_add_swap(swapAddress: BlockchainAddress) {
|
||||||
|
const rateElement = document.createElement("li");
|
||||||
|
rateElement.id = "C"+swapAddress.asString();
|
||||||
|
rateElement.innerText = swapAddress.asString();
|
||||||
|
EXCHANGE_RATE_LIST.append(rateElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function ui_update_swap(swapState: LiquiditySwapContractState, swapAddress: BlockchainAddress) {
|
||||||
|
// Calculate rate
|
||||||
|
|
||||||
|
const infoA = TOKENS[swapState.tokenBalances.tokenAAddress.asString()];
|
||||||
|
const infoB = TOKENS[swapState.tokenBalances.tokenBAddress.asString()];
|
||||||
|
|
||||||
|
const symbolA = infoA.latest_state == null ? "???" : infoA.latest_state.symbol;
|
||||||
|
const symbolB = infoB.latest_state == null ? "???" : infoB.latest_state.symbol;
|
||||||
|
|
||||||
|
const rateBsForA: BN = await get_current_exchange_rate(swapAddress);
|
||||||
|
|
||||||
|
//
|
||||||
|
const rateElement = <Element>document.querySelector("#C"+swapAddress.asString());
|
||||||
|
// TODO: Liquidity information
|
||||||
|
rateElement.innerHTML = `${RATE_DECIMALS} ${symbolA} = ${rateBsForA} ${symbolB} (<a href="${address_to_url(swapAddress)}">Contract</a>)`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function update_swap_contract_info(){
|
||||||
|
for (const swapAddressStr in SWAPS) {
|
||||||
|
const swapAddress = BlockchainAddress.fromString(swapAddressStr);
|
||||||
|
get_contract_state(swapAddress, deserializeLiquiditySwapContractState).then(state => ui_update_swap(state, swapAddress));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function get_token_state(tokenAddress: BlockchainAddress): Promise<TokenStateV1 | TokenStateV2> {
|
||||||
|
try {
|
||||||
|
return await get_contract_state(tokenAddress, deserializeTokenStateV1);
|
||||||
|
} catch {
|
||||||
|
// Pass
|
||||||
|
}
|
||||||
|
|
||||||
|
return await get_contract_state(tokenAddress, deserializeTokenStateV2);
|
||||||
|
}
|
||||||
|
|
||||||
function setup() {
|
function setup() {
|
||||||
for (let router of ROUTERS) {
|
for (let router of ROUTERS) {
|
||||||
|
@ -52,16 +172,24 @@ function setup() {
|
||||||
get_contract_state(router, deserializeRouterState).then((state) => {
|
get_contract_state(router, deserializeRouterState).then((state) => {
|
||||||
console.log(state);
|
console.log(state);
|
||||||
|
|
||||||
for (let swapInfo of state.swapContracts) {
|
for (const swapInfo of state.swapContracts) {
|
||||||
SWAPS[swapInfo.swapAddress.asString()] = { latest_state: null };
|
TOKENS[swapInfo.tokenAAddress.asString()] = { latest_state: null, swaps: null, type: null };
|
||||||
TOKENS[swapInfo.tokenAAddress.asString()] = { latest_state: null };
|
TOKENS[swapInfo.tokenBAddress.asString()] = { latest_state: null, swaps: null, type: null };
|
||||||
TOKENS[swapInfo.tokenBAddress.asString()] = { latest_state: null };
|
SWAPS[swapInfo.swapAddress.asString()] = { latest_state: null, swaps: [swapInfo.tokenAAddress, swapInfo.tokenBAddress], type: null };
|
||||||
|
ui_add_swap(swapInfo.swapAddress);
|
||||||
// TODO: Deduplicate tokens
|
|
||||||
get_contract_state(swapInfo.swapAddress, deserializeLiquiditySwapContractState).then(console.log);
|
|
||||||
get_contract_state(swapInfo.tokenAAddress, deserializeTokenState).then(console.log);
|
|
||||||
get_contract_state(swapInfo.tokenBAddress, deserializeTokenState).then(console.log);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const tokenAddressStr in TOKENS) {
|
||||||
|
const tokenAddress = BlockchainAddress.fromString(tokenAddressStr);
|
||||||
|
get_token_state(tokenAddress).then(state => {
|
||||||
|
TOKENS[tokenAddress.asString()].type = "token_v1";
|
||||||
|
TOKENS[tokenAddress.asString()].latest_state = state;
|
||||||
|
ui_add_token(state, tokenAddress);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
update_swap_contract_info();
|
||||||
|
setInterval(update_swap_contract_info, 30*1000);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,6 +47,14 @@
|
||||||
transform: rotate(360deg);
|
transform: rotate(360deg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#token-list {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: auto auto auto 1fr;
|
||||||
|
grid-gap: 1em;
|
||||||
|
max-width: 400px;
|
||||||
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<link
|
<link
|
||||||
rel="stylesheet"
|
rel="stylesheet"
|
||||||
|
@ -63,30 +71,14 @@
|
||||||
<div id="overview">
|
<div id="overview">
|
||||||
<h2>Token Overview</h2>
|
<h2>Token Overview</h2>
|
||||||
<div id="token-list">
|
<div id="token-list">
|
||||||
<span>
|
<div></div>
|
||||||
<form class="pure-form" onSubmit="return false;">
|
<div></div>
|
||||||
<input
|
<div>Symbol</div>
|
||||||
class="pure-button pure-button-primary"
|
<div>Owned Amount</div>
|
||||||
id="private-key-connect-btn"
|
</div>
|
||||||
type="submit"
|
|
||||||
value="From" />
|
<h2>Exchange Rates</h2>
|
||||||
</form>
|
<ul id="rate-list">
|
||||||
</span>
|
|
||||||
<span>
|
|
||||||
<form class="pure-form" onSubmit="return false;">
|
|
||||||
<input
|
|
||||||
class="pure-button pure-button-primary"
|
|
||||||
id="private-key-connect-btn"
|
|
||||||
type="submit"
|
|
||||||
value="To" />
|
|
||||||
</form>
|
|
||||||
</span>
|
|
||||||
<span>
|
|
||||||
<img todo></img>
|
|
||||||
</span>
|
|
||||||
<span>
|
|
||||||
Owned
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user