Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Time locking treasury #83

Merged
merged 65 commits into from
Apr 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
343d55e
Refactor treasury creating
siegfried Apr 19, 2022
931c230
Add address in modal
siegfried Apr 19, 2022
8661d7e
Fix cypress test
siegfried Apr 19, 2022
ce8e930
Remove setAddress
siegfried Apr 19, 2022
ba076b3
Add required symbol
siegfried Apr 19, 2022
534cdd8
Improve toggle
siegfried Apr 19, 2022
9c3c815
Add getTipQuery
siegfried Apr 19, 2022
7188c12
Implement Timelock script
siegfried Apr 20, 2022
267d887
Extract NumberInput
siegfried Apr 20, 2022
c8fc47c
Apply NumberInput in TimeLockInput
siegfried Apr 20, 2022
32c8510
Fix 0 balance in sidebar listing
siegfried Apr 20, 2022
d820253
Add recursive script viewer
siegfried Apr 20, 2022
920c4b7
Apply NativeScriptView
siegfried Apr 20, 2022
a486fda
Fix NativeScriptView
siegfried Apr 20, 2022
5aa19da
Remove warning
siegfried Apr 20, 2022
e860cd5
Forbid empty script
siegfried Apr 20, 2022
0c93f32
Show address
siegfried Apr 20, 2022
0dbb0c7
Remove (min. 2)
siegfried Apr 20, 2022
337a097
Fix cypress
siegfried Apr 20, 2022
8982850
Rename Key Hash to Signature
siegfried Apr 20, 2022
8d6fedd
Refactor
siegfried Apr 20, 2022
cfee03d
Fix truncation in sidebar
siegfried Apr 20, 2022
5a24542
Add badge to script items
siegfried Apr 20, 2022
8ed18a7
Rename
siegfried Apr 20, 2022
a723edb
Extract native script badges
siegfried Apr 20, 2022
b4b44e5
Apply the new NativeScriptViewer
siegfried Apr 21, 2022
78b062b
Add cardano to NativeScriptViewer
siegfried Apr 21, 2022
7600589
Add copy signature button
siegfried Apr 21, 2022
5ec31a1
Move function
siegfried Apr 21, 2022
c98f204
Replace getTipQuery with getChainStatus
siegfried Apr 21, 2022
8397f64
Add chain progress bar
siegfried Apr 21, 2022
4f06601
Remove useGetTipQuery
siegfried Apr 21, 2022
de1eb43
Show verified status of NativeScript
siegfried Apr 21, 2022
3796f89
Fix treasury editing
siegfried Apr 21, 2022
3e5dae5
Verify time lock on signing page
siegfried Apr 21, 2022
e011499
Increase the poll interval
siegfried Apr 21, 2022
dbdc730
Implement slot timer base on system time
siegfried Apr 22, 2022
16f48b1
Replace chain status query with system timer
siegfried Apr 22, 2022
30e3aff
Improve script viewer in transaction
siegfried Apr 22, 2022
45e28d0
Rename SystemTime to Date
siegfried Apr 22, 2022
fce351b
Show date in NativeScriptViewer
siegfried Apr 22, 2022
a3c4b2e
Initialize current slot in new treasury
siegfried Apr 22, 2022
f0b0351
Add calendar to input slot
siegfried Apr 22, 2022
33476c0
Make grids responsive
siegfried Apr 22, 2022
30b9ba0
Add warning to expiry timelock
siegfried Apr 22, 2022
a8ce78c
Add warning to start slot
siegfried Apr 22, 2022
1f6df73
Add isRed to calendar
siegfried Apr 23, 2022
9469bd1
Show expired dates
siegfried Apr 23, 2022
b335f8d
Improve policy choosing
siegfried Apr 23, 2022
45882d5
Preview script
siegfried Apr 23, 2022
2f8d979
Make time lock stricter in viewer
siegfried Apr 23, 2022
4ec1f56
Improve treasury edit
siegfried Apr 23, 2022
7523f3d
Improve cypress treasuries spec
siegfried Apr 23, 2022
435e6ff
Fix treasury edit
siegfried Apr 23, 2022
cba0d07
Show timelock time
siegfried Apr 23, 2022
0aee2d7
Memorize transaction result
siegfried Apr 25, 2022
6c49f75
Move NewTransaction to new.tsx
siegfried Apr 26, 2022
8ba3185
Memorize parsed NativeScript
siegfried Apr 26, 2022
d65a9f9
Add tests
siegfried Apr 28, 2022
3320e60
Fix recipient id
siegfried Apr 28, 2022
3613236
Memorize txResult
siegfried Apr 28, 2022
bc45f05
Add suggestStartSlot and suggestExpirySlot
siegfried Apr 28, 2022
7b9d683
Add start and expiry slot to tx builder
siegfried Apr 28, 2022
d88a7a5
Update note
siegfried Apr 28, 2022
27b740c
Make Add Signer look like button
siegfried Apr 28, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 25 additions & 31 deletions cypress/integration/treasuries_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,6 @@ describe('Create a new treasury', () => {

cy.url()
.should('eq', 'http://localhost:3000/treasuries/new')

cy.contains('New Signer (min. 2)')
.should('be.visible')
})

