/
generateReceivedAssetKeys.ts
109 lines (91 loc) · 4.27 KB
/
generateReceivedAssetKeys.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
import { BytesLike, concat, isHexString, toBeHex, toNumber } from 'ethers';
// `@luksp/lsp-smart-contracts` constants
import { ERC725YDataKeys } from '@lukso/lsp-smart-contracts';
// LSP2 utils
import { isValidArrayLengthValue } from '../../LSP2ERC725YJSONSchema/isValidArrayLengthValue';
import { generateMappingKey } from '../../LSP2ERC725YJSONSchema/generateMappingKey';
import { generateArrayElementKeyAtIndex } from '../../LSP2ERC725YJSONSchema/generateArrayElementKeyAtIndex';
// typechain
import { UniversalProfile } from '../../typechain/lukso';
/**
* Generate an array of Data Key/Value pairs to be set on the receiver address after receiving assets.
*
* @since v0.0.1
* @category LSP5
*
* @param erc725YContract The contract instance of the asset reciever.
* @param assetAddress The address of the asset being received (_e.g: an LSP7 or LSP8 token_).
* @param assetInterfaceId The interfaceID of the asset being received.
*
* @return A set of LSP5 data keys & data values that can be used to update an array and map in ERC725Y storage.
*
* @see https://github.com/lukso-network/LIPs/blob/main/LSPs/LSP-5-ReceivedAssets.md
* @example
* ```ts
* generateReceivedAssetKeys(...) => { lsp5DataKeys: BytesLike[], lsp5DataValues: BytesLike[] }
* ```
*/
export const generateReceivedAssetKeys = async (
erc725YContract: UniversalProfile,
assetAddress: BytesLike,
assetInterfaceId: BytesLike,
) => {
if (isHexString(assetAddress, 20)) {
throw new Error(`'assetAddress' bytes length is not 20. Value: ${assetAddress}`);
}
if (isHexString(assetInterfaceId, 4)) {
throw new Error(`'assetInterfaceId' bytes length is not 4. Value: ${assetInterfaceId}`);
}
// --- `LSP5ReceivedAssets[]` Array ---
let currentArrayLengthBytes = await erc725YContract.getData(
ERC725YDataKeys.LSP5['LSP5ReceivedAssets[]'].length,
);
// CHECK that the value of `LSP5ReceivedAssets[]` Array length is a valid `uint128` (16 bytes long)
if (!isValidArrayLengthValue(currentArrayLengthBytes)) {
if (currentArrayLengthBytes === '0x' || currentArrayLengthBytes === '') {
// if it's the first asset received and nothing is set (= 0x)
// we need to convert it to: `0x00000000000000000000000000000000`
// to safely cast to a uint128 of length 0
currentArrayLengthBytes = toBeHex(0, 16);
} else {
// otherwise the array length is invalid
throw new Error(
`'LSP5ReceivedAssets[]' length invalid. Value: ${currentArrayLengthBytes}`,
);
}
}
// CHECK for potential overflow
if (currentArrayLengthBytes === `0x${'ff'.repeat(16)}`) {
throw new Error(
`'LSP5ReceivedAssets[]' length reached max value. Value: ${currentArrayLengthBytes}`,
);
}
const currentArrayLength = toNumber(currentArrayLengthBytes);
// --- `LSP5ReceivedAssetsMap:<assetAddress>` ---
const mapDataKey = generateMappingKey(
ERC725YDataKeys.LSP5.LSP5ReceivedAssetsMap,
assetAddress.toString(),
);
// CHECK that the map value is not already set in the storage for the newly received asset
// If that's the case, the asset is already registered. Do not try to update.
const fetchedMapValue = await erc725YContract.getData(mapDataKey);
if (!(fetchedMapValue === '0x') && !(fetchedMapValue === '')) {
throw new Error(`Asset already registred. Asset address: ${assetAddress}`);
}
// --- LSP5 Data Keys & Values ---
const lsp5DataKeys: BytesLike[] = [];
const lsp5DataValues: BytesLike[] = [];
// Increment `LSP5ReceivedAssets[]` Array length
lsp5DataKeys[0] = ERC725YDataKeys.LSP5['LSP5ReceivedAssets[]'].length;
lsp5DataValues[0] = toBeHex(currentArrayLength + 1, 16);
// Add asset address to `LSP5ReceivedAssets[index]`, where index == previous array length
lsp5DataKeys[1] = generateArrayElementKeyAtIndex(
ERC725YDataKeys.LSP5['LSP5ReceivedAssets[]'].length,
currentArrayLength,
);
lsp5DataValues[1] = assetAddress;
// Add interfaceId + index as value under `LSP5ReceivedAssetsMap:<assetAddress>`
lsp5DataKeys[2] = mapDataKey;
lsp5DataValues[2] = concat([assetInterfaceId, currentArrayLengthBytes]);
return { lsp5DataKeys, lsp5DataValues };
};