Skip to content

Commit

Permalink
[xp] handle node minimal fee
Browse files Browse the repository at this point in the history
  • Loading branch information
peterjah committed Apr 23, 2024
1 parent 7fcdbb4 commit dc61066
Show file tree
Hide file tree
Showing 13 changed files with 134 additions and 28 deletions.
4 changes: 4 additions & 0 deletions packages/massa-web3/open_rpc/massa.api.json
Original file line number Diff line number Diff line change
Expand Up @@ -2765,6 +2765,10 @@
"chain_id": {
"description": "Chain id",
"type": "number"
},
"minimal_fees": {
"description": "Minimal fee",
"type": "string"
}
},
"additionalProperties": false
Expand Down
6 changes: 2 additions & 4 deletions packages/massa-web3/src/experimental/accountOperation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,7 @@ export class AccountOperation {
}
const operation = new OperationManager(this.account.privateKey, this.client)
const details: RollOperation = {
// Todo: change with fetchMinimalFees once ready
fee: opts?.fee ?? 0n,
fee: opts?.fee ?? (await this.client.getMinimalFee()),
expirePeriod: await calculateExpirePeriod(
this.client,
opts?.periodToLive
Expand Down Expand Up @@ -97,8 +96,7 @@ export class AccountOperation {

const operation = new OperationManager(this.account.privateKey, this.client)
const details: TransferOperation = {
// Todo: change with fetchMinimalFees once ready
fee: opts?.fee ?? 0n,
fee: opts?.fee ?? (await this.client.getMinimalFee()),
expirePeriod: await calculateExpirePeriod(
this.client,
opts?.periodToLive
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export enum OperationType {
* Period to live is the number of periods the operation is valid for.
* This value must be positive and if it's too big, the node will (sliently?) reject the operation.
*
* If no fee is provided, 0 is used.
* If no fee is provided, minimal fee of connected node is used.
* If no periodToLive is provided, the DefaultPeriodToLive is used.
*/
export type OptOpDetails = {
Expand Down Expand Up @@ -245,8 +245,8 @@ export class OperationManager {
throw new Error('blockchainClient is mandatory to send operations')
}

const networkId = await this.blockchainClient.fetchChainId()
const signature = await this.sign(networkId, operation)
const chainId = await this.blockchainClient.getChainId()

Check warning on line 248 in packages/massa-web3/src/experimental/basicElements/operationManager.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🧾 Statement is not covered

Warning! Not covered statement
const signature = await this.sign(chainId, operation)

Check warning on line 249 in packages/massa-web3/src/experimental/basicElements/operationManager.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🧾 Statement is not covered

Warning! Not covered statement
const data = OperationManager.serialize(operation)
const publicKey = await this.privateKey.getPublicKey()

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { fromMAS } from '@massalabs/web3-utils'
import { fromMAS } from '../utils'

/**
* Calculates the cost of deploying a smart contract.
Expand Down
6 changes: 4 additions & 2 deletions packages/massa-web3/src/experimental/client/client.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { OperationStatus } from '../basicElements'
import { Slot } from '../generated/client'
import { NodeStatus, Slot } from '../generated/client'

export interface SendOperationInput {
data: Uint8Array
Expand All @@ -26,9 +26,11 @@ export interface EventFilter {
*/
export interface BlockchainClient {
sendOperation(data: SendOperationInput): Promise<string>
fetchChainId(): Promise<bigint>
fetchPeriod(): Promise<number>
getOperationStatus(operationId: string): Promise<OperationStatus>
getBalance(address: string, speculative?: boolean): Promise<bigint>
getEvents(filter: EventFilter): Promise<SCOutputEvent[]>
getChainId(): Promise<bigint>
getMinimalFee(): Promise<bigint>
status: NodeStatus
}
9 changes: 8 additions & 1 deletion packages/massa-web3/src/experimental/generated/client.ts

Large diffs are not rendered by default.

18 changes: 13 additions & 5 deletions packages/massa-web3/src/experimental/publicAPI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
OperationId,
AddressFilter,
} from './generated/client'
import { fromMAS } from './utils'

export enum Transport {
webSocket = 'websocket',
Expand Down Expand Up @@ -236,15 +237,22 @@ export class PublicAPI {
}

async getStatus(): Promise<NodeStatus> {
return this.connector.get_status()
this.status = await this.connector.get_status()
return this.status
}

async fetchChainId(): Promise<bigint> {
if (this.status) {
return BigInt(this.status.chain_id)
async getMinimalFee(): Promise<bigint> {
if (!this.status) {
await this.getStatus()
}
return fromMAS(this.status.minimal_fees)
}

return this.getStatus().then((r) => BigInt(r.chain_id))
async getChainId(): Promise<bigint> {
if (!this.status) {
await this.getStatus()
}
return BigInt(this.status.chain_id)
}

async fetchPeriod(): Promise<number> {
Expand Down
4 changes: 2 additions & 2 deletions packages/massa-web3/src/experimental/smartContract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export class ByteCode {
): Promise<Operation> {
const operation = new OperationManager(privateKey, client)
const details: ExecuteOperation = {
fee: opts?.fee ?? 0n,
fee: opts?.fee ?? (await client.getMinimalFee()),
expirePeriod: await calculateExpirePeriod(client, opts?.periodToLive),
type: OperationType.ExecuteSmartContractBytecode,
coins: opts?.coins ?? 0n,
Expand Down Expand Up @@ -200,7 +200,7 @@ export class SmartContract {
account.privateKey,
byteCode,
{
fee: opts?.fee,
fee: opts?.fee ?? (await client.getMinimalFee()),
periodToLive: opts?.periodToLive,
coins: opts?.coins,
maxGas: opts?.maxGas,
Expand Down
1 change: 1 addition & 0 deletions packages/massa-web3/src/experimental/utils/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './numbers'
16 changes: 16 additions & 0 deletions packages/massa-web3/src/experimental/utils/numbers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
export const MAS_DECIMALS = 9

/**
* Convert Mas float amount to 9 decimals bigint
*
* @param amount MAS amount in floating point representation

Check warning on line 6 in packages/massa-web3/src/experimental/utils/numbers.ts

View workflow job for this annotation

GitHub Actions / build

tsdoc-param-tag-missing-hyphen: The @param block should be followed by a parameter name and then a hyphen
* @returns amount in nanoMAS
*/
export const fromMAS = (amount: string | number): bigint => {
if (typeof amount === 'number') {
amount = amount.toString()
}
let [integerPart, decimalPart] = amount.split('.')
decimalPart = decimalPart ?? ''
return BigInt(integerPart + decimalPart.padEnd(MAS_DECIMALS, '0'))
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,34 @@ import { OperationStatus } from '../../../src/experimental/basicElements'
import { JsonRPCClient } from '../../../src/experimental/jsonRPCClient'
import 'dotenv/config'

describe('Basic use cases', () => {
test('AccountOperation - transfer', async () => {
const account = await Account.fromEnv()
describe('AccountOperation tests', () => {
let client: JsonRPCClient
let account: Account
let accountOperation: AccountOperation

const client = new JsonRPCClient('https://buildnet.massa.net/api/v2')
beforeAll(async () => {
client = new JsonRPCClient('https://buildnet.massa.net/api/v2')
account = await Account.fromEnv()
accountOperation = new AccountOperation(account, client)
})

const accountOperation = new AccountOperation(account, client)
test('transfer', async () => {
const transfer = await accountOperation.transfer(
'AU1wN8rn4SkwYSTDF3dHFY4U28KtsqKL1NnEjDZhHnHEy6cEQm53',
1n
)
expect(await transfer.getStatus()).toBe(OperationStatus.NotFound)
})

test('not enough fee', async () => {
expect(
accountOperation.transfer(
'AU1wN8rn4SkwYSTDF3dHFY4U28KtsqKL1NnEjDZhHnHEy6cEQm53',
1n,
{ fee: 1n }
)
).rejects.toThrow(
'Bad request: fee is too low provided: 0.000000001 , minimal_fees required: 0.01'
)
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,18 @@ import { ByteCode } from '../../../src/experimental/smartContract'
import 'dotenv/config'
import { JsonRPCClient } from '../../../src/experimental/jsonRPCClient'

describe('Basic use cases', () => {
test('Smart Contract - execute', async () => {
const account = await Account.fromEnv()
describe('Smart Contract', () => {
let client: JsonRPCClient
let account: Account

const client = new JsonRPCClient('https://buildnet.massa.net/api/v2')
beforeAll(async () => {
client = new JsonRPCClient('https://buildnet.massa.net/api/v2')
account = await Account.fromEnv()
})

test('execute', async () => {
const byteCode = new Uint8Array([1, 2, 3, 4])
const opts = {
fee: 1n,
periodToLive: 2,
coins: 3n,
maxGas: 4n,
Expand All @@ -24,4 +27,20 @@ describe('Basic use cases', () => {
)
expect(await contract.getSpeculativeEvents()).toHaveLength(1)
}, 61000)

test('not enough fee', async () => {
const byteCode = new Uint8Array([1, 2, 3, 4])
const opts = {
fee: 1n,
periodToLive: 2,
coins: 3n,
maxGas: 4n,
}

expect(
ByteCode.execute(client, account.privateKey, byteCode, opts)
).rejects.toThrow(
'Bad request: fee is too low provided: 0.000000001 , minimal_fees required: 0.01'
)
})
})
34 changes: 34 additions & 0 deletions packages/massa-web3/test/experimental/unit/numbers.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { fromMAS } from '../../../src/experimental/utils'

describe('amount format conversion tests', () => {
it('MAS(string) string => nMAS', () => {
let amount = '0'
expect(fromMAS(amount)).toStrictEqual(BigInt(0))

amount = '1.5234'
expect(fromMAS(amount)).toStrictEqual(BigInt('1523400000'))

amount = '0.123456789'
expect(fromMAS(amount)).toStrictEqual(BigInt('123456789'))

amount = '123456789'
expect(fromMAS(amount)).toStrictEqual(BigInt('123456789000000000'))

amount = '123456789'
expect(fromMAS(amount)).toStrictEqual(BigInt('123456789000000000'))
})

it('MAS(number) string => nMAS', () => {
let amount = 0
expect(fromMAS(amount)).toStrictEqual(BigInt(0))

amount = 1.5234
expect(fromMAS(amount)).toStrictEqual(BigInt('1523400000'))

amount = 0.123456789
expect(fromMAS(amount)).toStrictEqual(BigInt('123456789'))

amount = 123456789
expect(fromMAS(amount)).toStrictEqual(BigInt('123456789000000000'))
})
})

0 comments on commit dc61066

Please sign in to comment.