it('Should fill title and description', () => {
Expand All @@ -30,40 +27,41 @@ describe('Create a new treasury', () => {
})

it('Should add signers', () => {
cy.contains('Add')
.should('be.disabled')
addresses.forEach((address) => {
cy.contains('Add signer').click()

cy.get('textarea[placeholder="Add signer address and press enter"]')
.type("abcdefghijk")

cy.contains('Add')
.should('be.disabled')
cy.contains('New Signer')
.should('be.visible')

cy.get('textarea[placeholder="Add signer address and press enter"]')
.type('{selectall}{backspace}')

var signers = 0
addresses.map((address) => {
cy.get('textarea[placeholder="Add signer address and press enter"]')
.type(address)
.should("have.value", address)

cy.contains('Add')
cy.contains('Add Address')
.should('be.enabled')

cy.contains('Add')
cy.contains('Add Address')
.click()
})

cy.contains('Add')
.should('be.disabled')
cy.contains('Signers')
.parent()
.find('ul')
.children()
.should('have.length', 2)

signers++;
cy.contains('Signers')
.parent()
.find('ul')
.children()
.should('have.length', signers)
})
cy.contains('Add signer').click()

cy.contains('Add Address')
.should('be.disabled')

cy.get('textarea[placeholder="Add signer address and press enter"]')
.type("abcdefghijk")

cy.contains('Add Address')
.should('be.disabled')

cy.contains('Cancel').click()
})
//TODO: add test to check if add button is disabled when using wrong address

Expand Down Expand Up @@ -124,8 +122,6 @@ describe('Create a new treasury', () => {
cy.contains('Edit Info')
.click()

cy.wait(1500)

cy.get('input[placeholder="Write Name"]')
.type(addedName)
.should("have.value", editedName);
Expand All @@ -137,8 +133,6 @@ describe('Create a new treasury', () => {
cy.contains('Save Treasury')
.click()

cy.wait(1500)

cy.contains(editedName)
.should('be.visible')

Expand Down Expand Up @@ -187,4 +181,4 @@ describe('Create a new treasury', () => {

cy.url().should('eq', 'http://localhost:3000/treasuries/gwMCgoIAWBz2v%2FtCURgnKF2D4mFnXqjUTGnZS54dlZQ1d90KggBYHOHZFcEMhAAXvTkIioJQeycVCkOOiQd4QiFJEwk%3D')
})
})
})
81 changes: 39 additions & 42 deletions src/cardano/multiplatform-lib.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,34 @@
import type { ShelleyProtocolParams, TransactionOutput } from '@cardano-graphql/client-ts'
import type { Address, BaseAddress, BigNum, Ed25519KeyHash, NativeScript, NativeScripts, NetworkInfo, ScriptHash, Transaction, TransactionBuilder, TransactionHash, TransactionUnspentOutputs, Value as CardanoValue, Vkeywitness } from '@dcspark/cardano-multiplatform-lib-browser'
import type { Address, BaseAddress, BigNum, Ed25519KeyHash, NativeScript, NetworkInfo, ScriptHash, Transaction, TransactionBuilder, TransactionHash, TransactionOutput as CardanoTransactionOutput, TransactionUnspentOutputs, Value as CardanoValue, Vkeywitness } from '@dcspark/cardano-multiplatform-lib-browser'
import { nanoid } from 'nanoid'
import { useEffect, useState } from 'react'
import type { Config } from './config'
import type { Value } from './query-api'
import { getAssetName, getPolicyId } from './query-api'

type CardanoWASM = typeof import('@dcspark/cardano-multiplatform-lib-browser')
type MultiSigType = 'all' | 'any' | 'atLeast'
type Recipient = {
id: string
address: string
value: Value
}

const newRecipient = (): Recipient => {
return {
id: nanoid(),
address: '',
value: {
lovelace: BigInt(0),
assets: new Map()
}
}
}

const isAddressNetworkCorrect = (config: Config, address: Address): boolean => {
const networkId = address.network_id()
return config.isMainnet ? networkId === 1 : networkId === 0
}

type Result<T> =
| { isOk: true, data: T }
Expand Down Expand Up @@ -78,6 +101,19 @@ class Cardano {
return this._wasm
}

public buildTxOutput(recipient: Recipient): Result<CardanoTransactionOutput> {
const { Address, TransactionOutputBuilder } = this.lib
return getResult(() => {
const address = Address.from_bech32(recipient.address)
const builder = TransactionOutputBuilder
.new()
.with_address(address)
.next()
const value = this.getCardanoValue(recipient.value)
return builder.with_value(value).build()
})
}

public getMinLovelace(value: Value, hasDataHash: boolean, coinsPerUtxoWord: number): bigint {
const minimum = this.lib.min_ada_required(
this.getCardanoValue(value),
Expand Down Expand Up @@ -173,16 +209,6 @@ class Cardano {
})
}

public buildMultiSigScript(keyHashes: Ed25519KeyHash[], type: MultiSigType, required: number): NativeScript {
const publicKeyScripts = keyHashes.map((keyHash) => this.buildPublicKeyScript(keyHash))

switch (type) {
case 'all': return this.buildAllScript(publicKeyScripts)
case 'any': return this.buildAnyScript(publicKeyScripts)
case 'atLeast': return this.buildAtLeastScript(publicKeyScripts, required)
}
}

public createTxBuilder(protocolParameters: ShelleyProtocolParams): TransactionBuilder {
const { BigNum, TransactionBuilder, TransactionBuilderConfigBuilder, LinearFee } = this.lib
const { minFeeA, minFeeB, poolDeposit, keyDeposit,
Expand Down Expand Up @@ -267,35 +293,6 @@ class Cardano {
return utxosSet
}

private buildPublicKeyScript(keyHash: Ed25519KeyHash): NativeScript {
const { ScriptPubkey, NativeScript } = this.lib
return NativeScript.new_script_pubkey(ScriptPubkey.new(keyHash))
}

private buildAllScript(scripts: NativeScript[]): NativeScript {
const { ScriptAll, NativeScript } = this.lib
return NativeScript.new_script_all(ScriptAll.new(this.buildNativeScripts(scripts)))
}

private buildAnyScript(scripts: NativeScript[]): NativeScript {
const { ScriptAny, NativeScript } = this.lib
return NativeScript.new_script_any(ScriptAny.new(this.buildNativeScripts(scripts)))
}

private buildAtLeastScript(scripts: NativeScript[], required: number): NativeScript {
const { ScriptNOfK, NativeScript } = this.lib
return NativeScript.new_script_n_of_k(ScriptNOfK.new(required, this.buildNativeScripts(scripts)))
}

private buildNativeScripts(scripts: NativeScript[]): NativeScripts {
const { NativeScripts } = this.lib
const nativeScripts = NativeScripts.new()
scripts.forEach((script) => {
nativeScripts.add(script)
})
return nativeScripts
}

private getScriptHashBaseAddress(scriptHash: ScriptHash, networkInfo: NetworkInfo): BaseAddress {
const { BaseAddress, StakeCredential } = this.lib
const networkId = networkInfo.network_id()
Expand Down Expand Up @@ -338,5 +335,5 @@ const useCardanoMultiplatformLib = () => {
return cardano
}

export type { Cardano, CardanoIterable, Result, MultiSigType }
export { encodeCardanoData, getResult, toIter, toHex, useCardanoMultiplatformLib, verifySignature, Loader }
export type { Cardano, CardanoIterable, Result, MultiSigType, Recipient }
export { encodeCardanoData, getResult, toIter, toHex, useCardanoMultiplatformLib, verifySignature, Loader, newRecipient, isAddressNetworkCorrect }
28 changes: 16 additions & 12 deletions src/cardano/query-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import { Config } from './config'

const getPolicyId = (assetId: string) => assetId.slice(0, 56)
const getAssetName = (assetId: string) => assetId.slice(56)
const decodeASCII = (assetName: string): string => {
return Buffer.from(assetName, 'hex').toString('ascii')
}

type Assets = Map<string, bigint>

Expand All @@ -30,7 +33,19 @@ const getBalanceByUTxOs = (utxos: TransactionOutput[]): Value => {
}
}

const createApolloClient = (config: Config) => new ApolloClient({
uri: config.queryAPI.URI,
cache: new InMemoryCache({
typePolicies: {
PaymentAddress: {
keyFields: ['address']
}
}
})
})

type Query<D, V> = (options: QueryHookOptions<D, V>) => QueryResult<D, V>;
type OptionalQuery<D, V> = (options?: QueryHookOptions<D, V>) => QueryResult<D, V>;

const GetUTxOsToSpendQuery = gql`
query getUTxOsToSpend($addresses: [String]!) {
Expand Down Expand Up @@ -110,16 +125,5 @@ function getBalanceByPaymentAddresses(paymentAddresses: PaymentAddress[]): Value
return balance
}

const createApolloClient = (config: Config) => new ApolloClient({
uri: config.queryAPI.URI,
cache: new InMemoryCache({
typePolicies: {
PaymentAddress: {
keyFields: ['address']
}
}
})
})

export type { Value }
export { createApolloClient, getBalanceByUTxOs, getPolicyId, getAssetName, getBalanceByPaymentAddresses, useGetUTxOsToSpendQuery, usePaymentAddressesQuery }
export { createApolloClient, decodeASCII, getBalanceByUTxOs, getPolicyId, getAssetName, getBalanceByPaymentAddresses, useGetUTxOsToSpendQuery, usePaymentAddressesQuery }
26 changes: 26 additions & 0 deletions src/cardano/utils.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { estimateDateBySlot, estimateSlotByDate, getEpochBySlot, getSlotInEpochBySlot } from "./utils"

test('estimateSlotByDate', () => {
expect(estimateSlotByDate(new Date('2022-04-21T22:26:39.000Z'), true)).toBe(59013708)
expect(estimateSlotByDate(new Date('2022-04-21T22:28:04.000Z'), false)).toBe(56210868)
expect(estimateSlotByDate(new Date('2022-04-28T01:56:00.000Z'), false)).toBe(56741744)
})

test('estimateDateBySlot', () => {
expect(estimateDateBySlot(59013708, true).toISOString()).toBe('2022-04-21T22:26:39.000Z')
expect(estimateDateBySlot(56210868, false).toISOString()).toBe('2022-04-21T22:28:04.000Z')
expect(estimateDateBySlot(56741744, false).toISOString()).toBe('2022-04-28T01:56:00.000Z')
})

test('estimateDateBySlot', () => {
expect(getEpochBySlot(59013708, true)).toBe(334)
expect(getEpochBySlot(59016575, true)).toBe(334)
expect(getEpochBySlot(56210868, false)).toBe(200)
expect(getEpochBySlot(56211570, false)).toBe(200)
expect(getEpochBySlot(56213638, false)).toBe(200)
})

test('getSlotInEpochBySlot', () => {
expect(getSlotInEpochBySlot(59016575, true)).toBe(91775)
expect(getSlotInEpochBySlot(56213638, false)).toBe(183238)
})
10 changes: 10 additions & 0 deletions src/cardano/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const SlotLength = 432000
const shelleyStart = (isMainnet: boolean): number => isMainnet ? 4924800 : 4924800 + 129600 - SlotLength
const networkOffset = (isMainnet: boolean): number => isMainnet ? 1596491091 : 1599294016 + 129600 - SlotLength
const estimateDateBySlot = (slot: number, isMainnet: boolean): Date => new Date((slot - shelleyStart(isMainnet) + networkOffset(isMainnet)) * 1000)
const estimateSlotByDate = (date: Date, isMainnet: boolean): number => Math.floor(date.getTime() / 1000) + shelleyStart(isMainnet) - networkOffset(isMainnet)
const slotSinceShelley = (slot: number, isMainnet: boolean): number => slot - shelleyStart(isMainnet)
const getEpochBySlot = (slot: number, isMainnet: boolean) => Math.floor(slotSinceShelley(slot, isMainnet) / SlotLength) + (isMainnet ? 208 : 80) + 1
const getSlotInEpochBySlot = (slot: number, isMainnet: boolean) => slotSinceShelley(slot, isMainnet) % SlotLength

export { estimateDateBySlot, estimateSlotByDate, getEpochBySlot, getSlotInEpochBySlot, SlotLength }
43 changes: 42 additions & 1 deletion src/components/currency.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,45 @@ const ADAAmount: NextPage<{
return <AssetAmount quantity={lovelace} decimals={6} symbol={getADASymbol(config)} className={className} />
}

export { getADASymbol, removeTrailingZero, toDecimal, ADAAmount, AssetAmount, CurrencyInput }
const LabeledCurrencyInput: NextPage<{
symbol: string
decimal: number
value: bigint
min?: bigint
max: bigint
maxButton?: boolean
onChange: (_: bigint) => void
placeholder?: string
}> = (props) => {
const { decimal, value, onChange, min, max, maxButton, symbol, placeholder } = props
const changeHandle = (value: bigint) => {
const min = value > max ? max : value
onChange(min)
}
const isValid = value > 0 && value <= max && (min ? value >= min : true)

return (
<label className='flex grow border rounded overflow-hidden'>
<CurrencyInput
className={['p-2 block w-full outline-none', isValid ? '' : 'text-red-500'].join(' ')}
decimals={decimal}
value={value}
onChange={changeHandle}
placeholder={placeholder} />
<div className='p-2 space-x-1'>
<span>of</span>
<span>{toDecimal(max, decimal)}</span>
<span>{symbol}</span>
</div>
{maxButton &&
<button
onClick={() => onChange(max)}
className='bg-gray-100 border-l py-2 px-4 group text-sky-700'>
Max
</button>
}
</label>
)
}

export { getADASymbol, removeTrailingZero, toDecimal, ADAAmount, AssetAmount, CurrencyInput, LabeledCurrencyInput }
Loading