Skip to content

Commit

Permalink
Merge pull request #85 from ar-io/PE-6067-token-classes
Browse files Browse the repository at this point in the history
  • Loading branch information
dtfiedler authored May 15, 2024
2 parents 4a33b39 + 3fb0bea commit 3ebef66
Show file tree
Hide file tree
Showing 5 changed files with 265 additions and 20 deletions.
46 changes: 26 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ const gateways = await arIO.getGateways();
"QGWqtJdLLgm2ehFWiiPzMaoFLD50CnGuzZIPEdoDRGQ": {
"end": 0,
"observerWallet": "IPdwa3Mb_9pDD8c2IaJx6aad51Ss-_TfStVwBuhtXMs",
"operatorStake": 250000,
"operatorStake": 250000000000, // value in mIO
"settings": {
"fqdn": "ar-io.dev",
"label": "AR.IO Test",
Expand Down Expand Up @@ -205,11 +205,12 @@ const arweave = Arweave.init({
port: 443,
protocol: 'https'
})
// for browser environments
const browserSigner = new ArConnectSigner(window.arweaveWallet, arweave);
const arIOWriteable = ArIO.init({ signer: browserSigner});

// for node environments
const nodeSigner = new ArweaveSigner(JWK);
// read and write client that has access to all APIs
const arIOWriteable = ArIO.init({ signer: nodeSigner});

```
Expand Down Expand Up @@ -318,23 +319,27 @@ Retrieves the balance of the specified wallet address.

```typescript
const arIO = ArIO.init();
// the balance will be returned in mIO as a value
const balance = await arIO.getBalance({
address: 'INSERT_WALLET_ADDRESS',
});
// convert to IO token using helper class
const balanceIO = new mIO(balance).toIO();
```

<details>
<summary>Output</summary>

```json
1000000
// value in mIO
1000000000000
```

</details>

#### `getBalances({ evaluationOptions })`

Retrieves the balances of the ArIO contract.
Retrieves the balances of the ArIO contract in `mIO`

<!--
// ALM - A part of me wonders whether streaming JSON might be beneficial in the future
Expand All @@ -352,9 +357,9 @@ const balances = await arIO.getBalances();

```json
{
"-4xgjroXENKYhTWqrBo57HQwvDL51mMvSxJy6Y2Z_sA": 5000,
"-7vXsQZQDk8TMDlpiSLy3CnLi5PDPlAaN2DaynORpck": 5000,
"-9JU3W8g9nOAB1OrJQ8FxkaWCpv5slBET2HppTItbmk": 5000
"-4xgjroXENKYhTWqrBo57HQwvDL51mMvSxJy6Y2Z_sA": 5000000000, // value in mIO
"-7vXsQZQDk8TMDlpiSLy3CnLi5PDPlAaN2DaynORpck": 5000000000, // value in mIO
"-9JU3W8g9nOAB1OrJQ8FxkaWCpv5slBET2HppTItbmk": 5000000000 // value in mIO
}
```

Expand All @@ -378,7 +383,7 @@ const gateway = await arIO.getGateway({
{
"end": 0,
"observerWallet": "IPdwa3Mb_9pDD8c2IaJx6aad51Ss-_TfStVwBuhtXMs",
"operatorStake": 250000,
"operatorStake": 250000000000, // value in mIO
"settings": {
"fqdn": "ar-io.dev",
"label": "AR.IO Test",
Expand Down Expand Up @@ -427,7 +432,7 @@ const gateways = await arIO.getGateways();
"QGWqtJdLLgm2ehFWiiPzMaoFLD50CnGuzZIPEdoDRGQ": {
"end": 0,
"observerWallet": "IPdwa3Mb_9pDD8c2IaJx6aad51Ss-_TfStVwBuhtXMs",
"operatorStake": 250000,
"operatorStake": 250000000000, // value in mIO
"settings": {
"fqdn": "ar-io.dev",
"label": "AR.IO Test",
Expand Down Expand Up @@ -507,7 +512,7 @@ const records = await arIO.getArNSRecords();
},
"ar-io": {
"contractTxId": "eNey-H9RB9uCdoJUvPULb35qhZVXZcEXv8xds4aHhkQ",
"purchasePrice": 17386.717520731843,
"purchasePrice": 75541282285, // value in mIO
"startTimestamp": 1706747215,
"type": "permabuy",
"undernames": 10
Expand Down Expand Up @@ -640,7 +645,7 @@ const observers = await arIO.getPrescribedObservers();
{
"gatewayAddress": "BpQlyhREz4lNGS-y3rSS1WxADfxPpAuing9Lgfdrj2U",
"observerAddress": "2Fk8lCmDegPg6jjprl57-UCpKmNgYiKwyhkU4vMNDnE",
"stake": 10000,
"stake": 10000000000, // value in mIO
"start": 1296976,
"stakeWeight": 1,
"tenureWeight": 0.41453703703703704,
Expand Down Expand Up @@ -673,7 +678,7 @@ const previousEpochObservers = await arIO.getPrescribedObservers({
{
"gatewayAddress": "2Ic0ZIpt85tjiVRaD_qoTSo9jgT7w0rbf4puSTRidcU",
"observerAddress": "2Ic0ZIpt85tjiVRaD_qoTSo9jgT7w0rbf4puSTRidcU",
"stake": 10000,
"stake": 10000000000, // vault in mIO
"start": 1292450,
"stakeWeight": 1,
"tenureWeight": 0.4494598765432099,
Expand Down Expand Up @@ -768,10 +773,10 @@ Joins a gateway to the ar.io network via its associated wallet. Requires `signer

```typescript
const jointNetworkParams = {
qty: 10_000, // minimum operator stake allowed
qty: new IOToken(10_000).valueOf(), // minimum operator stake allowed
autoStake: true, // auto-stake operator rewards to the gateway
allowDelegatedStaking: true, // allows delegated staking
minDelegatedStake: 100, // minimum delegated stake allowed (in mIO)
minDelegatedStake: new IOToken(100).valueOf(), // minimum delegated stake allowed (in mIO)
delegateRewardShareRatio: 10, // percentage of rewards to share with delegates (e.g. 10%)
label: 'john smith', // min 1, max 64 characters
note: 'The example gateway', // max 256 characters
Expand All @@ -793,7 +798,7 @@ Writes new gateway settings to the callers gateway configuration. Requires `sign

```typescript
const updateGatewaySettingsParams = {
minDelegatedStake: 100,
minDelegatedStake: new IOToken(100).valueOf(),
};

const signer = new ArweaveSigner(jwk);
Expand All @@ -811,7 +816,7 @@ Increases the callers stake on the target gateway. Requires `signer` to be provi
```typescript
const params = {
target: 't4Xr0_J4Iurt7caNST02cMotaz2FIbWQ4Kbj616RHl3',
qty: 100,
qty: new IOToken(100).valueOf(),
};

const signer = new ArweaveSigner(jwk);
Expand All @@ -827,7 +832,7 @@ Decreases the callers stake on the target gateway. Requires `signer` to be provi
```typescript
const params = {
target: 't4Xr0_J4Iurt7caNST02cMotaz2FIbWQ4Kbj616RHl3',
qty: 100,
qty: new IOToken(100).valueOf(),
};

const signer = new ArweaveSigner(jwk);
Expand All @@ -842,7 +847,7 @@ Increases the callers operator stake. Must be executed with a wallet registered

```typescript
const params = {
qty: 100,
qty: new IOToken(100).valueOf(),
};

const signer = new ArweaveSigner(jwk);
Expand All @@ -857,7 +862,7 @@ Decreases the callers operator stake. Must be executed with a wallet registered

```typescript
const params = {
qty: 100,
qty: new IOToken(100).valueOf(),
};

const signer = new ArweaveSigner(jwk);
Expand Down Expand Up @@ -891,7 +896,8 @@ Transfers `IO` or `mIO` depending on the `denomination` selected, defaulting as
const authenticatedArIO = ArIO.init({ signer });
const { id: txId } = await authenticatedArIO.transfer({
target: '-5dV7nk7waR8v4STuwPnTck1zFVkQqJh5K9q9Zik4Y5',
qty: 1000,
qty: new IOToken(1000).valueOf(),
denomination: 'IO',
});
```

Expand Down
2 changes: 2 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,5 @@ export const ARNS_TESTNET_REGISTRY_TX =

export const ARNS_DEVNET_REGISTRY_TX =
'_NctcA2sRy1-J4OmIQZbYFPM17piNcbdBPH2ncX2RL8';

export const MIO_PER_IO = 1_000_000;
151 changes: 151 additions & 0 deletions src/token.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
/**
* Copyright (C) 2022-2024 Permanent Data Solutions, Inc. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { MIO_PER_IO } from './constants.js';

interface Equatable<T> {
equals(other: T): boolean;
}

class PositiveFiniteInteger implements Equatable<PositiveFiniteInteger> {
constructor(private readonly positiveFiniteInteger: number) {
if (
!Number.isFinite(this.positiveFiniteInteger) ||
!Number.isInteger(this.positiveFiniteInteger) ||
this.positiveFiniteInteger < 0
) {
throw new Error(
`Number must be a non-negative integer value! ${positiveFiniteInteger}`,
);
}
}

[Symbol.toPrimitive](hint?: string): number | string {
if (hint === 'string') {
this.toString();
}

return this.positiveFiniteInteger;
}

plus(positiveFiniteInteger: PositiveFiniteInteger): PositiveFiniteInteger {
return new PositiveFiniteInteger(
this.positiveFiniteInteger + positiveFiniteInteger.positiveFiniteInteger,
);
}

minus(positiveFiniteInteger: PositiveFiniteInteger): PositiveFiniteInteger {
return new PositiveFiniteInteger(
this.positiveFiniteInteger - positiveFiniteInteger.positiveFiniteInteger,
);
}

isGreaterThan(positiveFiniteInteger: PositiveFiniteInteger): boolean {
return (
this.positiveFiniteInteger > positiveFiniteInteger.positiveFiniteInteger
);
}

isGreaterThanOrEqualTo(
positiveFiniteInteger: PositiveFiniteInteger,
): boolean {
return (
this.positiveFiniteInteger >= positiveFiniteInteger.positiveFiniteInteger
);
}

isLessThan(positiveFiniteInteger: PositiveFiniteInteger): boolean {
return (
this.positiveFiniteInteger < positiveFiniteInteger.positiveFiniteInteger
);
}

isLessThanOrEqualTo(positiveFiniteInteger: PositiveFiniteInteger): boolean {
return (
this.positiveFiniteInteger <= positiveFiniteInteger.positiveFiniteInteger
);
}

toString(): string {
return `${this.positiveFiniteInteger}`;
}

valueOf(): number {
return this.positiveFiniteInteger;
}

toJSON(): number {
return this.positiveFiniteInteger;
}

equals(other: PositiveFiniteInteger): boolean {
return this.positiveFiniteInteger === other.positiveFiniteInteger;
}
}

export class IOToken {
protected value: number;
constructor(value: number) {
if (!Number.isFinite(value) || value < 0) {
throw new Error('IOToken must be a non-negative finite number');
}
this.value = +value.toFixed(6);
}

valueOf(): number {
return this.value;
}

toMIO(): mIOToken {
return new mIOToken(Math.floor(this.value * MIO_PER_IO));
}
}

export class mIOToken extends PositiveFiniteInteger {
constructor(value: number) {
super(value);
}

multiply(multiplier: mIOToken | number): mIOToken {
// always round down on multiplication and division
const result = Math.floor(this.valueOf() * multiplier.valueOf());
return new mIOToken(result);
}

divide(divisor: mIOToken | number): mIOToken {
if (divisor.valueOf() === 0) {
// TODO: how should we handle this
throw new Error('Cannot divide by zero');
}
// always round down on multiplication and division
const result = Math.floor(this.valueOf() / divisor.valueOf());
return new mIOToken(result);
}

plus(addend: mIOToken): mIOToken {
const result = super.plus(addend);
return new mIOToken(result.valueOf());
}

minus(subtractHend: mIOToken): mIOToken {
const result = super.minus(subtractHend);
return new mIOToken(result.valueOf());
}

toIO(): IOToken {
return new IOToken(this.valueOf() / MIO_PER_IO);
}
}
1 change: 1 addition & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@
export * from './arns-service.js';
export * from './contract-state.js';
export * from './common.js';
export * from './token.js';
Loading

0 comments on commit 3ebef66

Please sign in to comment.