Web3 SDK
Installation
Connexion à Alephium
NodeProvider
est une abstraction d'une connexion au réseau Alephium, vous pouvez obtenir un NodeProvider
en:
Ou spécifiez la API_KEY
si vous avez alephium.api.api-key
dans votre fichier de configuration du nœud complet :
const API_KEY = // alephium.api.api-key à partir de votre configuration de nœud complet
const nodeProvider = new NodeProvider('http://localhost:22973', API_KEY)
Parfois, il est pratique de configurer un NodeProvider
global pour votre projet:
Interrogation de la Blockchain
Une fois que vous avez un NodeProvider
, vous avez une connexion à la blockchain, que vous pouvez utiliser pour interroger l'état actuel du contrat, récupérer les événements historiques du contrat, rechercher les contrats déployés, etc.
// Obtenez la hauteur de la blockchain à partir de l'index de la chaîne donnée
await nodeProvider.blockflow.getBlockflowChainInfo({
fromGroup: 0,
toGroup: 0
})
// { currentHeight: 315 }
// Obtenez le bloc à partir du hachage de bloc donné
await nodeProvider.blockflow.getBlockflowBlocksBlockHash('1ccfe845988ebf878384dd2dc9e55920261566c2ad9143963180222059ffd3b0')
// {
// hash: '1ccfe845988ebf878384dd2dc9e55920261566c2ad9143963180222059ffd3b0',
// timestamp: 1665207807876,
// chainFrom: 0,
// chainTo: 0,
// height: 315,
// deps: [
// '5e9209fbf2b4c656b136933684b8606382575f16795ddc7a6317c5d4b7e378c5',
// 'b5d69f03d4d0eea5a4c7e0bc0e2e8c5a8f99306050d641b599991b441ea0f9ea',
// 'f344b3dd45f39d62cfd200cfa3312c080018102908787c5565b8c8af3647368f',
// 'fa359ce0b5accb1584cd280ae3c32f210424a9fd32cb736b6cdfe64882651010',
// '8bc7d94ddfc66129049862e501ff5d5042e1e978b1a51d28e238321444a91071',
// '5896ee3cbc8c4f4432a0b221039113061b6cf7eeb794b25758d247120d0df712',
// 'fc312b0c04a0eeb0767ec98137b740047d7c5e8f64463531828b7015cbeaeaa3'
// ],
// transactions: [
// {
// unsigned: [Object],
// scriptExecutionOk: true,
// contractInputs: [],
// generatedOutputs: [],
// inputSignatures: [],
// scriptSignatures: []
// }
// ],
// nonce: '5da513de7183d7eb1a90b46c4ffe6f7ae4fdf012f0018f41',
// version: 0,
// depStateHash: '2edccc3e717d38a6564fb970f6714f0c00c074226b2cb1ee6272781d3c9bc870',
// txsHash: '738e879791e0c164a5a12b658e2b37e65ed9c415c2e152656c8469273f775f5a',
// target: '20ffffff'
// }
// Obtenez le statut de la transaction
await nodeProvider.transactions.getTransactionsStatus({
txId: 'f33da0d8f4c00d68e2d5818cb9617219a1108b801f387fc8d1595287e4dbf2aa'
})
// {
// type: 'Confirmed',
// blockHash: '47c95e02a7d7ca442ee1849d76a5987c7b72ddcad0b05e9f01df4bc4878f5980',
// txIndex: 0,
// chainConfirmations: 295,
// fromGroupConfirmations: 295,
// toGroupConfirmations: 295
// }
// Obtenez le solde du compte
await nodeProvider.addresses.getAddressesAddressBalance('1DrDyTr9RpRsQnDnXo2YRiPzPW4ooHX5LLoqXrqfMrpQH')
// {
// balance: '999972904436900000000000',
// balanceHint: '999972.9044369 ALPH',
// lockedBalance: '0',
// lockedBalanceHint: '0 ALPH',
// tokenBalances: [
// {
// id: 'ee682f8182f56b55a36a7f916a901685752e00d0e4b90de073e6658156ae82a9',
// amount: '10000000000000000000'
// }
// ],
// utxoNum: 1
// }
Écriture dans la blockchain
Les transactions sont utilisées pour changer l'état de la blockchain. Chaque transaction doit être signée avec une clé privée, ce qui peut être fait via le SignerProvider
. Et il existe deux SignerProvider
dans alephium/web3-wallet
.
Installation de Web3 Wallet
Note
Les deux portefeuilles sont utilisés pour le développement et le déploiement de contrats, veuillez ne pas les utiliser pour stocker de grandes quantités de jetons.
Portefeuille Node
Veuillez suivre le guide pour créer un portefeuille de nœud complet.
// Créez un portefeuille de nœud en utilisant le nom du portefeuille
const nodeWallet = new NodeWallet('alephium-web3-test-only-wallet', nodeProvider)
// Déverrouillez le portefeuille avec le mot de passe
await nodeWallet.unlock('alph')
// Obtenez les comptes
await nodeWallet.getAccounts()
// [
// {
// publicKey: '0381818e63bd9e35a5489b52a430accefc608fd60aa2c7c0d1b393b5239aedf6b0',
// address: '1DrDyTr9RpRsQnDnXo2YRiPzPW4ooHX5LLoqXrqfMrpQH',
// group: 0
// }
// ]
// Transférer 1 ALPH à 15Z54erRksUHb7qxegcKN5DePMv96tXdc1jW26fW3REwT
await nodeWallet.signAndSubmitTransferTx({
signerAddress: '1DrDyTr9RpRsQnDnXo2YRiPzPW4ooHX5LLoqXrqfMrpQH',
destinations: [{
address: '15Z54erRksUHb7qxegcKN5DePMv96tXdc1jW26fW3REwT',
attoAlphAmount: 10n ** 18n,
}]
})
// {
// txId: '7d11b0e88f0158cdd94fcf4e7af04a76be1101293a50050523f72422a9269f36',
// fromGroup: 0,
// toGroup: 0
// }
// Verrouillez le portefeuille
await nodeWallet.lock()
PrivateKeyWallet
// Créez un portefeuille PrivateKeyWallet à partir de la clé privée
const wallet = new PrivateKeyWallet({privateKey: 'a642942e67258589cd2b1822c631506632db5a12aabcf413604e785300d762a5', keyType: undefined, nodeProvider: web3.getCurrentNodeProvider()})
// Créez un portefeuille PrivateKeyWallet à partir de la mnémonique et du groupe, ici il créera un compte sur le groupe 0
const wallet = PrivateKeyWallet.FromMnemonicWithGroup(
'vault alarm sad mass witness property virus style good flower rice alpha viable evidence run glare pretty scout evil judge enroll refuse another lava',
0,
undefined,
undefined,
undefined,
nodeProvider
)
console.log(wallet.account)
// {
// address: '1DrDyTr9RpRsQnDnXo2YRiPzPW4ooHX5LLoqXrqfMrpQH',
// publicKey: '0381818e63bd9e35a5489b52a430accefc608fd60aa2c7c0d1b393b5239aedf6b0',
// group: 0
// }
// Transférer 1 ALPH à 15Z54erRksUHb7qxegcKN5DePMv96tXdc1jW26fW3REwT
await wallet.signAndSubmitTransferTx({
signerAddress: wallet.account.address,
destinations: [{
address: '15Z54erRksUHb7qxegcKN5DePMv96tXdc1jW26fW3REwT',
attoAlphAmount: 10n ** 18n
}]
})
// {
// txId: '9a23eab3796a56f538a4574d617b667fb9187721c8df9f39ac89d878cb9755a0',
// fromGroup: 0,
// toGroup: 0
// }
Contrats
Similaire à Ethereum, un contrat est une abstraction du code de programme qui vit sur la blockchain Alephium. Utilisons l'exemple suivant pour illustrer comment tester, déployer et appeler un contrat, veuillez suivre le guide pour créer un projet.
Testez le contrat
Tests unitaires
Le SDK fournit une fonctionnalité de test unitaire, qui appelle le contrat comme une transaction normale, mais au lieu de changer l'état de la blockchain, il renvoie le nouveau contrat état, les sorties de transaction et les événements..
web3.setCurrentNodeProvider('http://localhost:22973')
const wallet = new PrivateKeyWallet('a642942e67258589cd2b1822c631506632db5a12aabcf413604e785300d762a5')
// Commencez par construire le projet
await Project.build()
// Testez la méthode `withdraw` du contrat TokenFaucet, cela NE changera PAS l'état de la blockchain
const testContractAddress = randomContractAddress()
// Le `TokenFaucet` est généré dans le guide de démarrage
const result = await TokenFaucet.tests.withdraw({
address: testContractAddress,
// État initial du contrat de test
initialFields: {
symbol: Buffer.from('TF', 'utf8').toString('hex'),
name: Buffer.from('TokenFaucet', 'utf8').toString('hex'),
decimals: 18n,
supply: 10n ** 18n,
balance: 10n
},
// Actifs détenus par le contrat de test avant un test
initialAsset: {
alphAmount: 10n ** 18n,
tokens: [{
id: binToHex(contractIdFromAddress(testContractAddress)),
amount: 10n
}]
},
// Arguments pour tester la fonction cible du contrat de test
testArgs: { amount: 1n },
// Actifs détenus par l'appelant de la fonction
inputAssets: [{
address: wallet.account.address,
asset: { alphAmount: 10n ** 18n }
}]
})
const contractState = result.contracts[0] as TokenFaucetTypes.State
expect(contractState.address).toEqual(testContractAddress)
Un exemple complet peut être trouvé dans notre alephium-nextjs-template
Tests d'intégration
En plus des tests unitaires, vous pouvez également exécuter certains tests d'intégration, soyez prudent car ceux-ci peuvent modifier l'état de la blockchain.
web3.setCurrentNodeProvider('http://127.0.0.1:22973', undefined, fetch)
await Project.build()
const accounts = signer.getAccounts()
const account = accounts[0]
const testAddress = account.address
await signer.setSelectedAccount(testAddress)
const testGroup = account.group
const deployed = deployments.getDeployedContractResult(testGroup, 'TokenFaucet')
const tokenId = deployed.contractInstance.contractId
const tokenAddress = deployed.contractInstance.address
const faucet = TokenFaucet.at(tokenAddress)
const initialState = await faucet.fetchState()
const initialBalance = initialState.fields.balance
// Appelez la fonction `withdraw` 10 fois
for (let i = 0; i < 10; i++) {
await Withdraw.execute(signer, {
initialFields: { token: tokenId, amount: 1n },
attoAlphAmount: DUST_AMOUNT * 2n
})
//!!! L'état de la blockchain est modifié !!!
const newState = await faucet.fetchState()
const newBalance = newState.fields.balance
expect(newBalance).toEqual(initialBalance - BigInt(i) - 1n)
}
Plus de détails peuvent être trouvés dans notre dossier de tests d'intégration
Déployez le contrat
web3.setCurrentNodeProvider('http://localhost:22973')
const wallet = new PrivateKeyWallet('a642942e67258589cd2b1822c631506632db5a12aabcf413604e785300d762a5')
await Project.build()
// Créez une transaction pour déployer le contrat et soumettez la transaction au réseau Alephium :
// `initialFields` est requis si le contrat a des champs
// `initialAttoAlphAmount` doit être supérieur ou égal à 1 ALPH, les actifs seront envoyés au contrat depuis le compte de l'expéditeur de la transaction
// `issueTokenAmount` spécifie la quantité de jetons à émettre
const issueTokenAmount = 10n
const deployResult = await TokenFaucet.deploy(wallet, {
initialFields: {
symbol: Buffer.from('TF', 'utf8').toString('hex'),
name: Buffer.from('TokenFaucet', 'utf8').toString('hex'),
decimals: 18n,
supply: issueTokenAmount,
balance: issueTokenAmount
},
initialAttoAlphAmount: 10n ** 18n,
issueTokenAmount: issueTokenAmount
})
console.log(JSON.stringify(deployResult, null, 2))
// {
// "signature": "ea95754bae7935311acf15d3323293f03bce89bb6c82939427da5e3074f0ada93b0cda24138dcec1de4e21a1a66dc1f0c8e99297e6ff8fe10587d1821cbae23f",
// "fromGroup": 0,
// "toGroup": 0,
// "unsignedTx": "000401010103000000091500bee85f379545a2ed9f6cceb331288842f378cf0f04012ad4ac8824aae7d6f80a13c40de0b6b3a7640000a2144055050609121b4024402d404a010000000102ce0002010000000102ce0102010000000102ce0202010000000102ce0302010000000102a0000201020101001116000e320c7bb4b11600aba00016002ba10005b416005f14160403025446030b546f6b656e4661756365740212020a140301020a130aae188000dfdfc1174876e8000137a444479fa782e8b88d4f95e28b3b2417e5bc30d33a5ae8486d4a8885b82b224259c1e6000381818e63bd9e35a5489b52a430accefc608fd60aa2c7c0d1b393b5239aedf6b000",
// "gasAmount": 57311,
// "gasPrice": "100000000000",
// "txId": "c9adbbc02f34d2b3f2db8790354bca9f3d6f7a1fde9b9269a393d6693663c084",
// "contractAddress": "v8uU9yLfzUwqpJeS9Kd76DB75WJBcEuMdYgEQ2Gn8pLF",
// "groupIndex": 0,
// "contractId": "1581cc793fc7bafce6ef89eaf66404c9eec17561ef0e169ef2a4329c9f190c00",
// "instance": {
// "address": "v8uU9yLfzUwqpJeS9Kd76DB75WJBcEuMdYgEQ2Gn8pLF",
// "contractId": "1581cc793fc7bafce6ef89eaf66404c9eec17561ef0e169ef2a4329c9f190c00",
// "groupIndex": 0
// }
// }
// Obtenez l'état du contrat
const tokenFaucet = deployResult.instance
const contractState = await tokenFaucet.fetchState()
console.log(JSON.stringify(contractState, null, 2))
// {
// "address": "v8uU9yLfzUwqpJeS9Kd76DB75WJBcEuMdYgEQ2Gn8pLF",
// "contractId": "1581cc793fc7bafce6ef89eaf66404c9eec17561ef0e169ef2a4329c9f190c00",
// "bytecode": "050609121b4024402d404a010000000102ce0002010000000102ce0102010000000102ce0202010000000102ce0302010000000102a0000201020101001116000e320c7bb4b11600aba00016002ba10005b416005f",
// "initialStateHash": "236a82352f5e34f813ecf274385912ed0ba67f1c305c24f7a6934c18d32213b1",
// "codeHash": "641343b4f1c08b03969b127b452acc7535cad20231bc32af6c0b5f218dd8ff0c",
// "fields": {
// "symbol": "5446",
// "name": "546f6b656e466175636574",
// "decimals": "18",
// "supply": "10",
// "balance": "10"
// },
// "fieldsSig": {
// "names": [
// "symbol",
// "name",
// "decimals",
// "supply",
// "balance"
// ],
// "types": [
// "ByteVec",
// "ByteVec",
// "U256",
// "U256",
// "U256"
// ],
// "isMutable": [
// false,
// false,
// false,
// false,
// true
// ]
// },
// "asset": {
// "alphAmount": "1000000000000000000",
// "tokens": [
// {
// "id": "1581cc793fc7bafce6ef89eaf66404c9eec17561ef0e169ef2a4329c9f190c00",
// "amount": "10"
// }
// ]
// }
// }
À partir de la sortie, nous pouvons voir que nous avons déployé avec succès le contrat, et il y a 10 jetons dans l'actif du contrat.
Appelez le contrat
Vous pouvez utiliser des scripts pour appeler des contrats sur la blockchain Alephium, le code du script sera exécuté lorsque la transaction est soumise au réseau Alephium, mais le code du script ne sera pas stocké dans l'état de la blockchain.
web3.setCurrentNodeProvider('http://localhost:22973')
const wallet = new PrivateKeyWallet('a642942e67258589cd2b1822c631506632db5a12aabcf413604e785300d762a5')
await Project.build()
// Adresse du contrat à partir du résultat du déploiement
const contractAddress = deployResult.instance.address
// ID de contrat à partir du résultat du déploiement
const contractId = deployResult.instance.contractId
// Créez une transaction d'appel de contrat, `initialFields` est requis si le script a des champs
// Le `Withdraw` est généré dans le guide de démarrage
const executeResult = await Withdraw.execute(wallet, {
initialFields: {
token: contractId,
amount: 1n
}
})
console.log(JSON.stringify(executeResult, null, 2))
// {
// "signature": "16ec5eed788bfe7a803ec89f47d8ef7c1ac5f626ad88e4b46ecdde8be9fdef5719db49b09ef4e11a94901082e34c52629aafad4b9753483bf853ff695b19b9a4",
// "fromGroup": 0,
// "toGroup": 0,
// "unsignedTx": "0004010101030000000513010d0c1440201581cc793fc7bafce6ef89eaf66404c9eec17561ef0e169ef2a4329c9f190c0001058000a447c1174876e8000137a44447f11525fe8e58af5a02b2f81bbbf35ea3c1bdf319ffe76794709c9e9d4e80c599000381818e63bd9e35a5489b52a430accefc608fd60aa2c7c0d1b393b5239aedf6b000",
// "gasAmount": 42055,
// "gasPrice": "100000000000",
// "txId": "c29e9cb10b3e0b34979b9daac73151d98ee4de8f913e66aa0f0c8dc0cb99a617",
// "groupIndex": 0
// }
// Obtenez le solde du compte
const balance = await wallet.nodeProvider.addresses.getAddressesAddressBalance(wallet.account.address)
console.log(JSON.stringify(balance, null, 2))
// {
// "balance": "999998990063400000000000",
// "balanceHint": "999998.9900634 ALPH",
// "lockedBalance": "0",
// "lockedBalanceHint": "0 ALPH",
// "tokenBalances": [
// {
// "id": "1581cc793fc7bafce6ef89eaf66404c9eec17561ef0e169ef2a4329c9f190c00",
// "amount": "1"
// }
// ],
// "utxoNum": 2
// }
Interrogez les événements de contrat historiques
Les événements de contrat sont indexés par adresse de contrat avec des décalages, et vous pouvez interroger les événements historiques d'une adresse de contrat en spécifiant le décalage et la limite (facultatif).
const nodeProvider = new NodeProvider('http://localhost:22973')
// Adresse du contrat à partir du résultat du déploiement du contrat
const contractAddress = deployResult.instance.address
// Interrogez les événements de contrat à partir de l'index 0, et la `limit` ne peut pas être supérieure à 100
const result = await nodeProvider.events.getEventsContractContractaddress(
contractAddress, {start: 0, limit: 100}
)
// Dans la prochaine requête, vous pouvez commencer par `result.nextStart`
console.log(JSON.stringify(result, null, 2))
// {
// "events": [
// {
// "blockHash": "0c07e672c40629a5c943cc7e4ec677140cbd50fdc9dcacb6a1d30bc38c0e7b50",
// "txId": "c29e9cb10b3e0b34979b9daac73151d98ee4de8f913e66aa0f0c8dc0cb99a617",
// "eventIndex": 0,
// "fields": [
// {
// "type": "Address",
// "value": "1DrDyTr9RpRsQnDnXo2YRiPzPW4ooHX5LLoqXrqfMrpQH"
// },
// {
// "type": "U256",
// "value": "1"
// }
// ]
// }
// ],
// "nextStart": 1
// }
// Parfois, des événements peuvent être émis à partir de blocs non canoniques en raison de la réorganisation des blocs, vous pouvez vérifier si le bloc est dans la chaîne principale
await nodeProvider.blockflow.getBlockflowIsBlockInMainChain({blockHash: events[0].blockHash})
// true
// Obtenez le compteur d'événements de contrat actuel
await nodeProvider.events.getEventsContractContractaddressCurrentCount(contractAddress)
// 1
// Vous pouvez également obtenir des événements par identifiant de transaction si `alephium.node.event-log.index-by-tx-id` est activé dans votre fichier de configuration de nœud complet
await nodeProvider.events.getEventsTxIdTxid('c29e9cb10b3e0b34979b9daac73151d98ee4de8f913e66aa0f0c8dc0cb99a617')
// {
// "events": [
// {
// "blockHash": "0c07e672c40629a5c943cc7e4ec677140cbd50fdc9dcacb6a1d30bc38c0e7b50",
// "contractAddress": "v8uU9yLfzUwqpJeS9Kd76DB75WJBcEuMdYgEQ2Gn8pLF",
// "eventIndex": 0,
// "fields": [
// {
// "type": "Address",
// "value": "1DrDyTr9RpRsQnDnXo2YRiPzPW4ooHX5LLoqXrqfMrpQH"
// },
// {
// "type": "U256",
// "value": "1"
// }
// ]
// }
// ]
// }
Écoute des événements
En plus d'interroger les événements un par un, vous pouvez également obtenir des événements par abonnement aux événements. Il interrogera périodiquement et obtiendra de nouveaux événements.
web3.setCurrentNodeProvider('http://localhost:22973')
// L'instance de contrat `TokenFaucet` à partir du résultat du déploiement
const tokenFaucet = deployResult.instance
// Le `TokenFaucetTypes.WithdrawEvent` est généré dans le guide de démarrage
const events: TokenFaucetTypes.WithdrawEvent[] = []
const subscribeOptions = {
// Il vérifiera les nouveaux événements depuis le nœud complet toutes les `pollingInterval`
pollingInterval: 500,
// La fonction de rappel sera appelée pour chaque événement
messageCallback: (event: TokenFaucetTypes.WithdrawEvent): Promise<void> => {
events.push(event)
return Promise.resolve()
},
// Cette fonction de rappel sera appelée en cas d'erreur
errorCallback: (error: any, subscription): Promise<void> => {
console.log(error)
subscription.unsubscribe()
return Promise.resolve()
}
}
// Abonnez-vous aux événements du contrat à partir de l'index 0
const subscription = tokenFaucet.subscribeWithdrawEvent(subscribeOptions, 0)
await new Promise((resolve) => setTimeout(resolve, 1000))
console.log(JSON.stringify(events, null, 2))
// [
// {
// "contractAddress": "v8uU9yLfzUwqpJeS9Kd76DB75WJBcEuMdYgEQ2Gn8pLF",
// "blockHash": "0c07e672c40629a5c943cc7e4ec677140cbd50fdc9dcacb6a1d30bc38c0e7b50",
// "txId": "c29e9cb10b3e0b34979b9daac73151d98ee4de8f913e66aa0f0c8dc0cb99a617",
// "eventIndex": 0,
// "name": "Withdraw",
// "fields": {
// "to": "1DrDyTr9RpRsQnDnXo2YRiPzPW4ooHX5LLoqXrqfMrpQH",
// "amount": "1"
// }
// }
// ]
// Se désabonner
subscription.unsubscribe()
Outils
Conversion entre l'ID de contrat et l'adresse de contrat
const contractId = 'bfc891f2f7fbb466bd7808f71cc022debb71fd3c1ceb752b623eb9c48ec4d165'
const contractAddress = addressFromContractId(contractId)
console.log(binToHex(contractIdFromAddress(contractAddress)) === contractId)
// true
Obtenez le groupe d'une adresse
const group = groupOfAddress('1DrDyTr9RpRsQnDnXo2YRiPzPW4ooHX5LLoqXrqfMrpQH')
console.log(group)
// 0