1
0

Basic state loading

This commit is contained in:
Jon Michael Aanes 2024-06-09 01:09:54 +02:00
parent 4715e44373
commit 9344332788
Signed by: Jmaa
SSH Key Fingerprint: SHA256:Ab0GfHGCblESJx7JRE4fj4bFy/KRpeLhi41y4pF3sNA
3 changed files with 169 additions and 49 deletions

View File

@ -3,17 +3,31 @@
*/
import BN from "bn.js";
import { LittleEndianByteInput } from "@secata-public/bitmanipulation-ts";
import { BlockchainAddress, StateBytes } from "@partisiablockchain/abi-client";
import { TransactionFailedError } from "../client/TransactionApi";
import { PutTransactionWasSuccessful } from "../client/TransactionData";
import { ShardedClient } from "../client/ShardedClient";
import { RouterState, deserializeRouterState } from "../abi/SwapRouter";
import { LiquiditySwapContractState, deserializeLiquiditySwapContractState } from "../abi/LiquiditySwapContract";
import { TokenState, deserializeTokenState } from "../abi/TokenV1";
import { TokenBalance, LiquiditySwapContractState, deserializeLiquiditySwapContractState } from "../abi/LiquiditySwapLock";
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 { 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 {
latest_state: TokenState | LiquiditySwapContractState | null;
latest_state: TokenStateV1 | TokenStateV2 | null;
swaps: BlockchainAddress[] | null,
type: ContractType | null,
}
const ROUTERS: BlockchainAddress[] = [BlockchainAddress.fromString("02f8eb18e09dfe6797880c952527747202560338bf")];
@ -25,15 +39,15 @@ const TOKENS: Record<BlockchainAddressKey, ContractState> = {};
const SWAPS: Record<BlockchainAddressKey, ContractState> = {};
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 {
state: { data: string };
}
function get_contract_state<T>(contractAddress: BlockchainAddress, deserialize: (state_bytes: StateBytes) => T): Promise<T> {
return SHARDED_CLIENT
.getContractData<RawContractData>(contractAddress.asString())
.then((contract) => {
async function get_contract_state<T>(contractAddress: BlockchainAddress, deserialize: (state_bytes: StateBytes) => T): Promise<T> {
const contract = await SHARDED_CLIENT.getContractData<RawContractData>(contractAddress.asString());
if (contract == null) {
throw new Error("Could not find data for contract");
}
@ -42,9 +56,115 @@ function get_contract_state<T>(contractAddress: BlockchainAddress, deserialize:
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() {
for (let router of ROUTERS) {
@ -52,16 +172,24 @@ function setup() {
get_contract_state(router, deserializeRouterState).then((state) => {
console.log(state);
for (let swapInfo of state.swapContracts) {
SWAPS[swapInfo.swapAddress.asString()] = { latest_state: null };
TOKENS[swapInfo.tokenAAddress.asString()] = { latest_state: null };
TOKENS[swapInfo.tokenBAddress.asString()] = { latest_state: null };
// 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 swapInfo of state.swapContracts) {
TOKENS[swapInfo.tokenAAddress.asString()] = { latest_state: null, swaps: null, type: null };
TOKENS[swapInfo.tokenBAddress.asString()] = { latest_state: null, swaps: null, type: null };
SWAPS[swapInfo.swapAddress.asString()] = { latest_state: null, swaps: [swapInfo.tokenAAddress, swapInfo.tokenBAddress], type: null };
ui_add_swap(swapInfo.swapAddress);
}
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);
});
}
}

View File

@ -47,6 +47,14 @@
transform: rotate(360deg);
}
}
#token-list {
display: grid;
grid-template-columns: auto auto auto 1fr;
grid-gap: 1em;
max-width: 400px;
}
</style>
<link
rel="stylesheet"
@ -63,30 +71,14 @@
<div id="overview">
<h2>Token Overview</h2>
<div id="token-list">
<span>
<form class="pure-form" onSubmit="return false;">
<input
class="pure-button pure-button-primary"
id="private-key-connect-btn"
type="submit"
value="From" />
</form>
</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>
<div>Symbol</div>
<div>Owned Amount</div>
</div>
<h2>Exchange Rates</h2>
<ul id="rate-list">
</div>
</div>