Merge branch 'feature/m' into feature/m-jordan

This commit is contained in:
Jordan Prince 2021-05-01 13:52:36 -05:00
commit 817fdffbc3
116 changed files with 1116 additions and 1089 deletions

View File

@ -1,16 +1,31 @@
{
"name": "@solana/bridge-sdk",
"version": "0.0.1",
"description": "Bridge common sdk utilities",
"main": "dist/lib/index.js",
"types": "dist/lib/index.d.ts",
"exports": {
".": "./dist/lib/"
},
"license": "Apache-2.0",
"publishConfig": {
"access": "public"
},
"engines": {
"node": ">=10"
},
"dependencies": {
"@solana/wallet-base": "0.0.1",
"@solana/wallet-ledger": "0.0.1",
"@babel/preset-typescript": "^7.13.0",
"@oyster/common": "0.0.1",
"@solana/spl-token": "0.0.13",
"@solana/spl-token-swap": "0.1.0",
"@solana/wallet-base": "0.0.1",
"@solana/wallet-ledger": "0.0.1",
"@solana/web3.js": "^1.5.0",
"bignumber.js": "^9.0.1",
"bn.js": "^5.1.3",
"bs58": "^4.0.1",
"buffer-layout": "^1.2.0",
"buffer-layout": "1.2.0",
"ethers": "^4.0.48",
"eventemitter3": "^4.0.7",
"lodash": "^4.17.20",
@ -18,9 +33,20 @@
"web3": "^1.3.0"
},
"scripts": {
"build": "tsc",
"start": "npm-run-all --parallel watch watch-css watch-css-src",
"watch-css": "less-watch-compiler src/ dist/lib/",
"watch-css-src": "less-watch-compiler src/ src/",
"watch": "tsc --watch",
"test": "jest test",
"clean": "rm -rf dist",
"prepare": "run-s clean build",
"format:fix": "prettier --write \"**/*.+(js|jsx|ts|tsx|json|css|md)\"",
"ethers": "typechain --target ethers-v4 --outDir src/contracts 'contracts/*.json'"
},
"files": [
"dist"
],
"browserslist": {
"production": [
">0.2%",
@ -37,6 +63,14 @@
"type": "git",
"url": "https://github.com/solana-labs/oyster"
},
"peerDependencies": {
"react": "*",
"react-dom": "*"
},
"resolutions": {
"react": "16.13.1",
"react-dom": "16.13.1"
},
"homepage": ".",
"devDependencies": {
"@typechain/ethers-v4": "^1.0.0",

View File

@ -1,10 +1,10 @@
// 44 - bridge config
import { Layout } from '@oyster/common';
import { publicKey } from '@oyster/common/dist/lib/utils/layout';
import * as BufferLayout from 'buffer-layout';
export const BridgeLayout: typeof BufferLayout.Structure = BufferLayout.struct([
export const BridgeLayout = BufferLayout.struct([
BufferLayout.u32('guardianSetIndex'),
BufferLayout.u8('guardianSetExpirationTime'),
Layout.publicKey('tokenProgram'),
publicKey('tokenProgram'),
BufferLayout.u8('isInitialized'),
]);

View File

@ -1,7 +1,7 @@
import { programIds, sendTransactionWithRetry } from '@oyster/common';
import { programIds, sendTransactionWithRetry, sleep } from '@oyster/common';
import { WalletAdapter } from '@solana/wallet-base';
import { ethers } from 'ethers';
import { WormholeFactory } from '../../../contracts/WormholeFactory';
import { WormholeFactory } from '../../contracts/WormholeFactory';
import { bridgeAuthorityKey } from './../helpers';
import { Connection, PublicKey, SystemProgram } from '@solana/web3.js';
import { Token } from '@solana/spl-token';
@ -9,7 +9,7 @@ import { ProgressUpdate, TransferRequest } from './interface';
import BN from 'bn.js';
import { createLockAssetInstruction } from '../lock';
import { TransferOutProposalLayout } from '../transferOutProposal';
import { SolanaBridge } from '../../../core';
import { SolanaBridge } from '../../core';
export const fromSolana = async (
connection: Connection,
@ -113,6 +113,17 @@ export const fromSolana = async (
wallet,
[ix, fee_ix, lock_ix],
[],
undefined,
false,
undefined,
() => {
setProgress({
message: 'Executing Solana Transaction',
type: 'wait',
group,
step: counter++,
});
},
);
return steps.wait(request, transferKey, slot);
@ -124,31 +135,39 @@ export const fromSolana = async (
) => {
return new Promise<void>((resolve, reject) => {
let completed = false;
let unsubscribed = false;
let startSlot = slot;
let group = 'Lock assets';
const solConfirmationMessage = (current: number) =>
`Awaiting ETH confirmations: ${current} out of 32`;
`Awaiting Solana confirmations: ${current} out of 32`;
let replaceMessage = false;
let slotUpdateListener = connection.onSlotChange(slot => {
if (completed) return;
const passedSlots = slot.slot - startSlot;
if (unsubscribed) {
return;
}
const passedSlots = Math.max(Math.min(slot.slot - startSlot, 0), 32);
const isLast = passedSlots - 1 === 31;
if (passedSlots < 32) {
if (passedSlots <= 32) {
setProgress({
message: solConfirmationMessage(passedSlots),
type: isLast ? 'done' : 'wait',
step: counter++,
group,
replace: passedSlots > 0,
replace: replaceMessage,
});
replaceMessage = true;
}
if (completed || isLast) {
unsubscribed = true;
setProgress({
message: 'Awaiting guardian confirmation. (Up to few min.)',
type: 'wait',
step: counter++,
group,
});
if (isLast) {
setProgress({
message: 'Awaiting guardian confirmation',
type: 'wait',
step: counter++,
group,
});
}
}
});
@ -175,10 +194,19 @@ export const fromSolana = async (
completed = true;
connection.removeAccountChangeListener(accountChangeListener);
connection.removeSlotChangeListener(slotUpdateListener);
let signatures;
while (!signatures) {
try {
signatures = await bridge.fetchSignatureStatus(
lockup.signatureAccount,
);
break;
} catch {
await sleep(500);
}
}
let signatures = await bridge.fetchSignatureStatus(
lockup.signatureAccount,
);
let sigData = Buffer.of(
...signatures.reduce((previousValue, currentValue) => {
previousValue.push(currentValue.index);

View File

@ -1,6 +1,5 @@
import BN from 'bn.js';
import { BigNumber } from 'bignumber.js';
import { ethers } from 'ethers';
import { BigNumber } from 'ethers/utils';
import { ASSET_CHAIN } from '../constants';
export interface ProgressUpdate {
@ -16,7 +15,7 @@ export interface TransferRequestInfo {
name: string;
balance: BigNumber;
decimals: number;
allowance: BigNumber;
allowance: ethers.utils.BigNumber;
isWrapped: boolean;
chainID: number;
assetAddress: Buffer;

View File

@ -8,8 +8,8 @@ import {
createAssociatedTokenAccountInstruction,
} from '@oyster/common';
import { ethers } from 'ethers';
import { ERC20Factory } from '../../../contracts/ERC20Factory';
import { WormholeFactory } from '../../../contracts/WormholeFactory';
import { ERC20Factory } from '../../contracts/ERC20Factory';
import { WormholeFactory } from '../../contracts/WormholeFactory';
import { AssetMeta, createWrappedAssetInstruction } from './../meta';
import { bridgeAuthorityKey, wrappedAssetMintKey } from './../helpers';
import {
@ -21,6 +21,7 @@ import {
import { AccountInfo } from '@solana/spl-token';
import { TransferRequest, ProgressUpdate } from './interface';
import { WalletAdapter } from '@solana/wallet-base';
import { BigNumber } from 'bignumber.js';
export const toSolana = async (
connection: Connection,
@ -39,6 +40,9 @@ export const toSolana = async (
signer.getAddress(),
'pending',
);
const amountBigNumber = new BigNumber(request.amount.toString()).toFormat(
request.info.decimals,
);
const amountBN = ethers.utils.parseUnits(
request.amount.toString(),

View File

@ -5,7 +5,7 @@ import assert from 'assert';
// @ts-ignore
import * as BufferLayout from 'buffer-layout';
import * as bs58 from 'bs58';
import { AssetMeta } from '../models/bridge';
import { AssetMeta } from '../bridge';
export interface Lockup {
lockupAddress: PublicKey;

View File

@ -1 +1,10 @@
// TODO: move bridge interaction code to this library
export * as bridge from './bridge';
export * from './bridge';
export * as core from './core';
export * from './core';
export * from './contracts/ERC20Factory';
export * from './contracts/IERC20Factory';
export * from './contracts/WETHFactory';
export * from './contracts/WormholeFactory';
export * from './contracts/WrappedAssetFactory';

View File

@ -0,0 +1,4 @@
declare module 'buffer-layout' {
const bl: any;
export = bl;
}

View File

@ -1,22 +1,23 @@
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"module": "commonjs",
"target": "es2019",
"moduleResolution": "node",
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"allowJs": true,
"allowSyntheticDefaultImports": true,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"downlevelIteration": true,
"noEmit": true,
"jsx": "react",
"typeRoots": ["types", "../../types", "../../node_modules/@types"]
"typeRoots": ["types", "../../types", "../../node_modules/@types"],
"outDir": "./dist/lib",
"rootDir": "./src",
"composite": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true
},
"include": ["src"]
"include": ["src/**/*"]
}

View File

@ -14,6 +14,7 @@
"@solana/spl-token-swap": "0.1.0",
"@solana/wallet-base": "0.0.1",
"@solana/wallet-ledger": "0.0.1",
"@solana/bridge-sdk": "0.0.1",
"@solana/web3.js": "^1.5.0",
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.5.0",
@ -37,6 +38,7 @@
"@web3-react/walletlink-connector": "^6.0.9",
"@welldone-software/why-did-you-render": "^6.0.5",
"animejs": "^3.2.1",
"bignumber.js": "^9.0.1",
"bn.js": "^5.1.3",
"bs58": "^4.0.1",
"buffer-layout": "^1.2.0",

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 111 KiB

View File

@ -1,50 +1,44 @@
<svg width="816" height="176" viewBox="0 0 816 176" fill="none" xmlns="http://www.w3.org/2000/svg">
<svg width="816" height="87" viewBox="0 0 816 87" fill="none" xmlns="http://www.w3.org/2000/svg">
<g filter="url(#filter0_b)">
<path d="M424.384 172L426.282 169.322L410.202 95L388.316 95L376.927 140.419L365.425 95L343.539 95L327.459 169.322L329.358 172H340.524L354.147 107.275H355.599L370.227 164.188H383.515L398.143 107.275H399.594L413.217 172H424.384Z" fill="url(#paint0_linear)"/>
<path d="M0 97.6783L1.89829 95.0001H13.0647L26.6877 159.725H28.1393L42.7673 102.812H56.0554L70.6833 159.725H72.135L85.758 95.0001H96.9244L98.8227 97.6783L82.743 172H60.8569L49.3555 126.581L37.9658 172H16.0796L0 97.6783Z" fill="url(#paint1_linear)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M241.862 172V95.0001H283.341C295.4 95.0001 301.43 101.026 301.43 113.078V125.688C301.43 135.137 297.708 140.865 290.264 142.874L303.663 169.099L301.988 172H290.375L276.082 143.767H255.485V172H241.862ZM255.485 131.491H282.894C286.169 131.491 287.807 129.855 287.807 126.581V112.186C287.807 108.912 286.169 107.275 282.894 107.275H255.485V131.491Z" fill="url(#paint2_linear)"/>
<path d="M460.199 95.0001V172H473.822V138.187H514.906V172H528.529V95.0001H514.906V125.912H473.822V95.0001H460.199Z" fill="url(#paint3_linear)"/>
<path d="M673.495 172V95.0001H687.117V159.725H721.957V172H673.495Z" fill="url(#paint4_linear)"/>
<path d="M755.598 95.0001V172H816V159.725H769.221V138.187H798.827V125.912H769.221V107.275H816V95.0001H755.598Z" fill="url(#paint5_linear)"/>
<path d="M424.384 82.9999L426.282 80.3217L410.202 6.00001L388.316 6.00001L376.927 51.4188L365.425 6L343.539 6L327.459 80.3217L329.358 82.9999H340.524L354.147 18.2754H355.599L370.227 75.1883H383.515L398.143 18.2754H399.594L413.217 82.9999H424.384Z" fill="url(#paint0_linear)"/>
<path d="M0 8.67833L1.89829 6.00007H13.0647L26.6877 70.7246H28.1393L42.7673 13.8117H56.0554L70.6833 70.7246H72.135L85.758 6.00007H96.9244L98.8227 8.67833L82.743 83H60.8569L49.3555 37.5812L37.9658 83H16.0796L0 8.67833Z" fill="url(#paint1_linear)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M241.862 83V6.00007H283.341C295.4 6.00007 301.43 12.0261 301.43 24.0783V36.6884C301.43 46.1367 297.708 51.8652 290.264 53.8739L303.663 80.0985L301.988 83H290.375L276.082 54.7667H255.485V83H241.862ZM255.485 42.4913H282.894C286.169 42.4913 287.807 40.8546 287.807 37.5812V23.1856C287.807 19.9121 286.169 18.2754 282.894 18.2754H255.485V42.4913Z" fill="url(#paint2_linear)"/>
<path d="M460.199 6.00007V83H473.822V49.187H514.906V83H528.529V6.00007H514.906V36.9116H473.822V6.00007H460.199Z" fill="url(#paint3_linear)"/>
<path d="M673.495 83V6.00007H687.117V70.7246H721.957V83H673.495Z" fill="url(#paint4_linear)"/>
<path d="M755.598 6.00007V83H816V70.7246H769.221V49.187H798.827V36.9116H769.221V18.2754H816V6.00007H755.598Z" fill="url(#paint5_linear)"/>
</g>
<path fill-rule="evenodd" clip-rule="evenodd" d="M159.495 106.073L141.269 124.091C136.572 128.735 136.572 136.265 141.269 140.909L159.495 158.927C164.192 163.571 171.808 163.571 176.505 158.927L194.731 140.909C199.428 136.265 199.428 128.735 194.731 124.091L176.505 106.073C171.808 101.429 164.192 101.429 159.495 106.073ZM131.549 114.482C121.484 124.433 121.484 140.567 131.549 150.518L149.775 168.537C159.84 178.488 176.16 178.488 186.225 168.537L204.451 150.518C214.516 140.567 214.516 124.433 204.451 114.482L186.225 96.4634C176.16 86.5122 159.84 86.5122 149.775 96.4634L131.549 114.482Z" fill="#DC1FFF"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M591.495 106.073L573.269 124.091C568.572 128.735 568.572 136.265 573.269 140.909L591.495 158.927C596.192 163.571 603.808 163.571 608.505 158.927L626.731 140.909C631.428 136.265 631.428 128.735 626.731 124.091L608.505 106.073C603.808 101.429 596.192 101.429 591.495 106.073ZM563.549 114.482C553.484 124.433 553.484 140.567 563.549 150.518L581.775 168.537C591.84 178.488 608.16 178.488 618.225 168.537L636.451 150.518C646.516 140.567 646.516 124.433 636.451 114.482L618.225 96.4634C608.16 86.5122 591.84 86.5122 581.775 96.4634L563.549 114.482Z" fill="#00FFA3"/>
<path d="M149.811 12H156.924V10.75H151.22V6.79545H156.47V5.54545H151.22V1.61364H156.833V0.363636H149.811V12ZM171.537 1.61364H175.196V12H176.605V1.61364H180.264V0.363636H171.537V1.61364ZM195.239 12H196.648V6.79545H202.852V12H204.261V0.363636H202.852V5.54545H196.648V0.363636H195.239V12ZM219.883 12H226.996V10.75H221.292V6.79545H226.542V5.54545H221.292V1.61364H226.905V0.363636H219.883V12ZM242.245 12H243.654V7.45455H246.2C246.302 7.45455 246.398 7.45455 246.495 7.44886L248.95 12H250.586L247.955 7.19318C249.438 6.6875 250.131 5.47727 250.131 3.93182C250.131 1.875 248.904 0.363636 246.177 0.363636H242.245V12ZM243.654 6.18182V1.61364H246.131C248.018 1.61364 248.745 2.53409 248.745 3.93182C248.745 5.32955 248.018 6.18182 246.154 6.18182H243.654ZM265.279 12H272.393V10.75H266.688V6.79545H271.938V5.54545H266.688V1.61364H272.302V0.363636H265.279V12ZM295.278 0.363636V7.95455C295.278 9.65909 294.108 10.8864 292.165 10.8864C290.221 10.8864 289.051 9.65909 289.051 7.95455V0.363636H287.642V8.06818C287.642 10.4545 289.426 12.2045 292.165 12.2045C294.903 12.2045 296.687 10.4545 296.687 8.06818V0.363636H295.278ZM312.301 0.363636V12H313.619V3.15909H313.733L317.369 12H318.642L322.279 3.15909H322.392V12H323.71V0.363636H322.029L318.074 10.0227H317.938L313.983 0.363636H312.301ZM359.863 9.95455H361.136V6.84091H364.249V5.56818H361.136V2.45455H359.863V5.56818H356.749V6.84091H359.863V9.95455ZM403.646 3.27273H405.01C404.947 1.51705 403.328 0.204545 401.078 0.204545C398.851 0.204545 397.101 1.5 397.101 3.45455C397.101 5.02273 398.237 5.95455 400.055 6.47727L401.487 6.88636C402.714 7.22727 403.805 7.65909 403.805 8.81818C403.805 10.0909 402.578 10.9318 400.964 10.9318C399.578 10.9318 398.351 10.3182 398.237 9H396.783C396.919 10.9091 398.464 12.2045 400.964 12.2045C403.646 12.2045 405.169 10.7273 405.169 8.84091C405.169 6.65909 403.101 5.95455 401.896 5.63636L400.714 5.31818C399.851 5.09091 398.464 4.63636 398.464 3.38636C398.464 2.27273 399.487 1.45455 401.033 1.45455C402.442 1.45455 403.51 2.125 403.646 3.27273ZM430.104 6.18182C430.104 2.5 427.945 0.204545 424.967 0.204545C421.99 0.204545 419.831 2.5 419.831 6.18182C419.831 9.86364 421.99 12.1591 424.967 12.1591C427.945 12.1591 430.104 9.86364 430.104 6.18182ZM428.74 6.18182C428.74 9.20455 427.081 10.8409 424.967 10.8409C422.854 10.8409 421.195 9.20455 421.195 6.18182C421.195 3.15909 422.854 1.52273 424.967 1.52273C427.081 1.52273 428.74 3.15909 428.74 6.18182ZM445.273 12H452.091V10.75H446.682V0.363636H445.273V12ZM468.285 12L469.455 8.70455H474.16L475.33 12H476.808L472.535 0.363636H471.08L466.808 12H468.285ZM469.898 7.45455L471.762 2.20455H471.853L473.717 7.45455H469.898ZM500.647 0.363636H499.261V9.52273H499.147L492.784 0.363636H491.42V12H492.829V2.86364H492.943L499.284 12H500.647V0.363636ZM516.744 12L517.915 8.70455H522.619L523.79 12H525.267L520.994 0.363636H519.54L515.267 12H516.744ZM518.358 7.45455L520.221 2.20455H520.312L522.176 7.45455H518.358ZM557.179 12H561.384C564.134 12 565.27 10.6591 565.27 8.90909C565.27 7.06818 563.998 6.06818 562.929 6V5.88636C563.929 5.61364 564.816 4.95455 564.816 3.45455C564.816 1.75 563.679 0.363636 561.248 0.363636H557.179V12ZM558.588 10.75V6.70455H561.452C562.975 6.70455 563.929 7.72727 563.929 8.90909C563.929 9.93182 563.225 10.75 561.384 10.75H558.588ZM558.588 5.47727V1.61364H561.248C562.793 1.61364 563.475 2.43182 563.475 3.45455C563.475 4.68182 562.475 5.47727 561.202 5.47727H558.588ZM580.386 12H581.795V7.45455H584.34C584.442 7.45455 584.539 7.45455 584.636 7.44886L587.09 12H588.727L586.096 7.19318C587.579 6.6875 588.272 5.47727 588.272 3.93182C588.272 1.875 587.045 0.363636 584.317 0.363636H580.386V12ZM581.795 6.18182V1.61364H584.272C586.158 1.61364 586.886 2.53409 586.886 3.93182C586.886 5.32955 586.158 6.18182 584.295 6.18182H581.795ZM604.829 0.363636H603.42V12H604.829V0.363636ZM624.045 12C627.591 12 629.591 9.79545 629.591 6.15909C629.591 2.54545 627.591 0.363636 624.204 0.363636H620.454V12H624.045ZM621.864 10.75V1.61364H624.114C626.841 1.61364 628.227 3.34091 628.227 6.15909C628.227 9 626.841 10.75 623.954 10.75H621.864ZM652.709 4H654.163C653.732 1.75 651.845 0.204545 649.391 0.204545C646.408 0.204545 644.3 2.5 644.3 6.18182C644.3 9.86364 646.391 12.1591 649.482 12.1591C652.254 12.1591 654.232 10.3125 654.232 7.43182V6.18182H649.8V7.43182H652.868C652.828 9.52273 651.453 10.8409 649.482 10.8409C647.323 10.8409 645.663 9.20455 645.663 6.18182C645.663 3.15909 647.323 1.52273 649.391 1.52273C651.073 1.52273 652.215 2.47159 652.709 4ZM669.445 12H676.559V10.75H670.854V6.79545H676.104V5.54545H670.854V1.61364H676.468V0.363636H669.445V12Z" fill="url(#paint6_linear)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M159.495 17.0732L141.269 35.0915C136.572 39.7354 136.572 47.2646 141.269 51.9085L159.495 69.9268C164.192 74.5707 171.808 74.5707 176.505 69.9268L194.731 51.9085C199.428 47.2646 199.428 39.7354 194.731 35.0915L176.505 17.0732C171.808 12.4293 164.192 12.4293 159.495 17.0732ZM131.549 25.4817C121.484 35.4329 121.484 51.5671 131.549 61.5183L149.775 79.5366C159.84 89.4878 176.16 89.4878 186.225 79.5366L204.451 61.5183C214.516 51.5671 214.516 35.4329 204.451 25.4817L186.225 7.46342C176.16 -2.48781 159.84 -2.48781 149.775 7.46342L131.549 25.4817Z" fill="#DC1FFF"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M591.495 17.0732L573.269 35.0915C568.572 39.7354 568.572 47.2646 573.269 51.9085L591.495 69.9268C596.192 74.5707 603.808 74.5707 608.505 69.9268L626.731 51.9085C631.428 47.2646 631.428 39.7354 626.731 35.0915L608.505 17.0732C603.808 12.4293 596.192 12.4293 591.495 17.0732ZM563.549 25.4817C553.484 35.4329 553.484 51.5671 563.549 61.5183L581.775 79.5366C591.84 89.4878 608.16 89.4878 618.225 79.5366L636.451 61.5183C646.516 51.5671 646.516 35.4329 636.451 25.4817L618.225 7.46342C608.16 -2.48781 591.84 -2.48781 581.775 7.46342L563.549 25.4817Z" fill="#00FFA3"/>
<defs>
<filter id="filter0_b" x="-6" y="89" width="828" height="89" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<filter id="filter0_b" x="-6" y="0" width="828" height="89" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feGaussianBlur stdDeviation="3"/>
<feGaussianBlur in="BackgroundImage" stdDeviation="3"/>
<feComposite in2="SourceAlpha" operator="in" result="effect1_backgroundBlur"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_backgroundBlur" result="shape"/>
</filter>
<linearGradient id="paint0_linear" x1="408" y1="67" x2="408" y2="193.791" gradientUnits="userSpaceOnUse">
<linearGradient id="paint0_linear" x1="408" y1="-22" x2="408" y2="104.791" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="#010202" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint1_linear" x1="408" y1="67" x2="408" y2="193.791" gradientUnits="userSpaceOnUse">
<linearGradient id="paint1_linear" x1="408" y1="-22" x2="408" y2="104.791" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="#010202" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint2_linear" x1="408" y1="67" x2="408" y2="193.791" gradientUnits="userSpaceOnUse">
<linearGradient id="paint2_linear" x1="408" y1="-22" x2="408" y2="104.791" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="#010202" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint3_linear" x1="408" y1="67" x2="408" y2="193.791" gradientUnits="userSpaceOnUse">
<linearGradient id="paint3_linear" x1="408" y1="-22" x2="408" y2="104.791" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="#010202" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint4_linear" x1="408" y1="67" x2="408" y2="193.791" gradientUnits="userSpaceOnUse">
<linearGradient id="paint4_linear" x1="408" y1="-22" x2="408" y2="104.791" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="#010202" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint5_linear" x1="408" y1="67" x2="408" y2="193.791" gradientUnits="userSpaceOnUse">
<linearGradient id="paint5_linear" x1="408" y1="-22" x2="408" y2="104.791" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="#010202" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint6_linear" x1="132.986" y1="16" x2="708.028" y2="16" gradientUnits="userSpaceOnUse">
<stop offset="0.130208" stop-color="#DC1FFF"/>
<stop offset="0.5" stop-color="#4DA9FF"/>
<stop offset="0.885417" stop-color="#00CC82"/>
</linearGradient>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 9.0 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

@ -51,9 +51,9 @@ export const AppBar = (props: { isRoot?: boolean }) => {
<div className={`app-bar-item ${isActiveClass('move')}`}>
<Link to="/move">Bridge</Link>
</div>
<div className={`app-bar-item ${isActiveClass('faq')}`}>
<Link to="/faq">FAQ</Link>
</div>
{/*<div className={`app-bar-item ${isActiveClass('faq')}`}>*/}
{/* <Link to="/faq">FAQ</Link>*/}
{/*</div>*/}
<div className={`app-bar-item ${isActiveClass('proof-of-assets')}`}>
<Link to="/proof-of-assets">Proof-of-Assets</Link>
</div>

View File

@ -0,0 +1,45 @@
@import "_colors";
#recent-tx-container {
max-width: 70%;
margin: auto;
padding-bottom: 70px;
.description-text {
color: @tungsten-60
}
.ant-table-pagination.ant-pagination {
margin: 16px 100px;
}
.ant-table {
thead {
tr > th.ant-table-cell {
background-color: @tungsten-100;
border: none;
}
}
tbody > tr:nth-child(even) > td.ant-table-cell {
background-color: @tungsten-100;
border: none;
}
tbody > tr:nth-child(odd) > td.ant-table-cell {
background-color: @tungsten-50;
border: none;
}
}
}
@media screen and (max-width: 900px) {
#recent-tx-container {
max-width: 100%;
}
}
@media screen and (max-width: 1200px) {
#recent-tx-container {
max-width: 90%;
}
}

View File

@ -0,0 +1,134 @@
import { Table } from 'antd';
import React from 'react';
import './index.less';
import { Link } from 'react-router-dom';
import { TokenDisplay } from '../../components/TokenDisplay';
import { toChainSymbol } from '../../contexts/chainPair';
import { formatUSD, shortenAddress } from '@oyster/common';
import { useWormholeAccounts } from '../../hooks/useWormholeAccounts';
export const AssetsTable = () => {
const {
loading: loadingLockedAccounts,
externalAssets,
totalInUSD,
} = useWormholeAccounts();
const columns = [
{
title: 'Symbol',
dataIndex: 'symbol',
key: 'symbol',
render(text: string, record: any) {
return {
props: {
style: {},
},
children: (
<Link
to={`/move?from=${toChainSymbol(record.chain)}&token=${
record.symbol
}`}
>
<span style={{ display: 'inline-flex', alignItems: 'center' }}>
{record.logo && (
<TokenDisplay logo={record.logo} chain={record.chain} />
)}{' '}
{record.symbol}
</span>
</Link>
),
};
},
},
{
title: 'Name',
dataIndex: 'name',
key: 'name',
},
{
title: 'Amount',
dataIndex: 'amount',
key: 'amount',
},
{
title: 'Amount ($)',
dataIndex: 'amountInUSD',
key: 'amountInUSD',
},
{
title: 'Price',
dataIndex: 'price',
width: 100,
key: 'price',
render(text: string, record: any) {
return {
props: {
style: { textAlign: 'right' },
},
children: record.price ? formatUSD.format(record.price) : '--',
};
},
},
{
title: 'Asset Address',
dataIndex: 'address',
key: 'address',
render(text: string, record: any) {
return {
props: {
style: {},
},
children: (
<a href={record.explorer} target="_blank" rel="noopener noreferrer">
{shortenAddress(text, 6)}
</a>
),
};
},
},
{
title: 'Wrapped Address',
dataIndex: 'mintKey',
key: 'mintKey',
render(text: string, record: any) {
return {
props: {
style: {},
},
children: (
<a
href={record.wrappedExplorer}
target="_blank"
rel="noopener noreferrer"
>
{shortenAddress(text, 6)}
</a>
),
};
},
},
];
return (
<div id={'recent-tx-container'}>
<div className={'home-subtitle'}>Total Value Locked</div>
<div
className={'assets-total description-text'}
style={{ marginBottom: '70px', fontSize: '40px' }}
>
{formatUSD.format(totalInUSD)}
</div>
<Table
scroll={{
scrollToFirstRowOnChange: false,
x: 900,
}}
dataSource={externalAssets.filter(a => a.name)}
columns={columns}
loading={loadingLockedAccounts}
/>
</div>
);
};

View File

@ -8,9 +8,8 @@ import {
useWallet,
} from '@oyster/common';
import './style.less';
import { ASSET_CHAIN } from '../../models/bridge/constants';
import { TokenSelectModal } from '../TokenSelectModal';
import { chainToName } from '../../utils/assets';
import { ASSET_CHAIN, chainToName } from '../../utils/assets';
import { TokenChain } from '../TokenDisplay/tokenChain';
import { EthereumConnect } from '../EthereumConnect';
import { CurrentUserWalletBadge } from '../CurrentUserWalletBadge';
@ -41,7 +40,7 @@ export function Input(props: {
props.onInputChange && props.onInputChange(props.balance)
}
>
{props.balance}
{props.balance?.toFixed(6)}
</div>
</div>
<div className="input-container">

View File

@ -25,7 +25,7 @@ export const AppLayout = React.memo((props: any) => {
</Content>
<Footer>
<div className={'description-text'} style={{ color: '#2F506F' }}>
© Solana Foundation
</div>
</Footer>
</Layout>

View File

@ -21,8 +21,8 @@ import { TokenChain } from '../TokenDisplay/tokenChain';
import bs58 from 'bs58';
import { SyncOutlined } from '@ant-design/icons';
import { typeToIcon } from '../Transfer';
import { ProgressUpdate } from '../../models/bridge';
import { WormholeFactory } from '../../contracts/WormholeFactory';
import { ProgressUpdate } from '@solana/bridge-sdk';
import { WormholeFactory } from '@solana/bridge-sdk';
import { useEthereum } from '../../contexts';
import { useBridge } from '../../contexts/bridge';

View File

@ -3,7 +3,7 @@ import { TokenInfo } from '@solana/spl-token-registry';
import { debug } from 'console';
import React from 'react';
import { useEthereum } from '../../contexts';
import { ASSET_CHAIN } from '../../models/bridge/constants';
import { ASSET_CHAIN } from '../../utils/assets';
import './style.less';
import { TokenChain } from './tokenChain';

View File

@ -1,5 +1,5 @@
import React from 'react';
import { ASSET_CHAIN } from '../../models/bridge/constants';
import { ASSET_CHAIN } from '../../utils/assets';
export const TokenChain = (props: {
chain?: ASSET_CHAIN;

View File

@ -6,9 +6,9 @@ import './style.less';
import { Input, Modal } from 'antd';
import { useEthereum } from '../../contexts';
import { TokenDisplay } from '../TokenDisplay';
import { ASSET_CHAIN } from '../../models/bridge/constants';
import { ASSET_CHAIN } from '../../utils/assets';
import { useConnectionConfig } from '@oyster/common';
import { filterModalSolTokens } from '../../utils/assets';
import { filterModalEthTokens, filterModalSolTokens } from '../../utils/assets';
export const TokenSelectModal = (props: {
onSelectToken: (token: string) => void;
@ -24,7 +24,10 @@ export const TokenSelectModal = (props: {
const inputRef = useRef<Input>(null);
const tokens = useMemo(
() => [...ethTokens, ...filterModalSolTokens(solTokens)],
() => [
...filterModalEthTokens(ethTokens),
...filterModalSolTokens(solTokens),
],
[ethTokens, solTokens],
);

View File

@ -11,7 +11,7 @@ import {
ProgressUpdate,
toSolana,
TransferRequest,
} from '../../models/bridge';
} from '@solana/bridge-sdk';
import { useEthereum } from '../../contexts';
import { TokenDisplay } from '../TokenDisplay';
import { useTokenChainPairState } from '../../contexts/chainPair';

View File

@ -22,7 +22,7 @@
.exchange-card {
position: relative;
max-width: calc(100% - 460px);
max-width: 900px;
width: 100%;
margin: 167px auto 150px auto;
display: flex;

View File

@ -1,5 +1,5 @@
import React, { createContext, FunctionComponent, useContext } from 'react';
import { SolanaBridge } from '../core';
import { SolanaBridge } from '@solana/bridge-sdk';
import {
useConnection,
useConnectionConfig,

View File

@ -14,18 +14,23 @@ import {
useUserAccounts,
} from '@oyster/common';
import { TokenInfo } from '@solana/spl-token-registry';
import { ASSET_CHAIN, filterModalSolTokens } from '../utils/assets';
import {
ASSET_CHAIN,
filterModalEthTokens,
filterModalSolTokens,
} from '../utils/assets';
import { useEthereum } from './ethereum';
import { BigNumber } from 'ethers/utils';
import { WrappedAssetFactory } from '../contracts/WrappedAssetFactory';
import { WormholeFactory } from '../contracts/WormholeFactory';
import { BigNumber } from 'bignumber.js';
import { AssetMeta, WrappedAssetFactory } from '@solana/bridge-sdk';
import { WormholeFactory } from '@solana/bridge-sdk';
import {
bridgeAuthorityKey,
TransferRequestInfo,
wrappedAssetMintKey,
} from '../models/bridge';
} from '@solana/bridge-sdk';
import { useBridge } from './bridge';
import { PublicKey } from '@solana/web3.js';
import { ethers } from 'ethers';
export interface TokenChainContextState {
info?: TransferRequestInfo;
@ -104,7 +109,7 @@ export const useCurrencyLeg = (mintAddress: string) => {
name: '',
balance: new BigNumber(0),
decimals: 0,
allowance: new BigNumber(0),
allowance: new ethers.utils.BigNumber(0),
isWrapped: false,
chainID: 0,
assetAddress: new Buffer(0),
@ -117,43 +122,69 @@ export const useCurrencyLeg = (mintAddress: string) => {
(async () => {
const ethToken = ethTokens.find(t => t.address === mintAddress);
const solToken = solTokens.find(t => t.address === mintAddress);
let solToken = solTokens.find(t => t.address === mintAddress);
let mintKeyAddress = '';
let symbol = '';
let decimals = 0;
// eth assets on eth chain
// eth asset on sol chain
// sol asset on eth chain
// sol asset on sol chain
//let ethAddress: string = '';
// console.log({ chain, solToken, ethToken });
//console.log({ chain, solToken, ethToken });
if (chain === ASSET_CHAIN.Solana) {
if (!solToken) {
if (!solToken && ethToken) {
try {
const bridgeId = programIds().wormhole.pubkey;
const authority = await bridgeAuthorityKey(bridgeId);
const assetAddress = Buffer.from(ethToken.address.slice(2), 'hex');
const meta: AssetMeta = {
decimals: Math.min(ethToken.decimals, 9),
address: assetAddress,
chain: ASSET_CHAIN.Ethereum,
};
const mintKey = await wrappedAssetMintKey(
bridgeId,
authority,
meta,
);
if (mintKey) {
mintKeyAddress = mintKey.toBase58();
solToken = solTokens.find(t => t.address === mintKeyAddress);
if (!solToken) {
symbol = ethToken.symbol;
decimals = ethToken.decimals;
}
} else {
setInfo(defaultCoinInfo);
return;
}
} catch {
setInfo(defaultCoinInfo);
return;
}
}
if (!solToken && (!symbol || !mintKeyAddress || !decimals)) {
setInfo(defaultCoinInfo);
return;
}
// TODO: checked if mint is wrapped mint from eth...
const currentAccount = userAccounts?.find(
a => a.info.mint.toBase58() === solToken.address,
a => a.info.mint.toBase58() === (solToken?.address || mintKeyAddress),
);
const assetMeta = await bridge?.fetchAssetMeta(
new PublicKey(solToken.address),
new PublicKey(solToken?.address || mintKeyAddress),
);
if (!assetMeta || !currentAccount) {
setInfo(defaultCoinInfo);
return;
}
let info = {
address: currentAccount.pubkey.toBase58(),
name: solToken.symbol,
name: solToken?.symbol || symbol,
balance: new BigNumber(currentAccount?.info.amount.toNumber() || 0),
allowance: new BigNumber(0),
decimals: solToken.decimals,
allowance: new ethers.utils.BigNumber(0),
decimals: solToken?.decimals || decimals,
isWrapped: assetMeta.chain != ASSET_CHAIN.Solana,
chainID: assetMeta.chain,
assetAddress: assetMeta.address,
mint: solToken.address,
mint: solToken?.address || mintKeyAddress,
};
setInfo(info);
}
@ -193,7 +224,9 @@ export const useCurrencyLeg = (mintAddress: string) => {
}
if (chain === ASSET_CHAIN.Ethereum) {
info.balance = await e.balanceOf(addr);
info.balance = new BigNumber(
new ethers.utils.BigNumber(await e.balanceOf(addr)).toString(),
);
} else {
// TODO: get balance on other chains for assets that came from eth
@ -207,7 +240,7 @@ export const useCurrencyLeg = (mintAddress: string) => {
});
}
// console.log({ info });
//console.log({ info });
setInfo(info);
}
})();
@ -252,7 +285,10 @@ export function TokenChainPairProvider({ children = null as any }) {
const setChainB = quote.setChain;
const tokens = useMemo(
() => [...ethTokens, ...filterModalSolTokens(solTokens)],
() => [
...filterModalEthTokens(ethTokens),
...filterModalSolTokens(solTokens),
],
[ethTokens, solTokens],
);

View File

@ -224,6 +224,7 @@ export const EthereumProvider: FunctionComponent = ({ children }) => {
return (
<Button
key={provider.url}
size="large"
type={providerUrl === provider.url ? 'primary' : 'ghost'}
onClick={onClick}

View File

@ -1,30 +0,0 @@
import { useCallback, useEffect, useRef, useState } from 'react';
import {
useConnection,
useConnectionConfig,
MintParser,
cache,
getMultipleAccounts,
ParsedAccount,
TokenAccountParser,
} from '@oyster/common';
import { WORMHOLE_PROGRAM_ID } from '../utils/ids';
import { ASSET_CHAIN } from '../utils/assets';
import { useEthereum } from '../contexts';
import { Connection, PublicKey } from '@solana/web3.js';
import { models } from '@oyster/common';
import { AccountInfo, MintInfo, TOKEN_PROGRAM_ID } from '@solana/spl-token';
import { WrappedMetaLayout } from './../models/bridge';
import bs58 from 'bs58';
import {
COINGECKO_COIN_PRICE_API,
COINGECKO_POOL_INTERVAL,
useCoingecko,
} from '../contexts/coingecko';
export const useEthUserAccount = () => {
const [address, setAddress] = useState('');
// const { web3 } = useEthereum();
return address;
};

View File

@ -20,7 +20,7 @@ import {
bridgeAuthorityKey,
wrappedAssetMintKey,
WrappedMetaLayout,
} from './../models/bridge';
} from '@solana/bridge-sdk';
import bs58 from 'bs58';
import {
@ -275,7 +275,8 @@ export const useWormholeAccounts = () => {
})();
return () => {
connection.removeProgramAccountChangeListener(wormholeSubId);
if (wormholeSubId !== 0)
connection.removeProgramAccountChangeListener(wormholeSubId);
};
}, [connection, setExternalAssets]);
@ -330,7 +331,7 @@ export const useWormholeAccounts = () => {
if (ids.length === 0) {
return;
}
console.log('Querying Prices...');
const parameters = `?ids=${ids.join(',')}&vs_currencies=usd`;
const resp = await window.fetch(COINGECKO_COIN_PRICE_API + parameters);
const data = await resp.json();

View File

@ -5,6 +5,7 @@ import {
programIds,
notify,
useWallet,
ParsedAccountBase,
} from '@oyster/common';
import {
WORMHOLE_PROGRAM_ID,
@ -21,7 +22,7 @@ import {
import {
bridgeAuthorityKey,
TransferOutProposalLayout,
} from './../models/bridge';
} from '@solana/bridge-sdk';
import bs58 from 'bs58';
import {
@ -29,27 +30,26 @@ import {
COINGECKO_POOL_INTERVAL,
useCoingecko,
} from '../contexts/coingecko';
import { BN } from 'bn.js';
import { WormholeFactory } from '../contracts/WormholeFactory';
import { BigNumber } from 'bignumber.js';
import { WormholeFactory } from '@solana/bridge-sdk';
import { ethers } from 'ethers';
import { useBridge } from '../contexts/bridge';
import { SolanaBridge } from '../core';
import { SolanaBridge } from '@solana/bridge-sdk';
import BN from 'bn.js';
type WrappedTransferMeta = {
chain: number;
decimals: number;
address: string;
publicKey: PublicKey;
coinId?: string;
price?: number;
explorer?: any;
logo?: string;
symbol?: string;
amount: number;
value?: number | string;
txhash?: string;
txhash: string;
date: number; // timestamp
status?: string;
owner?: string;
@ -57,6 +57,8 @@ type WrappedTransferMeta = {
vaa?: any;
};
const transferCache = new Map<string, WrappedTransferMeta>();
const queryWrappedMetaTransactions = async (
authorityKey: PublicKey,
connection: Connection,
@ -103,14 +105,16 @@ const queryWrappedMetaTransactions = async (
assetAddress = new PublicKey(metaTransfer.assetAddress).toBase58();
}
const dec = new BN(10).pow(new BN(metaTransfer.assetDecimals));
const rawAmount = new BN(metaTransfer.amount, 2, 'le');
const div = rawAmount.div(dec).toNumber();
const mod = rawAmount.mod(dec).toNumber();
const amount = parseFloat(div + '.' + mod.toString());
const dec = new BigNumber(10).pow(
new BigNumber(metaTransfer.assetDecimals),
);
const rawAmount = new BigNumber(
new BN(metaTransfer.amount, 2, 'le').toString(),
);
const amount = rawAmount.div(dec).toNumber();
const txhash = acc.publicKey.toBase58();
transfers.set(assetAddress, {
transfers.set(txhash, {
publicKey: acc.publicKey,
amount,
date: metaTransfer.vaaTime,
@ -127,80 +131,97 @@ const queryWrappedMetaTransactions = async (
await Promise.all(
[...transfers.values()].map(async transfer => {
const resp = await (connection as any)._rpcRequest(
'getConfirmedSignaturesForAddress2',
[transfer.publicKey.toBase58()],
);
for (const sig of resp.result) {
const confirmedTx = await connection.getParsedConfirmedTransaction(
sig.signature,
const cachedTransfer = transferCache.get(transfer.txhash);
if (cachedTransfer && cachedTransfer.status === 'Completed') {
transfer.vaa = cachedTransfer.vaa;
transfer.status = cachedTransfer.status;
transfer.owner = cachedTransfer.owner;
} else {
const resp = await (connection as any)._rpcRequest(
'getConfirmedSignaturesForAddress2',
[transfer.publicKey.toBase58()],
);
if (!confirmedTx) continue;
const instructions = confirmedTx.transaction?.message?.instructions;
const filteredInstructions = instructions?.filter(ins => {
return ins.programId.toBase58() === WORMHOLE_PROGRAM_ID.toBase58();
});
if (filteredInstructions && filteredInstructions?.length > 0) {
for (const ins of filteredInstructions) {
const data = bs58.decode((ins as PartiallyDecodedInstruction).data);
if (data[0] === TRANSFER_ASSETS_OUT_INSTRUCTION) {
try {
transfer.owner = (ins as PartiallyDecodedInstruction).accounts[10].toBase58();
} catch {
// Catch no owner
transfer.owner = '';
}
}
if (
data[0] === POSTVAA_INSTRUCTION &&
confirmedTx.meta?.err == null &&
bridge
) {
const lockup = transfer.lockup;
let vaa = lockup.vaa;
for (let i = vaa.length; i > 0; i--) {
if (vaa[i] == 0xff) {
vaa = vaa.slice(0, i);
break;
for (const sig of resp.result) {
const confirmedTx = await connection.getParsedConfirmedTransaction(
sig.signature,
'finalized',
);
if (!confirmedTx) continue;
const instructions = confirmedTx.transaction?.message?.instructions;
const filteredInstructions = instructions?.filter(ins => {
return ins.programId.toBase58() === WORMHOLE_PROGRAM_ID.toBase58();
});
if (filteredInstructions && filteredInstructions?.length > 0) {
for (const ins of filteredInstructions) {
const data = bs58.decode(
(ins as PartiallyDecodedInstruction).data,
);
if (data[0] === TRANSFER_ASSETS_OUT_INSTRUCTION) {
try {
transfer.owner = (ins as PartiallyDecodedInstruction).accounts[10].toBase58();
} catch {
// Catch no owner
transfer.owner = '';
}
}
let signatures = await bridge.fetchSignatureStatus(
lockup.signatureAccount,
);
let sigData = Buffer.of(
...signatures.reduce((previousValue, currentValue) => {
previousValue.push(currentValue.index);
previousValue.push(...currentValue.signature);
return previousValue;
}, new Array<number>()),
);
if (
data[0] === POSTVAA_INSTRUCTION &&
confirmedTx.meta?.err == null &&
bridge
) {
const lockup = transfer.lockup;
let vaa = lockup.vaa;
for (let i = vaa.length; i > 0; i--) {
if (vaa[i] == 0xff) {
vaa = vaa.slice(0, i);
break;
}
}
try {
let signatures = await bridge.fetchSignatureStatus(
lockup.signatureAccount,
);
let sigData = Buffer.of(
...signatures.reduce((previousValue, currentValue) => {
previousValue.push(currentValue.index);
previousValue.push(...currentValue.signature);
vaa = Buffer.concat([
vaa.slice(0, 5),
Buffer.of(signatures.length),
sigData,
vaa.slice(6),
]);
try {
if (vaa?.length) {
const _ = await wh.parseAndVerifyVAA(vaa);
transfer.status = 'Failed';
transfer.vaa = vaa;
//TODO: handle vaa not posted
//console.log({ result });
} else {
return previousValue;
}, new Array<number>()),
);
vaa = Buffer.concat([
vaa.slice(0, 5),
Buffer.of(signatures.length),
sigData,
vaa.slice(6),
]);
try {
if (vaa?.length) {
const _ = await wh.parseAndVerifyVAA(vaa);
transfer.status = 'Failed';
transfer.vaa = vaa;
//TODO: handle vaa not posted
//console.log({ result });
} else {
transfer.status = 'Error';
transfer.vaa = vaa;
//TODO: handle empty data
//console.log({ vaa });
}
} catch (e) {
//console.log({ error: e });
transfer.vaa = vaa;
transfer.status = 'Completed';
transferCache.set(transfer.txhash, transfer);
}
} catch (e) {
transfer.status = 'Error';
transfer.vaa = vaa;
//TODO: handle empty data
//console.log({ vaa });
//TODO: handle error
}
} catch (e) {
//console.log({ error: e });
transfer.vaa = vaa;
transfer.status = 'Completed';
}
}
}
@ -297,6 +318,7 @@ export const useWormholeTransactions = () => {
if (ids.length === 0) return;
console.log('Querying Prices...');
const parameters = `?ids=${ids.join(',')}&vs_currencies=usd`;
const resp = await window.fetch(COINGECKO_COIN_PRICE_API + parameters);
const usdByCoinId = await resp.json();
@ -313,7 +335,7 @@ export const useWormholeTransactions = () => {
() => dataSourcePriceQuery(),
COINGECKO_POOL_INTERVAL,
);
}, [transfers, setAmountInUSD]);
}, [transfers, setAmountInUSD, coinList]);
useEffect(() => {
if (transfers && coinList && !loading) {

View File

@ -41,7 +41,7 @@ export function Routes() {
component={() => <HomeView />}
/>
<Route path="/move" children={<TransferView />} />
<Route path="/faq" children={<FaqView />} />
{/*<Route path="/faq" children={<FaqView />} />*/}
<Route
path="/proof-of-assets"
children={<ProofOfAssetsView />}

View File

@ -49,6 +49,18 @@ export const chainToName = (chain?: ASSET_CHAIN) => {
return CHAIN_NAME[chain || ASSET_CHAIN.Ethereum];
};
const EXCLUDED_COMMON_TOKENS = ['usdt', 'usdc'];
const EXCLUDED_SPL_TOKENS = ['sol', 'srm', ...EXCLUDED_COMMON_TOKENS];
export const filterModalSolTokens = (tokens: TokenInfo[]) => {
return tokens.filter(token => token.symbol !== 'SOL');
return tokens.filter(
token => EXCLUDED_SPL_TOKENS.indexOf(token.symbol.toLowerCase()) < 0,
);
};
const EXCLUDED_ETH_TOKENS = [...EXCLUDED_COMMON_TOKENS];
export const filterModalEthTokens = (tokens: TokenInfo[]) => {
return tokens.filter(
token => EXCLUDED_ETH_TOKENS.indexOf(token.symbol.toLowerCase()) < 0,
);
};

View File

@ -0,0 +1,28 @@
.description-container {
.ant-row {
align-items: center;
margin-bottom: 50px;
}
.main-logo {
.logo-title {
margin-bottom: 0;
font-size: 35px;
letter-spacing: 0;
}
}
}
.q-title {
font-size: 32px;
line-height: 160%;
max-width: 80%;
margin: auto;
}
.q-description {
font-size: 20px;
line-height: 160%;
max-width: 80%;
margin: auto;
margin-top: 20px;
text-align: justify;
}

View File

@ -1,4 +1,5 @@
import React from 'react';
import { Row, Col } from 'antd';
import './index.less';
@ -6,7 +7,47 @@ export const HelpView = () => {
return (
<div
className="flexColumn transfer-bg"
style={{ flex: 1, minHeight: '90vh' }}
></div>
style={{ flex: 1, minHeight: '90vh', paddingTop: '200px' }}
>
<div className={'description-container'}>
<Row>
<Col xs={24} sm={12}>
<div className={'q-title'}>How does Wormhole Work?</div>
<p className={'q-description'}>
Wormhole allows existing projects, platforms, and communities to
move tokenized assets seamlessly across blockchains to benefit
from Solanas high speed and low cost. Wormhole does this by
wrapping ERC-20 tokens, which are then usable in Solanas low-cost
defi ecosystem.
</p>
</Col>
<Col xs={24} sm={12}>
<div className={'main-logo'}>
<img src={'/help/overview.svg'} />
</div>
</Col>
</Row>
<Row>
<Col xs={24} sm={12}>
<div className={'main-logo'}>
<a
href={'https://github.com/certusone/wormhole'}
target={'_blank'}
>
<div className={'logo-title'}> get started</div>
</a>
</div>
</Col>
<Col xs={24} sm={12}>
<div className={'q-title'}>
How can I integrate Wormhole into my wallet or dapp?
</div>
<p className={'q-description'}>
Wormhole is an open-source project accessible to all.
</p>
</Col>
</Row>
</div>
</div>
);
};

View File

@ -25,6 +25,18 @@ section.ant-layout {
width: 25px;
height: 21px;
}
.logo-title {
/* SOLANA <-> ETHEREUM BRIDGE */
background: linear-gradient(90deg, #DC1FFF 11.29%, #4DA9FF 51.42%, #00CC82 93.23%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
font-size: 16px;
line-height: 160%;
text-align: center;
letter-spacing: 0.8em;
text-transform: uppercase;
margin-bottom: 90px;
}
.home-subtitle {
font-size: 32px;
@ -55,7 +67,9 @@ section.ant-layout {
.home-description {
display: flex;
flex-direction: row;
justify-content: space-between;
width: 90%;
margin: auto;
justify-content: space-evenly;
.home-description-item {
padding: 0 30px 0 30px;
@ -116,7 +130,7 @@ section.ant-layout {
font-size: 16px;
line-height: 160%;
width: auto;
margin: 3px 20px 30px 20px;
margin: 3px 20px 40px 20px;
}
& > div:first-child {
margin-bottom: 120px;

View File

@ -1,4 +1,3 @@
import { Table } from 'antd';
import anime from 'animejs';
import React from 'react';
import { formatUSD, shortenAddress } from '@oyster/common';
@ -8,109 +7,9 @@ import { Link } from 'react-router-dom';
import { useWormholeAccounts } from '../../hooks/useWormholeAccounts';
import { TokenDisplay } from '../../components/TokenDisplay';
import { toChainSymbol } from '../../contexts/chainPair';
import { AssetsTable } from '../../components/AssetsTable';
export const HomeView = () => {
const {
loading: loadingLockedAccounts,
externalAssets,
totalInUSD,
} = useWormholeAccounts();
const columns = [
{
title: 'Symbol',
dataIndex: 'symbol',
key: 'symbol',
render(text: string, record: any) {
return {
props: {
style: {},
},
children: (
<Link
to={`/move?from=${toChainSymbol(record.chain)}&token=${
record.symbol
}`}
>
<span style={{ display: 'inline-flex', alignItems: 'center' }}>
{record.logo && (
<TokenDisplay logo={record.logo} chain={record.chain} />
)}{' '}
{record.symbol}
</span>
</Link>
),
};
},
},
{
title: 'Name',
dataIndex: 'name',
key: 'name',
},
{
title: 'Amount',
dataIndex: 'amount',
key: 'amount',
},
{
title: 'Amount ($)',
dataIndex: 'amountInUSD',
key: 'amountInUSD',
},
{
title: 'Price',
dataIndex: 'price',
width: 100,
key: 'price',
render(text: string, record: any) {
return {
props: {
style: { textAlign: 'right' },
},
children: record.price ? formatUSD.format(record.price) : '--',
};
},
},
{
title: 'Asset Address',
dataIndex: 'address',
key: 'address',
render(text: string, record: any) {
return {
props: {
style: {},
},
children: (
<a href={record.explorer} target="_blank" rel="noopener noreferrer">
{shortenAddress(text, 6)}
</a>
),
};
},
},
{
title: 'Wrapped Address',
dataIndex: 'mintKey',
key: 'mintKey',
render(text: string, record: any) {
return {
props: {
style: {},
},
children: (
<a
href={record.wrappedExplorer}
target="_blank"
rel="noopener noreferrer"
>
{shortenAddress(text, 6)}
</a>
),
};
},
},
];
const handleDownArrow = () => {
const scrollTo = document.getElementById('how-it-works-container');
const scrollElement =
@ -129,11 +28,15 @@ export const HomeView = () => {
<div className="flexColumn home-container">
<div className={'justify-bottom-container wormhole-bg'}>
<div className={'main-logo'}>
<div className={'logo-title'}>
{' '}
SOLANA &lt;-&gt; ETHEREUM BRIDGE
</div>
<img src={'/home/main-logo.svg'} />
</div>
<div>
A decentralized and bi-directional bridge for
<br /> ERC-20 and SPL tokens
Easily move any tokens between Ethereum and Solana <br /> with
Wormholes bi-directional bridge
</div>
<div className={'grow-effect'}>
<Link to="/move">
@ -175,24 +78,7 @@ export const HomeView = () => {
</div>
</div>
</div>
<div id={'recent-tx-container'}>
<div className={'home-subtitle'}>Total Value Locked</div>
<div
className={'assets-total description-text'}
style={{ marginBottom: '70px', fontSize: '40px' }}
>
{formatUSD.format(totalInUSD)}
</div>
<Table
scroll={{
scrollToFirstRowOnChange: false,
x: 900,
}}
dataSource={externalAssets.filter(a => a.name)}
columns={columns}
loading={loadingLockedAccounts}
/>
</div>
<AssetsTable />
</div>
</>
);

View File

@ -0,0 +1,3 @@
div:not(.home-container) > #recent-tx-container {
margin-top: 150px;
}

View File

@ -1,12 +1,15 @@
import React from 'react';
import './index.less';
import { AssetsTable } from '../../components/AssetsTable';
export const ProofOfAssetsView = () => {
return (
<div
className="flexColumn transfer-bg"
style={{ flex: 1, minHeight: '90vh' }}
></div>
>
<AssetsTable />
</div>
);
};

View File

@ -1,6 +1,7 @@
{
"compilerOptions": {
"target": "es5",
"module": "esnext",
"target": "es2019",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
@ -9,14 +10,13 @@
"strict": true,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"downlevelIteration": true,
"noEmit": true,
"jsx": "react",
"typeRoots": ["types", "../../types", "../../node_modules/@types"]
"typeRoots": ["types", "../../types", "../../node_modules/@types"],
"moduleResolution": "node"
},
"include": ["src"]
}

View File

@ -40,6 +40,7 @@
"@types/react-router-dom": "^5.1.6",
"@welldone-software/why-did-you-render": "^6.0.5",
"antd": "^4.6.6",
"bignumber.js": "^9.0.1",
"bn.js": "^5.1.3",
"borsh": "^0.3.1",
"bs58": "^4.0.1",

View File

@ -120,7 +120,7 @@ export class AuctionData {
/// End time is the cut-off point that the auction is forced to end by.
endAuctionAt: BN | null;
/// Gap time is the amount of time in slots after the previous bid at which the auction ends.
endAuctionGap: BN | null;
auctionGap: BN | null;
/// The state the auction is in, whether it has started or ended.
state: AuctionState;
/// Auction Bids, each user may have one bid open at a time.
@ -138,7 +138,7 @@ export class AuctionData {
lastBid: BN | null;
endedAt: BN | null;
endAuctionAt: BN | null;
endAuctionGap: BN | null;
auctionGap: BN | null;
state: AuctionState;
bidState: BidState;
}) {
@ -148,7 +148,7 @@ export class AuctionData {
this.lastBid = args.lastBid;
this.endedAt = args.endedAt;
this.endAuctionAt = args.endAuctionAt;
this.endAuctionGap = args.endAuctionGap;
this.auctionGap = args.auctionGap;
this.state = args.state;
this.bidState = args.bidState;
}
@ -220,7 +220,7 @@ class CreateAuctionArgs {
/// End time is the cut-off point that the auction is forced to end by. See AuctionData.
endAuctionAt: BN | null;
/// Gap time is how much time after the previous bid where the auction ends. See AuctionData.
endAuctionGap: BN | null;
auctionGap: BN | null;
/// Token mint for the SPL token used for bidding.
tokenMint: PublicKey;
/// Authority
@ -231,14 +231,14 @@ class CreateAuctionArgs {
constructor(args: {
winners: WinnerLimit;
endAuctionAt: BN | null;
endAuctionGap: BN | null;
auctionGap: BN | null;
tokenMint: PublicKey;
authority: PublicKey;
resource: PublicKey;
}) {
this.winners = args.winners;
this.endAuctionAt = args.endAuctionAt;
this.endAuctionGap = args.endAuctionGap;
this.auctionGap = args.auctionGap;
this.tokenMint = args.tokenMint;
this.authority = args.authority;
this.resource = args.resource;
@ -274,7 +274,7 @@ export const AUCTION_SCHEMA = new Map<any, any>([
['instruction', 'u8'],
['winners', WinnerLimit],
['endAuctionAt', { kind: 'option', type: 'u64' }],
['endAuctionGap', { kind: 'option', type: 'u64' }],
['auctionGap', { kind: 'option', type: 'u64' }],
['tokenMint', 'pubkey'],
['authority', 'pubkey'],
['resource', 'pubkey'],
@ -323,7 +323,7 @@ export const AUCTION_SCHEMA = new Map<any, any>([
['lastBid', { kind: 'option', type: 'u64' }],
['endedAt', { kind: 'option', type: 'u64' }],
['endAuctionAt', { kind: 'option', type: 'u64' }],
['endAuctionGap', { kind: 'option', type: 'u64' }],
['auctionGap', { kind: 'option', type: 'u64' }],
['state', 'u8'],
['bidState', BidState],
],
@ -384,7 +384,7 @@ export async function createAuction(
winners: WinnerLimit,
resource: PublicKey,
endAuctionAt: BN | null,
endAuctionGap: BN | null,
auctionGap: BN | null,
tokenMint: PublicKey,
authority: PublicKey,
creator: PublicKey,
@ -399,7 +399,7 @@ export async function createAuction(
winners,
resource,
endAuctionAt,
endAuctionGap,
auctionGap,
tokenMint,
authority,
}),

View File

@ -328,22 +328,30 @@ const UseNativeAccount = () => {
);
useEffect(() => {
if (!connection || !wallet?.publicKey) {
return;
}
let subId = 0;
const updateAccount = (account: AccountInfo<Buffer> | null) => {
if (account) {
updateCache(account);
setNativeAccount(account);
}
};
connection.getAccountInfo(wallet.publicKey).then(acc => {
if (acc) {
updateCache(acc);
setNativeAccount(acc);
(async () => {
if (!connection || !wallet?.publicKey) {
return;
}
});
connection.onAccountChange(wallet.publicKey, acc => {
if (acc) {
updateCache(acc);
setNativeAccount(acc);
const account = await connection.getAccountInfo(wallet.publicKey)
updateAccount(account);
subId = connection.onAccountChange(wallet.publicKey, updateAccount);
})();
return () => {
if (subId) {
connection.removeAccountChangeListener(subId);
}
});
}
}, [setNativeAccount, wallet, wallet?.publicKey, connection, updateCache]);
return { nativeAccount };

View File

@ -24,6 +24,7 @@ import {
export type ENV =
| 'mainnet-beta'
| 'mainnet-beta (Serum)'
| 'testnet'
| 'devnet'
| 'localnet'
@ -32,6 +33,11 @@ export type ENV =
export const ENDPOINTS = [
{
name: 'mainnet-beta' as ENV,
endpoint: 'https://api.mainnet-beta.solana.com',
ChainId: ChainId.MainnetBeta,
},
{
name: 'mainnet-beta (Serum)' as ENV,
endpoint: 'https://solana-api.projectserum.com/',
ChainId: ChainId.MainnetBeta,
},
@ -412,6 +418,7 @@ export const sendTransactionWithRetry = async (
commitment: Commitment = 'singleGossip',
includesFeePayer: boolean = false,
block?: BlockhashAndFeeCalculator,
beforeSend?: () => void,
) => {
let transaction = new Transaction();
instructions.forEach(instruction => transaction.add(instruction));
@ -436,6 +443,10 @@ export const sendTransactionWithRetry = async (
transaction = await wallet.signTransaction(transaction);
}
if (beforeSend) {
beforeSend();
}
const { txid, slot } = await sendSignedTransaction({
connection,
signedTransaction: transaction,
@ -480,7 +491,7 @@ export async function sendSignedTransaction({
connection.sendRawTransaction(rawTransaction, {
skipPreflight: true,
});
await sleep(300);
await sleep(500);
}
})();
try {
@ -521,7 +532,7 @@ export async function sendSignedTransaction({
}
throw new Error(JSON.stringify(simulateResult.err));
}
throw new Error('Transaction failed');
// throw new Error('Transaction failed');
} finally {
done = true;
}
@ -632,7 +643,7 @@ async function awaitTransactionSignatureConfirmation(
}
}
})();
await sleep(1000);
await sleep(2000);
}
})();
})

View File

@ -88,7 +88,7 @@ export const PROGRAM_IDS = [
{
name: 'testnet',
governance: () => ({
programId: new PublicKey('DCVPuhaGNMLh73FRWFroH4o3ERUhBKMRWfBgJV94VqRk'),
programId: new PublicKey('A9KW1nhwZUr1kMX8C6rgzZvAE9AwEEUi2C77SiVvEiuN'),
}),
wormhole: () => ({
pubkey: new PublicKey('5gQf5AUhAgWYgUCt9ouShm9H7dzzXUsLdssYwe5krKhg'),
@ -103,10 +103,11 @@ export const PROGRAM_IDS = [
legacy: [],
}),
},
{
name: 'devnet',
governance: () => ({
programId: new PublicKey('DCVPuhaGNMLh73FRWFroH4o3ERUhBKMRWfBgJV94VqRk'),
programId: new PublicKey('A9KW1nhwZUr1kMX8C6rgzZvAE9AwEEUi2C77SiVvEiuN'),
}),
wormhole: () => ({
pubkey: new PublicKey('WormT3McKhFJ2RkiGpdw9GKvNCrB2aB54gb2uV9MfQC'),
@ -142,7 +143,7 @@ export const PROGRAM_IDS = [
];
export const setProgramIds = (envName: string) => {
let instance = PROGRAM_IDS.find(env => env.name === envName);
let instance = PROGRAM_IDS.find(env => envName.indexOf(env.name) >= 0);
if (!instance) {
return;
}

View File

@ -37,9 +37,9 @@ export const uint64 = (property = 'uint64'): unknown => {
return new BN(
[...data]
.reverse()
.map((i) => `00${i.toString(16)}`.slice(-2))
.map(i => `00${i.toString(16)}`.slice(-2))
.join(''),
16
16,
);
};
@ -69,9 +69,9 @@ export const uint128 = (property = 'uint128'): unknown => {
return new BN(
[...data]
.reverse()
.map((i) => `00${i.toString(16)}`.slice(-2))
.map(i => `00${i.toString(16)}`.slice(-2))
.join(''),
16
16,
);
};
@ -100,7 +100,7 @@ export const rustString = (property = 'string'): unknown => {
BufferLayout.u32('lengthPadding'),
BufferLayout.blob(BufferLayout.offset(BufferLayout.u32(), -8), 'chars'),
],
property
property,
);
const _decode = rsl.decode.bind(rsl);
const _encode = rsl.encode.bind(rsl);

View File

@ -49,6 +49,7 @@ export class TorusWalletAdapter extends EventEmitter implements WalletAdapter {
this._provider = new OpenLogin({
clientId: process.env.REACT_APP_CLIENT_ID || 'BNxdRWx08cSTPlzMAaShlM62d4f8Tp6racfnCg_gaH0XQ1NfSGo3h5B_IkLtgSnPMhlxsSvhqugWm0x8x-VkUXA',
network: "mainnet", // mainnet, testnet, development
uxMode: 'popup'
});
try {

View File

@ -69,8 +69,6 @@ export const createProposal = async (
adminValidationAccount,
adminDestinationAccount,
sigDestinationAccount,
yesVoteDumpAccount,
noVoteDumpAccount,
sourceHoldingAccount,
authority,
instructions: associatedInstructions,
@ -137,8 +135,6 @@ export const createProposal = async (
voteValidationAccount,
adminDestinationAccount,
sigDestinationAccount,
yesVoteDumpAccount,
noVoteDumpAccount,
sourceHoldingAccount,
useGovernance
? governance.info.governanceMint
@ -165,8 +161,6 @@ export const createProposal = async (
instructions,
],
[...associatedSigners, createGovernanceAccountsSigners, signers],
true,
true,
);
notify({
@ -193,8 +187,6 @@ interface ValidationReturn {
adminValidationAccount: PublicKey;
adminDestinationAccount: PublicKey;
sigDestinationAccount: PublicKey;
yesVoteDumpAccount: PublicKey;
noVoteDumpAccount: PublicKey;
sourceHoldingAccount: PublicKey;
authority: PublicKey;
signers: Account[][];
@ -329,24 +321,6 @@ async function getAssociatedAccountsAndInstructions(
let holdingSigners: Account[] = [];
let holdingInstructions: TransactionInstruction[] = [];
const yesVoteDumpAccount = createTokenAccount(
holdingInstructions,
wallet.publicKey,
accountRentExempt,
yesVoteMint,
authority,
holdingSigners,
);
const noVoteDumpAccount = createTokenAccount(
holdingInstructions,
wallet.publicKey,
accountRentExempt,
noVoteMint,
authority,
holdingSigners,
);
const sourceHoldingAccount = createTokenAccount(
holdingInstructions,
wallet.publicKey,
@ -369,8 +343,6 @@ async function getAssociatedAccountsAndInstructions(
adminValidationAccount,
adminDestinationAccount,
sigDestinationAccount,
yesVoteDumpAccount,
noVoteDumpAccount,
sourceHoldingAccount,
authority,
signers: [

View File

@ -7,16 +7,9 @@ import {
import { contexts, utils, actions, SequenceType } from '@oyster/common';
import { AccountLayout, MintLayout, Token } from '@solana/spl-token';
import {
GOVERNANCE_AUTHORITY_SEED,
ExecutionType,
Governance,
GovernanceType,
VotingEntryRule,
} from '../models/governance';
import { initGovernanceInstruction } from '../models/initGovernance';
import { GOVERNANCE_AUTHORITY_SEED, Governance } from '../models/governance';
import { createGovernanceInstruction } from '../models/createGovernance';
import BN from 'bn.js';
import { createEmptyGovernanceInstruction } from '../models/createEmptyGovernance';
const { sendTransactions } = contexts.Connection;
const { createMint, createTokenAccount } = actions;
@ -126,29 +119,18 @@ export const registerProgramGovernance = async (
);
instructions.push(
createEmptyGovernanceInstruction(
createGovernanceInstruction(
governanceKey,
uninitializedGovernance.program,
programDataAccount,
wallet.publicKey,
uninitializedGovernance.governanceMint,
wallet.publicKey,
uninitializedGovernance.councilMint,
),
);
instructions.push(
initGovernanceInstruction(
governanceKey,
uninitializedGovernance.program,
uninitializedGovernance.governanceMint,
uninitializedGovernance.voteThreshold!,
uninitializedGovernance.executionType || ExecutionType.Independent,
uninitializedGovernance.governanceType || GovernanceType.Governance,
uninitializedGovernance.votingEntryRule || VotingEntryRule.Anytime,
uninitializedGovernance.minimumSlotWaitingPeriod || new BN(0),
uninitializedGovernance.timeLimit || new BN(0),
uninitializedGovernance.name || '',
wallet.publicKey,
uninitializedGovernance.councilMint,
),
);

View File

@ -136,8 +136,6 @@ export const withdrawVotingTokens = async (
existingNoVoteAccount,
destinationAccount,
proposal.info.sourceHolding,
proposal.info.yesVotingDump,
proposal.info.noVotingDump,
proposal.info.votingMint,
proposal.info.yesVotingMint,
proposal.info.noVotingMint,

View File

@ -108,10 +108,9 @@ export const LABELS = {
GOVERNANCE_MINT: 'Governance Mint ID',
USE_COUNCIL_MINT: 'Allow Council Mint?',
COUNCIL_MINT: 'Council Mint ID',
PROPOSAL_TYPE: 'Proposal Type',
EXECUTION_TYPE: 'Execution Type',
VOTE_PERCENT_THRESHOLD: 'Vote Threshold (%)',
VOTING_ENTRY_RULES: 'Voting Entry Rules',
SELECT_PROPOSAL_TYPE: 'Select the type of proposals this app will generate',
SELECT_EXECUTION_TYPE: 'Select how transactions will be executed',

View File

@ -1,56 +0,0 @@
import { PublicKey, TransactionInstruction } from '@solana/web3.js';
import { utils } from '@oyster/common';
import * as BufferLayout from 'buffer-layout';
import { GovernanceInstruction } from './governance';
/// 0. `[]` Governance account. The account pubkey needs to be set to PDA with the following seeds:
/// 1) 'governance' const prefix, 2) Governed Program account key
/// 1. `[]` Account of the Program governed by this Governance account
/// 2. `[writable]` Program Data account of the Program governed by this Governance account
/// 3. `[signer]` Current Upgrade Authority account of the Program governed by this Governance account
/// 4. `[signer]` Payer
/// 5. `[]` System account.
/// 6. `[]` bpf_upgrade_loader account.
export const createEmptyGovernanceInstruction = (
governanceAccount: PublicKey,
programAccount: PublicKey,
programDataAccount: PublicKey,
programUpgradeAuthority: PublicKey,
governanceMint: PublicKey,
payer: PublicKey,
councilMint?: PublicKey,
): TransactionInstruction => {
const PROGRAM_IDS = utils.programIds();
const dataLayout = BufferLayout.struct([BufferLayout.u8('instruction')]);
const data = Buffer.alloc(dataLayout.span);
dataLayout.encode(
{
instruction: GovernanceInstruction.CreateEmptyGovernance,
},
data,
);
const keys = [
{ pubkey: governanceAccount, isSigner: false, isWritable: false },
{ pubkey: programAccount, isSigner: false, isWritable: false },
{ pubkey: programDataAccount, isSigner: false, isWritable: true },
{ pubkey: programUpgradeAuthority, isSigner: true, isWritable: false },
{ pubkey: payer, isSigner: true, isWritable: false },
{ pubkey: PROGRAM_IDS.system, isSigner: false, isWritable: false },
{
pubkey: PROGRAM_IDS.bpf_upgrade_loader,
isSigner: false,
isWritable: false,
},
];
return new TransactionInstruction({
keys,
programId: PROGRAM_IDS.governance.programId,
data,
});
};

View File

@ -34,11 +34,7 @@ export const createEmptyGovernanceVotingRecordInstruction = (
{ pubkey: proposalAccount, isSigner: false, isWritable: false },
{ pubkey: votingAccount, isSigner: false, isWritable: false },
{ pubkey: payer, isSigner: true, isWritable: false },
{
pubkey: PROGRAM_IDS.governance.programId,
isSigner: false,
isWritable: false,
},
{ pubkey: PROGRAM_IDS.system, isSigner: false, isWritable: false },
];
return new TransactionInstruction({

View File

@ -1,47 +1,49 @@
import { PublicKey, TransactionInstruction } from '@solana/web3.js';
import { utils } from '@oyster/common';
import * as BufferLayout from 'buffer-layout';
import { CONFIG_NAME_LENGTH, GovernanceInstruction } from './governance';
import { GOVERNANCE_NAME_LENGTH, GovernanceInstruction } from './governance';
import BN from 'bn.js';
import * as Layout from '../utils/layout';
/// 0. `[writable]` Governance account. The account pubkey needs to be set to PDA with the following seeds:
/// 1) 'governance' const prefix, 2) Governed Program account key
/// 1. `[]` Account of the Program governed by this Governance account
/// 2. `[]` Governance mint that this Governance uses
/// 3. `[]` Council mint that this Governance uses [Optional]
export const initGovernanceInstruction = (
/// 2. `[writable]` Program Data account of the Program governed by this Governance account
/// 3. `[signer]` Current Upgrade Authority account of the Program governed by this Governance account
/// 4. `[]` Governance mint that this Governance uses
/// 5. `[signer]` Payer
/// 6. `[]` System account.
/// 7. `[]` bpf_upgrade_loader account.
/// 8. `[]` Council mint that this Governance uses [Optional]
export const createGovernanceInstruction = (
governanceAccount: PublicKey,
programAccount: PublicKey,
governedProgramAccount: PublicKey,
governedProgramDataAccount: PublicKey,
governedProgramUpgradeAuthority: PublicKey,
governanceMint: PublicKey,
voteThreshold: number,
executionType: number,
governanceType: number,
votingEntryRule: number,
minimumSlotWaitingPeriod: BN,
timeLimit: BN,
name: string,
payer: PublicKey,
councilMint?: PublicKey,
): TransactionInstruction => {
const PROGRAM_IDS = utils.programIds();
if (name.length > CONFIG_NAME_LENGTH) {
throw new Error('Name is more than ' + CONFIG_NAME_LENGTH);
if (name.length > GOVERNANCE_NAME_LENGTH) {
throw new Error('Name is more than ' + GOVERNANCE_NAME_LENGTH);
}
const dataLayout = BufferLayout.struct([
BufferLayout.u8('instruction'),
BufferLayout.u8('voteThreshold'),
BufferLayout.u8('executionType'),
BufferLayout.u8('governanceType'),
BufferLayout.u8('votingEntryRule'),
Layout.uint64('minimumSlotWaitingPeriod'),
Layout.uint64('timeLimit'),
BufferLayout.seq(BufferLayout.u8(), CONFIG_NAME_LENGTH, 'name'),
BufferLayout.seq(BufferLayout.u8(), GOVERNANCE_NAME_LENGTH, 'name'),
]);
const nameAsBytes = utils.toUTF8Array(name);
for (let i = nameAsBytes.length; i <= CONFIG_NAME_LENGTH - 1; i++) {
for (let i = nameAsBytes.length; i <= GOVERNANCE_NAME_LENGTH - 1; i++) {
nameAsBytes.push(0);
}
@ -49,11 +51,8 @@ export const initGovernanceInstruction = (
dataLayout.encode(
{
instruction: GovernanceInstruction.InitGovernance,
instruction: GovernanceInstruction.CreateGovernance,
voteThreshold,
executionType,
governanceType,
votingEntryRule,
minimumSlotWaitingPeriod,
timeLimit,
name: nameAsBytes,
@ -63,8 +62,21 @@ export const initGovernanceInstruction = (
const keys = [
{ pubkey: governanceAccount, isSigner: false, isWritable: true },
{ pubkey: programAccount, isSigner: false, isWritable: false },
{ pubkey: governedProgramAccount, isSigner: false, isWritable: false },
{ pubkey: governedProgramDataAccount, isSigner: false, isWritable: true },
{
pubkey: governedProgramUpgradeAuthority,
isSigner: true,
isWritable: false,
},
{ pubkey: governanceMint, isSigner: false, isWritable: false },
{ pubkey: payer, isSigner: true, isWritable: false },
{ pubkey: PROGRAM_IDS.system, isSigner: false, isWritable: false },
{
pubkey: PROGRAM_IDS.bpf_upgrade_loader,
isSigner: false,
isWritable: false,
},
];
if (councilMint) {

View File

@ -27,17 +27,15 @@ export const executeInstruction = (
): TransactionInstruction => {
const PROGRAM_IDS = utils.programIds();
const dataLayout = BufferLayout.struct([
BufferLayout.u8('instruction'),
BufferLayout.u8('numberOfExtraAccounts'),
]);
const dataLayout = BufferLayout.struct([BufferLayout.u8('instruction')]);
const data = Buffer.alloc(dataLayout.span);
console.log('ACCTS', accountInfos);
dataLayout.encode(
{
instruction: GovernanceInstruction.Execute,
numberOfExtraAccounts: accountInfos.length,
},
data,
);

View File

@ -6,7 +6,7 @@ import { utils } from '@oyster/common';
export const DESC_SIZE = 200;
export const NAME_SIZE = 32;
export const CONFIG_NAME_LENGTH = 32;
export const GOVERNANCE_NAME_LENGTH = 32;
export const INSTRUCTION_LIMIT = 450;
export const MAX_TRANSACTIONS = 5;
export const TEMP_FILE_TXN_SIZE = 1000;
@ -21,13 +21,12 @@ export enum GovernanceInstruction {
AddCustomSingleSignerTransaction = 4,
Sign = 8,
Vote = 9,
InitGovernance = 10,
CreateGovernance = 10,
Execute = 11,
DepositGovernanceTokens = 12,
WithdrawVotingTokens = 13,
CreateEmptyGovernance = 14,
CreateGovernanceVotingRecord = 15,
CreateGovernanceVotingRecord = 14,
}
export interface GovernanceVotingRecord {
@ -63,12 +62,7 @@ export interface Governance {
accountType: GovernanceAccountType;
/// Vote threshold
voteThreshold: number;
/// Execution type
executionType: ExecutionType;
/// Governance Type
governanceType: GovernanceType;
/// Voting entry rule
votingEntryRule: VotingEntryRule;
/// Minimum slot time-distance from creation of proposal for an instruction to be placed
minimumSlotWaitingPeriod: BN;
/// Governance mint
@ -98,33 +92,18 @@ export const GovernanceLayout: typeof BufferLayout.Structure = BufferLayout.stru
[
BufferLayout.u8('accountType'),
BufferLayout.u8('voteThreshold'),
BufferLayout.u8('executionType'),
BufferLayout.u8('governanceType'),
BufferLayout.u8('votingEntryRule'),
Layout.uint64('minimumSlotWaitingPeriod'),
Layout.publicKey('governanceMint'),
BufferLayout.u8('councilMintOption'),
Layout.publicKey('councilMint'),
Layout.publicKey('program'),
Layout.uint64('timeLimit'),
BufferLayout.seq(BufferLayout.u8(), CONFIG_NAME_LENGTH, 'name'),
BufferLayout.seq(BufferLayout.u8(), GOVERNANCE_NAME_LENGTH, 'name'),
BufferLayout.u32('count'),
BufferLayout.seq(BufferLayout.u8(), 295, 'padding'),
],
);
export enum VotingEntryRule {
Anytime = 0,
}
export enum ExecutionType {
Independent = 0,
}
export enum GovernanceType {
Governance = 0,
}
export enum ProposalStateStatus {
/// Draft
Draft = 0,
@ -180,7 +159,6 @@ export const ProposalLayout: typeof BufferLayout.Structure = BufferLayout.struct
[
BufferLayout.u8('accountType'),
Layout.publicKey('config'),
Layout.publicKey('tokenProgramId'),
Layout.publicKey('state'),
Layout.publicKey('signatoryMint'),
Layout.publicKey('adminMint'),
@ -192,8 +170,7 @@ export const ProposalLayout: typeof BufferLayout.Structure = BufferLayout.struct
Layout.publicKey('adminValidation'),
Layout.publicKey('votingValidation'),
Layout.publicKey('sourceHolding'),
Layout.publicKey('yesVotingDump'),
Layout.publicKey('noVotingDump'),
BufferLayout.seq(BufferLayout.u8(), 300, 'padding'),
],
);
@ -225,9 +202,6 @@ export interface Proposal {
/// configuration values
config: PublicKey;
/// Token Program ID
tokenProgramId: PublicKey;
/// state values
state: PublicKey;
@ -262,12 +236,6 @@ export interface Proposal {
/// Governance holding account
sourceHolding: PublicKey;
/// Yes Voting dump account for exchanged vote tokens
yesVotingDump: PublicKey;
/// No Voting dump account for exchanged vote tokens
noVotingDump: PublicKey;
}
export const CustomSingleSignerTransactionLayout: typeof BufferLayout.Structure = BufferLayout.struct(
@ -309,7 +277,6 @@ export const ProposalParser = (
info: {
accountType: data.accountType,
config: data.config,
tokenProgramId: data.tokenProgramId,
state: data.state,
signatoryMint: data.signatoryMint,
adminMint: data.adminMint,
@ -321,8 +288,6 @@ export const ProposalParser = (
adminValidation: data.adminValidation,
votingValidation: data.votingValidation,
sourceHolding: data.sourceHolding,
yesVotingDump: data.yesVotingDump,
noVotingDump: data.noVotingDump,
},
};
@ -431,9 +396,7 @@ export const GovernanceParser = (
info: {
accountType: data.accountType,
voteThreshold: data.voteThreshold,
executionType: data.executionType,
governanceType: data.governanceType,
votingEntryRule: data.votingEntryRule,
minimumSlotWaitingPeriod: data.minimumSlotWaitingPeriod,
governanceMint: data.governanceMint,
councilMint: data.councilMintOption === 1 ? data.councilMint : null,

View File

@ -23,8 +23,6 @@ import { DESC_SIZE, NAME_SIZE, GovernanceInstruction } from './governance';
/// 10. `[writable]` Initialized Voting Validation account
/// 11. `[writable]` Initialized Destination account for first admin token
/// 12. `[writable]` Initialized Destination account for first signatory token
/// 13. `[writable]` Initialized Yes voting dump account
/// 14. `[writable]` Initialized No voting dump account
/// 15. `[writable]` Initialized source holding account
/// 16. `[]` Source mint
/// 17. `[]` Governance minting authority (pda with seed of Proposal key)
@ -44,8 +42,6 @@ export const initProposalInstruction = (
votingValidationAccount: PublicKey,
destinationAdminAccount: PublicKey,
destinationSignatoryAccount: PublicKey,
yesVotingDumpAccount: PublicKey,
noVotingDumpAccount: PublicKey,
sourceHoldingAccount: PublicKey,
sourceMintAccount: PublicKey,
authority: PublicKey,
@ -101,8 +97,6 @@ export const initProposalInstruction = (
{ pubkey: votingValidationAccount, isSigner: false, isWritable: true },
{ pubkey: destinationAdminAccount, isSigner: false, isWritable: true },
{ pubkey: destinationSignatoryAccount, isSigner: false, isWritable: true },
{ pubkey: yesVotingDumpAccount, isSigner: false, isWritable: true },
{ pubkey: noVotingDumpAccount, isSigner: false, isWritable: true },
{ pubkey: sourceHoldingAccount, isSigner: false, isWritable: true },
{
pubkey: sourceMintAccount,

View File

@ -15,8 +15,6 @@ import BN from 'bn.js';
/// 3. `[writable]` Initialized No Voting account from which to remove your voting tokens.
/// 4. `[writable]` User token account that you wish your actual tokens to be returned to.
/// 5. `[writable]` Source holding account owned by the Governance that will has the actual tokens in escrow.
/// 6. `[writable]` Initialized Yes Voting dump account owned by Proposal to which to send your voting tokens.
/// 7. `[writable]` Initialized No Voting dump account owned by Proposal to which to send your voting tokens.
/// 8. `[writable]` Voting mint account.
/// 9. `[writable]` Yes Voting mint account.
/// 10. `[writable]` No Voting mint account.
@ -32,8 +30,6 @@ export const withdrawVotingTokensInstruction = (
noVotingAccount: PublicKey,
destinationAccount: PublicKey,
sourceHoldingAccount: PublicKey,
yesVotingDump: PublicKey,
noVotingDump: PublicKey,
votingMint: PublicKey,
yesVotingMint: PublicKey,
noVotingMint: PublicKey,
@ -67,8 +63,6 @@ export const withdrawVotingTokensInstruction = (
{ pubkey: noVotingAccount, isSigner: false, isWritable: true },
{ pubkey: destinationAccount, isSigner: false, isWritable: true },
{ pubkey: sourceHoldingAccount, isSigner: false, isWritable: true },
{ pubkey: yesVotingDump, isSigner: false, isWritable: true },
{ pubkey: noVotingDump, isSigner: false, isWritable: true },
{ pubkey: votingMint, isSigner: false, isWritable: true },
{ pubkey: yesVotingMint, isSigner: false, isWritable: true },
{ pubkey: noVotingMint, isSigner: false, isWritable: true },

View File

@ -9,12 +9,7 @@ import {
deserializeMint,
ParsedAccount,
} from '@oyster/common';
import {
ExecutionType,
Governance,
GovernanceType,
VotingEntryRule,
} from '../../models/governance';
import { Governance } from '../../models/governance';
import { PublicKey } from '@solana/web3.js';
import { Table } from 'antd';
import MintSourceTokens from '../../components/Proposal/MintSourceTokens';
@ -32,24 +27,7 @@ const columns = [
key: 'voteThreshold',
render: (number: number) => <span>{number}</span>,
},
{
title: LABELS.EXECUTION_TYPE,
dataIndex: 'executionType',
key: 'executionType',
render: (number: number) => <span>{ExecutionType[number]}</span>,
},
{
title: LABELS.PROPOSAL_TYPE,
dataIndex: 'governanceType',
key: 'governanceType',
render: (number: number) => <span>{GovernanceType[number]}</span>,
},
{
title: LABELS.VOTING_ENTRY_RULES,
dataIndex: 'votingEntryRule',
key: 'votingEntryRule',
render: (number: number) => <span>{VotingEntryRule[number]}</span>,
},
{
title: LABELS.MINIMUM_SLOT_WAITING_PERIOD,
dataIndex: 'minimumSlotWaitingPeriod',

View File

@ -1,13 +1,8 @@
import React, { useState } from 'react';
import { Button, ButtonProps, InputNumber, Modal, Switch } from 'antd';
import { Form, Input, Select } from 'antd';
import { Form, Input } from 'antd';
import { PublicKey } from '@solana/web3.js';
import {
CONFIG_NAME_LENGTH,
ExecutionType,
GovernanceType,
VotingEntryRule,
} from '../../models/governance';
import { GOVERNANCE_NAME_LENGTH } from '../../models/governance';
import { LABELS } from '../../constants';
import { contexts, utils, tryParseKey } from '@oyster/common';
import { registerProgramGovernance } from '../../actions/registerProgramGovernance';
@ -16,7 +11,6 @@ import BN from 'bn.js';
const { useWallet } = contexts.Wallet;
const { useConnection } = contexts.Connection;
const { Option } = Select;
const { notify } = utils;
const layout = {
@ -68,10 +62,8 @@ export function NewForm({
const wallet = useWallet();
const connection = useConnection();
const onFinish = async (values: {
governanceType: GovernanceType;
executionType: ExecutionType;
voteThreshold: number;
votingEntryRule: VotingEntryRule;
minimumSlotWaitingPeriod: string;
timeLimit: string;
governanceMint: string;
@ -120,10 +112,8 @@ export function NewForm({
}
const uninitializedGovernance = {
governanceType: values.governanceType,
executionType: values.executionType,
voteThreshold: values.voteThreshold,
votingEntryRule: values.votingEntryRule,
minimumSlotWaitingPeriod: new BN(values.minimumSlotWaitingPeriod),
governanceMint: values.governanceMint
? new PublicKey(values.governanceMint)
@ -154,7 +144,7 @@ export function NewForm({
>
<Form {...layout} form={form} name="control-hooks" onFinish={onFinish}>
<Form.Item name="name" label="Name" rules={[{ required: true }]}>
<Input maxLength={CONFIG_NAME_LENGTH} />
<Input maxLength={GOVERNANCE_NAME_LENGTH} />
</Form.Item>
<Form.Item
name="program"
@ -204,36 +194,6 @@ export function NewForm({
>
<InputNumber maxLength={3} min={1} max={100} />
</Form.Item>
<Form.Item
name="executionType"
label={LABELS.EXECUTION_TYPE}
rules={[{ required: true }]}
initialValue={ExecutionType.Independent}
>
<Select placeholder={LABELS.SELECT_EXECUTION_TYPE}>
<Option value={ExecutionType.Independent}>Independent</Option>
</Select>
</Form.Item>
<Form.Item
name="governanceType"
label={LABELS.PROPOSAL_TYPE}
rules={[{ required: true }]}
initialValue={GovernanceType.Governance}
>
<Select placeholder={LABELS.SELECT_PROPOSAL_TYPE}>
<Option value={GovernanceType.Governance}>Single Signer</Option>
</Select>
</Form.Item>
<Form.Item
name="votingEntryRule"
label={LABELS.VOTING_ENTRY_RULES}
rules={[{ required: true }]}
initialValue={VotingEntryRule.Anytime}
>
<Select placeholder={LABELS.SELECT_VOTING_ENTRY_RULE}>
<Option value={VotingEntryRule.Anytime}>At any time</Option>
</Select>
</Form.Item>
</Form>
</Modal>
);

View File

@ -18,10 +18,10 @@
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.5.0",
"@testing-library/user-event": "^7.2.1",
"@toruslabs/openlogin": "^0.7.0",
"@toruslabs/openlogin-ed25519": "^0.7.0",
"@toruslabs/openlogin-utils": "^0.7.0",
"@toruslabs/torus-embed": "^1.9.10",
"@toruslabs/openlogin": "^0.8.0",
"@toruslabs/openlogin-ed25519": "^0.8.0",
"@toruslabs/openlogin-utils": "^0.8.0",
"@toruslabs/torus-embed": "^1.10.9",
"@types/animejs": "^3.1.3",
"@types/chart.js": "^2.9.29",
"@types/react-router-dom": "^5.1.6",

View File

@ -70,8 +70,8 @@ export async function createAuctionManager(
wallet: any,
settings: AuctionManagerSettings,
winnerLimit: WinnerLimit,
duration: BN,
gap: BN,
endAuctionAt: BN,
auctionGap: BN,
safetyDeposits: SafetyDepositDraft[],
paymentMint: PublicKey,
): Promise<{
@ -99,7 +99,14 @@ export async function createAuctionManager(
instructions: makeAuctionInstructions,
signers: makeAuctionSigners,
auction,
} = await makeAuction(wallet, winnerLimit, vault, duration, gap, paymentMint);
} = await makeAuction(
wallet,
winnerLimit,
vault,
endAuctionAt,
auctionGap,
paymentMint,
);
let nftConfigs = safetyDeposits.map((w, i) => ({
tokenAccount: w.holding,

View File

@ -10,8 +10,8 @@ export async function makeAuction(
wallet: any,
winnerLimit: WinnerLimit,
vault: PublicKey,
duration: BN,
gap: BN,
endAuctionAt: BN,
auctionGap: BN,
paymentMint: PublicKey,
): Promise<{
auction: PublicKey;
@ -43,8 +43,8 @@ export async function makeAuction(
createAuction(
winnerLimit,
vault,
duration,
gap,
endAuctionAt,
auctionGap,
paymentMint,
auctionManagerKey,
wallet.publicKey,

View File

@ -43,7 +43,9 @@ export const mintNFT = async (
env: ENV,
files: File[],
metadata: { name: string; symbol: string },
): Promise<void> => {
): Promise<{
metadataAccount: PublicKey,
} | void> => {
if (!wallet?.publicKey) {
return;
}
@ -258,6 +260,8 @@ export const mintNFT = async (
// TODO:
// 1. Jordan: --- upload file and metadata to storage API
// 2. pay for storage by hashing files and attaching memo for each file
return { metadataAccount }
};
export const prepPayForFilesTxn = async (

View File

@ -10,7 +10,7 @@
display: flex;
flex-direction: row;
align-items: flex-start;
padding: 24px;
padding: 0;
text-align: left;
/* gradients/metal */
@ -27,8 +27,17 @@
font-family: Inter;
font-size: 20px;
font-weight: 600;
padding: 24px 24px 0 24px;
}
.ant-card-meta {
width: 100%;
}
}
.ant-avatar.ant-avatar-circle {
margin-left: 24px;
}
.small {
@ -36,3 +45,19 @@
margin-left: auto;
margin-right: auto;
}
.cd-container {
background: linear-gradient(270deg, #616774 7.29%, #403F4C 100%);
box-shadow: inset 0px 4px 20px rgba(0, 0, 0, 0.3);
border-radius: 0px 0px 8px 8px;
margin-top: 20px;
padding: 24px;
}
.cd-title {
color: rgba(255, 255, 255, 0.7);
}
.cd-time {
color: white;
}

View File

@ -1,12 +1,16 @@
import React, { useLayoutEffect, useState } from 'react';
import React, { useEffect, useState } from 'react';
import { Card, Avatar, CardProps, Button } from 'antd';
import { MetadataCategory } from '@oyster/common';
import { ArtContent } from './../ArtContent';
import './index.less';
import { getCountdown } from '../../utils/utils';
import { useArt } from '../../hooks';
import { PublicKey } from '@solana/web3.js';
const { Meta } = Card;
export interface ArtCardProps extends CardProps {
pubkey?: PublicKey;
image?: string;
category?: MetadataCategory
name?: string;
@ -16,10 +20,34 @@ export interface ArtCardProps extends CardProps {
preview?: boolean;
small?: boolean;
close?: () => void;
endAuctionAt?: number;
}
export const ArtCard = (props: ArtCardProps) => {
const { className, small, category, image, name, preview, artist, close, ...rest } = props;
let { className, small, category, image, name, preview, artist, description, close, pubkey, endAuctionAt, ...rest } = props;
const art = useArt(pubkey);
category = art?.category || category;
image = art?.image || image;
name = art?.title || name || '';
artist = art?.artist || artist;
description = art?.about || description;
const [hours, setHours] = useState<number>(23)
const [minutes, setMinutes] = useState<number>(59)
const [seconds, setSeconds] = useState<number>(59)
useEffect(() => {
const interval = setInterval(() => {
if (!endAuctionAt) return
const { hours, minutes, seconds } = getCountdown(endAuctionAt)
setHours(hours)
setMinutes(minutes)
setSeconds(seconds)
}, 1000)
return () => clearInterval(interval)
}, [])
return (
<Card
hoverable={true}
@ -28,7 +56,7 @@ export const ArtCard = (props: ArtCardProps) => {
{close && <Button className="card-close-button" shape="circle" onClick={(e) => {
e.stopPropagation();
e.preventDefault();
close();
close && close();
}} >X</Button>}
<ArtContent category={category} content={image} preview={preview} />
</>}
@ -36,9 +64,19 @@ export const ArtCard = (props: ArtCardProps) => {
>
<Meta
title={`${name}`}
description={<span>
description={<div>
<Avatar src="img/artist1.jpeg" /> {artist}
</span>}
{endAuctionAt &&
<div className="cd-container">
{(hours == 0 && minutes == 0 && seconds == 0) ?
<div className="cd-title">Finished</div>
: <>
<div className="cd-title">Ending in</div>
<div className="cd-time">{hours}h {minutes}m {seconds}s</div>
</>}
</div>
}
</div>}
/>
</Card>
);

View File

@ -23,6 +23,7 @@ import { sendPlaceBid } from '../../actions/sendPlaceBid';
import { sendRedeemBid } from '../../actions/sendRedeemBid';
const { useWallet } = contexts.Wallet;
export const AuctionCard = ({ auctionView }: { auctionView: AuctionView }) => {
const [days, setDays] = useState<number>(99);
const [hours, setHours] = useState<number>(23);
const [minutes, setMinutes] = useState<number>(59);
const [seconds, setSeconds] = useState<number>(59);
@ -47,17 +48,15 @@ export const AuctionCard = ({ auctionView }: { auctionView: AuctionView }) => {
useEffect(() => {
const interval = setInterval(() => {
const slotDiff =
(auctionView.auction.info.endedAt?.toNumber() || 0) - clock;
// const { hours, minutes, seconds } = getCountdown(
// auctionView.auction.info.endedAt?.toNumber(),
// );
const { days, hours, minutes, seconds } = getCountdown(
auctionView.auction.info.endAuctionAt?.toNumber() as number
);
// setHours(hours);
// setMinutes(minutes);
// setSeconds(seconds);
setHours(1);
setDays(Math.min(days, 99));
setHours(hours);
setMinutes(minutes);
setSeconds(seconds);
}, 1000);
return () => clearInterval(interval);
}, [clock]);
@ -72,21 +71,29 @@ export const AuctionCard = ({ auctionView }: { auctionView: AuctionView }) => {
{isStarted && <div className="info-header">HIGHEST BID</div>}
{isStarted && <div style={{ fontWeight: 700, fontSize: '1.6rem' }}>40.00</div>}
<br />
<div className="info-header">AUCTION ENDS IN</div>
<Row style={{ width: 300 }}>
<Col span={8}>
<div className="cd-number">{hours || '--'}</div>
<div className="cd-label">hours</div>
</Col>
<Col span={8}>
<div className="cd-number">{minutes || '--'}</div>
<div className="cd-label">minutes</div>
</Col>
<Col span={8}>
<div className="cd-number">{seconds || '--'}</div>
<div className="cd-label">seconds</div>
</Col>
</Row>
{(days == 0 && hours == 0 && minutes == 0 && seconds == 0) ?
<div className="info-header">AUCTION HAS ENDED</div>
: <>
<div className="info-header">AUCTION ENDS IN</div>
<Row style={{ width: 300 }}>
{days > 0 && <Col span={8}>
<div className="cd-number">{days}</div>
<div className="cd-label">days</div>
</Col>}
<Col span={8}>
<div className="cd-number">{hours}</div>
<div className="cd-label">hours</div>
</Col>
<Col span={8}>
<div className="cd-number">{minutes}</div>
<div className="cd-label">minutes</div>
</Col>
{!days && <Col span={8}>
<div className="cd-number">{seconds}</div>
<div className="cd-label">seconds</div>
</Col>}
</Row>
</>}
<br />
<div
className="info-content"
@ -156,7 +163,7 @@ export const AuctionCard = ({ auctionView }: { auctionView: AuctionView }) => {
);
};
export const AuctionBids = ({bids}: {bids : ParsedAccount<BidderMetadata>[]}) => {
export const AuctionBids = ({ bids }: { bids: ParsedAccount<BidderMetadata>[] }) => {
return (
<Col style={{ width: '100%' }}>
{bids.map((bid, index) => {

Some files were not shown because too many files have changed in this diff Show More