Skip to content

Commit

Permalink
Merge pull request #1896 from aeternity/feature/update-calldata
Browse files Browse the repository at this point in the history
Update aepp-calldata to 1.5.1
  • Loading branch information
davidyuk authored Sep 19, 2023
2 parents 6dcdbf5 + dd4ea8b commit e9cfe81
Show file tree
Hide file tree
Showing 13 changed files with 58 additions and 78 deletions.
4 changes: 1 addition & 3 deletions docs/guides/typed-data.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,8 @@ corresponds to the data

## Implementation

- [encodeFateValue](https://github.com/aeternity/aepp-sdk-js/blob/5952a7b9f4d0cf30ad7caa0831dfb974d1e91afc/src/utils/typed-data.ts#L44-L52) — use to generate the first argument for `signTypedData`;
- [AccountBase::signTypedData](https://github.com/aeternity/aepp-sdk-js/blob/5952a7b9f4d0cf30ad7caa0831dfb974d1e91afc/src/account/Base.ts#L63-L70) — calculates signature, supported in MemoryAccount and in aepp-wallet connection;
- [AccountBase:signTypedData](https://github.com/aeternity/aepp-sdk-js/blob/5952a7b9f4d0cf30ad7caa0831dfb974d1e91afc/src/account/Base.ts#L63-L70) — calculates signature, supported in MemoryAccount and in aepp-wallet connection;
- [hashTypedData](https://github.com/aeternity/aepp-sdk-js/blob/5952a7b9f4d0cf30ad7caa0831dfb974d1e91afc/src/utils/typed-data.ts#L87-L95) — calculates the overall hash of typed data to sign;
- [decodeFateValue](https://github.com/aeternity/aepp-sdk-js/blob/5952a7b9f4d0cf30ad7caa0831dfb974d1e91afc/src/utils/typed-data.ts#L55-L63) — use to preview data to sign on wallet side;
- [hashJson](https://github.com/aeternity/aepp-sdk-js/blob/5952a7b9f4d0cf30ad7caa0831dfb974d1e91afc/src/utils/typed-data.ts#L16-L18) — deterministic hashing of an arbitrary JS value, used to calculate `hash(aci)`;
- [hashDomain](https://github.com/aeternity/aepp-sdk-js/blob/5952a7b9f4d0cf30ad7caa0831dfb974d1e91afc/src/utils/typed-data.ts#L68-L85) — use for debugging or to prepare the hash value for smart contract.

Expand Down
1 change: 1 addition & 0 deletions examples/browser/aepp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"build": "vue-cli-service build"
},
"dependencies": {
"@aeternity/aepp-calldata": "^1.5.1",
"@aeternity/aepp-sdk": "file:../../..",
"buffer": "^6.0.3",
"core-js": "^3.32.1",
Expand Down
8 changes: 4 additions & 4 deletions examples/browser/aepp/src/TypedData.vue
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,8 @@

<script>
import { mapState } from 'vuex';
import {
hashTypedData, encodeFateValue, verify, decode,
} from '@aeternity/aepp-sdk';
import { hashTypedData, verify, decode } from '@aeternity/aepp-sdk';
import { TypeResolver, ContractByteArrayEncoder } from '@aeternity/aepp-calldata';
import Value from './components/Value.vue';
import FieldAction from './components/FieldAction.vue';
Expand Down Expand Up @@ -145,7 +144,8 @@ export default {
return JSON.parse(this.aci);
},
dataEncoded() {
return encodeFateValue(this.dataParsed, this.aciParsed);
const dataType = new TypeResolver().resolveType(this.aciParsed);
return new ContractByteArrayEncoder().encodeWithType(this.dataParsed, dataType);
},
hash() {
return hashTypedData(this.dataEncoded, this.aciParsed, this.domain);
Expand Down
1 change: 1 addition & 0 deletions examples/browser/wallet-iframe/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"build": "vue-cli-service build"
},
"dependencies": {
"@aeternity/aepp-calldata": "^1.5.1",
"@aeternity/aepp-sdk": "file:../../..",
"buffer": "^6.0.3",
"core-js": "^3.32.1",
Expand Down
7 changes: 5 additions & 2 deletions examples/browser/wallet-iframe/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,9 @@
import {
MemoryAccount, generateKeyPair, AeSdkWallet, Node, CompilerHttp,
BrowserWindowMessageConnection, METHODS, WALLET_TYPE, RPC_STATUS,
RpcConnectionDenyError, RpcRejectedByUserError, unpackTx, decodeFateValue,
RpcConnectionDenyError, RpcRejectedByUserError, unpackTx,
} from '@aeternity/aepp-sdk';
import { TypeResolver, ContractByteArrayEncoder } from '@aeternity/aepp-calldata';
import Value from './Value.vue';
export default {
Expand Down Expand Up @@ -173,7 +174,9 @@ export default {
async signTypedData(data, aci, { aeppRpcClientId: id, aeppOrigin, ...options }) {
if (id != null) {
const opt = { ...options, aci, decodedData: decodeFateValue(data, aci) };
const dataType = new TypeResolver().resolveType(aci);
const decodedData = new ContractByteArrayEncoder().decodeWithType(data, dataType);
const opt = { ...options, aci, decodedData };
genConfirmCallback(`sign typed data ${data}`)(id, opt, aeppOrigin);
}
return super.signTypedData(data, aci, options);
Expand Down
1 change: 1 addition & 0 deletions examples/browser/wallet-web-extension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"build": "vue-cli-service build"
},
"dependencies": {
"@aeternity/aepp-calldata": "^1.5.1",
"@aeternity/aepp-sdk": "file:../../..",
"core-js": "^3.32.1",
"tailwindcss": "^2.2.19",
Expand Down
7 changes: 5 additions & 2 deletions examples/browser/wallet-web-extension/src/background.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import browser from 'webextension-polyfill';
import {
AeSdkWallet, CompilerHttp, Node, MemoryAccount, generateKeyPair, BrowserRuntimeConnection,
WALLET_TYPE, RpcConnectionDenyError, RpcRejectedByUserError, unpackTx, decodeFateValue,
WALLET_TYPE, RpcConnectionDenyError, RpcRejectedByUserError, unpackTx,
} from '@aeternity/aepp-sdk';
import { TypeResolver, ContractByteArrayEncoder } from '@aeternity/aepp-calldata';

function stringifyBigint(value) {
return JSON.stringify(
Expand Down Expand Up @@ -67,8 +68,10 @@ class AccountMemoryProtected extends MemoryAccount {

async signTypedData(data, aci, { aeppRpcClientId: id, aeppOrigin, ...options }) {
if (id != null) {
const dataType = new TypeResolver().resolveType(aci);
const decodedData = new ContractByteArrayEncoder().decodeWithType(data, dataType);
const opt = {
...options, aci, data, decodedData: decodeFateValue(data, aci),
...options, aci, data, decodedData,
};
await genConfirmCallback('sign typed data')(id, opt, aeppOrigin);
}
Expand Down
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@
"SDK"
],
"dependencies": {
"@aeternity/aepp-calldata": "^1.4.0",
"@aeternity/aepp-calldata": "^1.5.1",
"@aeternity/argon2": "^0.0.1",
"@aeternity/uuid": "^0.0.1",
"@azure/core-client": "1.6.0",
Expand Down
19 changes: 19 additions & 0 deletions src/deprecated.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import { TypeResolver, ContractByteArrayEncoder } from '@aeternity/aepp-calldata';
import { AciValue } from './utils/typed-data';
import { Encoded } from './utils/encoder';
import { RpcError } from './aepp-wallet-communication/schema';

/**
Expand All @@ -21,3 +24,19 @@ export class RpcBroadcastError extends RpcError {
* @deprecated use isAuctionName instead
*/
export const NAME_BID_MAX_LENGTH = 12; // # this is the max length for a domain to be part of a bid

/**
* @deprecated use ContractByteArrayEncoder:encodeWithType from \@aeternity/aepp-calldata
*/
export function encodeFateValue(value: unknown, aci: AciValue): Encoded.ContractBytearray {
const valueType = new TypeResolver().resolveType(aci, {});
return new ContractByteArrayEncoder().encodeWithType(value, valueType);
}

/**
* @deprecated use ContractByteArrayEncoder:decodeWithType from \@aeternity/aepp-calldata
*/
export function decodeFateValue(value: Encoded.ContractBytearray, aci: AciValue): any {
const valueType = new TypeResolver().resolveType(aci, {});
return new ContractByteArrayEncoder().decodeWithType(value, valueType);
}
8 changes: 4 additions & 4 deletions src/index-browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,7 @@ export {
export {
encode, decode, Encoding, Encoded,
} from './utils/encoder';
export {
encodeFateValue, decodeFateValue, hashTypedData, hashDomain, hashJson,
} from './utils/typed-data';
export { hashTypedData, hashDomain, hashJson } from './utils/typed-data';
export {
aensRevoke, aensUpdate, aensTransfer, aensQuery, aensClaim, aensPreclaim, aensBid,
} from './aens';
Expand Down Expand Up @@ -111,4 +109,6 @@ export {
TagNotFoundError, TxNotInChainError, AlreadyConnectedError, NoWalletConnectedError,
RpcConnectionError,
} from './utils/errors';
export { RpcBroadcastError, NAME_BID_MAX_LENGTH } from './deprecated';
export {
RpcBroadcastError, NAME_BID_MAX_LENGTH, encodeFateValue, decodeFateValue,
} from './deprecated';
34 changes: 4 additions & 30 deletions src/utils/typed-data.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,4 @@
// js extension is required for mjs build
// @ts-expect-error see https://github.com/aeternity/aepp-calldata-js/issues/216
// eslint-disable-next-line import/extensions
import ContractByteArrayEncoder from '@aeternity/aepp-calldata/src/ContractByteArrayEncoder.js';
// @ts-expect-error see https://github.com/aeternity/aepp-calldata-js/issues/216
// eslint-disable-next-line import/extensions
import AciTypeResolver from '@aeternity/aepp-calldata/src/AciTypeResolver.js';
import { TypeResolver, ContractByteArrayEncoder } from '@aeternity/aepp-calldata';
import canonicalize from 'canonicalize';
import { Encoded, decode } from './encoder';
import { hash, messagePrefixLength } from './crypto';
Expand Down Expand Up @@ -40,28 +34,6 @@ export interface Domain {
contractAddress?: Encoded.ContractAddress;
}

// TODO: replace with api on calldata side https://github.com/aeternity/aepp-calldata-js/issues/216
export function encodeFateValue(
value: unknown,
aci: AciValue,
): Encoded.ContractBytearray {
const contractByteArrayEncoder = new ContractByteArrayEncoder();
const aciTypeResolver = new AciTypeResolver([]);
aciTypeResolver.isCustomType = () => false;
return contractByteArrayEncoder.encode(aciTypeResolver.resolveType(aci), value);
}

// TODO: replace with api on calldata side https://github.com/aeternity/aepp-calldata-js/issues/216
export function decodeFateValue(
value: Encoded.ContractBytearray,
aci: AciValue,
): Encoded.ContractBytearray {
const contractByteArrayEncoder = new ContractByteArrayEncoder();
const aciTypeResolver = new AciTypeResolver([]);
aciTypeResolver.isCustomType = () => false;
return contractByteArrayEncoder.decodeWithType(value, aciTypeResolver.resolveType(aci));
}

/**
* Hashes domain object, can be used to inline domain hash to contract source code
*/
Expand All @@ -81,7 +53,9 @@ export function hashDomain(domain: Domain): Buffer {
type: { option: ['contract_pubkey'] },
}],
} as const;
return hash(decode(encodeFateValue(domain, domainAci)));
const domainType = new TypeResolver().resolveType(domainAci, {});
const fateValue = new ContractByteArrayEncoder().encodeWithType(domain, domainType);
return hash(decode(fateValue));
}

export function hashTypedData(
Expand Down
36 changes: 8 additions & 28 deletions test/integration/typed-data.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { describe, it } from 'mocha';
import { expect } from 'chai';
import canonicalize from 'canonicalize';
import { TypeResolver, ContractByteArrayEncoder } from '@aeternity/aepp-calldata';
import {
AeSdk, Contract, decode, Encoded, encodeFateValue, decodeFateValue,
AeSdk, Contract, decode, Encoded,
hashDomain, hashJson, hashTypedData,
} from '../../src';
import { Domain } from '../../src/utils/typed-data';
Expand All @@ -20,7 +21,6 @@ describe('typed data', () => {
});

const plainAci = 'int';
const plainDataDecoded = 42n;
const plainData = 'cb_VNLOFXc=';

const recordAci = {
Expand All @@ -29,32 +29,8 @@ describe('typed data', () => {
{ name: 'parameter', type: 'int' },
],
} as const;
const recordDataDecoded = {
operation: 'test',
parameter: 42n,
};
const recordData = 'cb_KxF0ZXN0VANAuWU=';

describe('encodeFateValue', () => {
it('encodes plain value', () => {
expect(encodeFateValue(plainDataDecoded, plainAci)).to.be.equal(plainData);
});

it('encodes record value', () => {
expect(encodeFateValue(recordDataDecoded, recordAci)).to.be.equal(recordData);
});
});

describe('decodeFateValue', () => {
it('decodes plain value', () => {
expect(decodeFateValue(plainData, plainAci)).to.be.eql(plainDataDecoded);
});

it('decodes record value', () => {
expect(decodeFateValue(recordData, recordAci)).to.be.eql(recordDataDecoded);
});
});

const domain: Domain = {
name: 'Test app',
version: 2,
Expand Down Expand Up @@ -140,14 +116,18 @@ describe('typed data', () => {
expect(domainHash).to.be.eql(hashDomain(domain));
});

const recordType = new TypeResolver().resolveType(recordAci, {});

it('calculates typed data hash', async () => {
const data = encodeFateValue({ operation: 'test', parameter: 43 }, recordAci);
const data = new ContractByteArrayEncoder()
.encodeWithType({ operation: 'test', parameter: 43 }, recordType);
const typedDataHash = Buffer.from((await contract.hashTypedData(43)).decodedResult);
expect(typedDataHash).to.be.eql(hashTypedData(data, recordAci, domain));
});

it('verifies signature', async () => {
const data = encodeFateValue({ operation: 'test', parameter: 45 }, recordAci);
const data = new ContractByteArrayEncoder()
.encodeWithType({ operation: 'test', parameter: 45 }, recordType);
const signature = await aeSdk.signTypedData(data, recordAci, domain);
const signatureDecoded = decode(signature);

Expand Down

0 comments on commit e9cfe81

Please sign in to comment.