diff --git a/404.html b/404.html index aa170aaa0..6b7d23063 100644 --- a/404.html +++ b/404.html @@ -15,7 +15,7 @@ - + @@ -96,12 +96,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/CNAME b/CNAME deleted file mode 100644 index 02728af5f..000000000 --- a/CNAME +++ /dev/null @@ -1 +0,0 @@ -docs.fetch.ai diff --git a/CosmPy/api/aerial/client/__init__/index.html b/CosmPy/api/aerial/client/__init__/index.html index 5ef5a10a1..59fdb70b3 100644 --- a/CosmPy/api/aerial/client/__init__/index.html +++ b/CosmPy/api/aerial/client/__init__/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/CosmPy/api/aerial/client/bank/index.html b/CosmPy/api/aerial/client/bank/index.html index 5c1df1cc1..6e4c2160c 100644 --- a/CosmPy/api/aerial/client/bank/index.html +++ b/CosmPy/api/aerial/client/bank/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/CosmPy/api/aerial/client/distribution/index.html b/CosmPy/api/aerial/client/distribution/index.html index 07c4056c6..5e66f3037 100644 --- a/CosmPy/api/aerial/client/distribution/index.html +++ b/CosmPy/api/aerial/client/distribution/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/CosmPy/api/aerial/client/staking/index.html b/CosmPy/api/aerial/client/staking/index.html index 6fb8e35a7..71a3a7f43 100644 --- a/CosmPy/api/aerial/client/staking/index.html +++ b/CosmPy/api/aerial/client/staking/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/CosmPy/api/aerial/client/utils/index.html b/CosmPy/api/aerial/client/utils/index.html index d4e2ff66a..9958e6ba7 100644 --- a/CosmPy/api/aerial/client/utils/index.html +++ b/CosmPy/api/aerial/client/utils/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/CosmPy/api/aerial/coins/index.html b/CosmPy/api/aerial/coins/index.html index 1907f5c3b..8ee2c5868 100644 --- a/CosmPy/api/aerial/coins/index.html +++ b/CosmPy/api/aerial/coins/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/CosmPy/api/aerial/config/index.html b/CosmPy/api/aerial/config/index.html index d4f6a7f1d..59032cac4 100644 --- a/CosmPy/api/aerial/config/index.html +++ b/CosmPy/api/aerial/config/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/CosmPy/api/aerial/contract/__init__/index.html b/CosmPy/api/aerial/contract/__init__/index.html index 881a390d3..7eb7fa697 100644 --- a/CosmPy/api/aerial/contract/__init__/index.html +++ b/CosmPy/api/aerial/contract/__init__/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/CosmPy/api/aerial/contract/cosmwasm/index.html b/CosmPy/api/aerial/contract/cosmwasm/index.html index 9f115c0c7..a3f888936 100644 --- a/CosmPy/api/aerial/contract/cosmwasm/index.html +++ b/CosmPy/api/aerial/contract/cosmwasm/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/CosmPy/api/aerial/exceptions/index.html b/CosmPy/api/aerial/exceptions/index.html index f7ab02c65..134db1565 100644 --- a/CosmPy/api/aerial/exceptions/index.html +++ b/CosmPy/api/aerial/exceptions/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/CosmPy/api/aerial/faucet/index.html b/CosmPy/api/aerial/faucet/index.html index baecd186f..b91e4d863 100644 --- a/CosmPy/api/aerial/faucet/index.html +++ b/CosmPy/api/aerial/faucet/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/CosmPy/api/aerial/gas/index.html b/CosmPy/api/aerial/gas/index.html index d96909fbc..c301875fb 100644 --- a/CosmPy/api/aerial/gas/index.html +++ b/CosmPy/api/aerial/gas/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/CosmPy/api/aerial/tx/index.html b/CosmPy/api/aerial/tx/index.html index 3f1bf3e08..1c43b38dc 100644 --- a/CosmPy/api/aerial/tx/index.html +++ b/CosmPy/api/aerial/tx/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/CosmPy/api/aerial/tx_helpers/index.html b/CosmPy/api/aerial/tx_helpers/index.html index ec79f066f..808f575bd 100644 --- a/CosmPy/api/aerial/tx_helpers/index.html +++ b/CosmPy/api/aerial/tx_helpers/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/CosmPy/api/aerial/urls/index.html b/CosmPy/api/aerial/urls/index.html index bdfe2cd62..75138eae9 100644 --- a/CosmPy/api/aerial/urls/index.html +++ b/CosmPy/api/aerial/urls/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/CosmPy/api/aerial/wallet/index.html b/CosmPy/api/aerial/wallet/index.html index eb0afaaea..b530e889e 100644 --- a/CosmPy/api/aerial/wallet/index.html +++ b/CosmPy/api/aerial/wallet/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/CosmPy/auto-compounder/index.html b/CosmPy/auto-compounder/index.html index a45a5c678..135eb0e03 100644 --- a/CosmPy/auto-compounder/index.html +++ b/CosmPy/auto-compounder/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/CosmPy/connect-to-network/index.html b/CosmPy/connect-to-network/index.html index f00a49877..6dc0d7131 100644 --- a/CosmPy/connect-to-network/index.html +++ b/CosmPy/connect-to-network/index.html @@ -21,7 +21,7 @@ - + @@ -102,12 +102,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/CosmPy/deploy-a-contract/index.html b/CosmPy/deploy-a-contract/index.html index 7944e4f74..2aeab7a9b 100644 --- a/CosmPy/deploy-a-contract/index.html +++ b/CosmPy/deploy-a-contract/index.html @@ -21,7 +21,7 @@ - + @@ -102,12 +102,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/CosmPy/index.html b/CosmPy/index.html index 7f3070c57..59973ffe0 100644 --- a/CosmPy/index.html +++ b/CosmPy/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/CosmPy/liquidity-pool/index.html b/CosmPy/liquidity-pool/index.html index 813204cb3..6a383e338 100644 --- a/CosmPy/liquidity-pool/index.html +++ b/CosmPy/liquidity-pool/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/CosmPy/low-level-api/index.html b/CosmPy/low-level-api/index.html index 90e530e2b..3e4bbf238 100644 --- a/CosmPy/low-level-api/index.html +++ b/CosmPy/low-level-api/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/CosmPy/oracles/index.html b/CosmPy/oracles/index.html index 2d25ad3a3..07e12e178 100644 --- a/CosmPy/oracles/index.html +++ b/CosmPy/oracles/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/CosmPy/query-balance/index.html b/CosmPy/query-balance/index.html index 2c82a2f87..aa93a9fcb 100644 --- a/CosmPy/query-balance/index.html +++ b/CosmPy/query-balance/index.html @@ -21,7 +21,7 @@ - + @@ -102,12 +102,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/CosmPy/send-tokens/index.html b/CosmPy/send-tokens/index.html index 20ebe4f5b..fc5cf39c5 100644 --- a/CosmPy/send-tokens/index.html +++ b/CosmPy/send-tokens/index.html @@ -21,7 +21,7 @@ - + @@ -102,12 +102,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/CosmPy/stake-optimizer/index.html b/CosmPy/stake-optimizer/index.html index e0694b241..6351d686d 100644 --- a/CosmPy/stake-optimizer/index.html +++ b/CosmPy/stake-optimizer/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/CosmPy/staking/index.html b/CosmPy/staking/index.html index f011eed76..b9237ee3d 100644 --- a/CosmPy/staking/index.html +++ b/CosmPy/staking/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/CosmPy/swap-automation/index.html b/CosmPy/swap-automation/index.html index 0ee5280e1..3654d2033 100644 --- a/CosmPy/swap-automation/index.html +++ b/CosmPy/swap-automation/index.html @@ -21,7 +21,7 @@ - + @@ -102,12 +102,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/CosmPy/wallet-topup/index.html b/CosmPy/wallet-topup/index.html index 6bfe0323a..2620e2492 100644 --- a/CosmPy/wallet-topup/index.html +++ b/CosmPy/wallet-topup/index.html @@ -21,7 +21,7 @@ - + @@ -102,12 +102,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/CosmPy/wallets-and-keys/index.html b/CosmPy/wallets-and-keys/index.html index 64737c0a0..f98a5aacf 100644 --- a/CosmPy/wallets-and-keys/index.html +++ b/CosmPy/wallets-and-keys/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/Jenesis/add-contracts/index.html b/Jenesis/add-contracts/index.html index 7c654ad9a..6d4c36518 100644 --- a/Jenesis/add-contracts/index.html +++ b/Jenesis/add-contracts/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/Jenesis/add-profile/index.html b/Jenesis/add-profile/index.html index e3c7ceaed..8e8f1f7e7 100644 --- a/Jenesis/add-profile/index.html +++ b/Jenesis/add-profile/index.html @@ -21,7 +21,7 @@ - + @@ -102,12 +102,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/Jenesis/compile-contracts/index.html b/Jenesis/compile-contracts/index.html index 75c098908..18d7d22f2 100644 --- a/Jenesis/compile-contracts/index.html +++ b/Jenesis/compile-contracts/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/Jenesis/deploy-contracts/index.html b/Jenesis/deploy-contracts/index.html index bb324c6a6..415c924a4 100644 --- a/Jenesis/deploy-contracts/index.html +++ b/Jenesis/deploy-contracts/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/Jenesis/index.html b/Jenesis/index.html index 6d2bf22e2..fb1754cfc 100644 --- a/Jenesis/index.html +++ b/Jenesis/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/Jenesis/keys/index.html b/Jenesis/keys/index.html index 5292bfa00..fb753c755 100644 --- a/Jenesis/keys/index.html +++ b/Jenesis/keys/index.html @@ -21,7 +21,7 @@ - + @@ -102,12 +102,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/Jenesis/use-contracts/index.html b/Jenesis/use-contracts/index.html index 1156f6a4a..3942bae46 100644 --- a/Jenesis/use-contracts/index.html +++ b/Jenesis/use-contracts/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/12-factor/index.html b/aea-framework-documentation/12-factor/index.html index 16515cc02..9b491a42e 100644 --- a/aea-framework-documentation/12-factor/index.html +++ b/aea-framework-documentation/12-factor/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/acn-internals/index.html b/aea-framework-documentation/acn-internals/index.html index 4f46a094d..708160343 100644 --- a/aea-framework-documentation/acn-internals/index.html +++ b/aea-framework-documentation/acn-internals/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/acn/index.html b/aea-framework-documentation/acn/index.html index 90afc3e36..c7f71e770 100644 --- a/aea-framework-documentation/acn/index.html +++ b/aea-framework-documentation/acn/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/aea-vs-mvc/index.html b/aea-framework-documentation/aea-vs-mvc/index.html index cc6095079..1b23eac61 100644 --- a/aea-framework-documentation/aea-vs-mvc/index.html +++ b/aea-framework-documentation/aea-vs-mvc/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/aeas/index.html b/aea-framework-documentation/aeas/index.html index 4f70b0b82..c4811d238 100644 --- a/aea-framework-documentation/aeas/index.html +++ b/aea-framework-documentation/aeas/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/agent-oriented-development/index.html b/aea-framework-documentation/agent-oriented-development/index.html index dcec1643e..85d5645e7 100644 --- a/aea-framework-documentation/agent-oriented-development/index.html +++ b/aea-framework-documentation/agent-oriented-development/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/agent-vs-aea/index.html b/aea-framework-documentation/agent-vs-aea/index.html index 4bb76df02..5b92b103c 100644 --- a/aea-framework-documentation/agent-vs-aea/index.html +++ b/aea-framework-documentation/agent-vs-aea/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/aggregation-demo/index.html b/aea-framework-documentation/aggregation-demo/index.html index 5c5609327..2520c3d45 100644 --- a/aea-framework-documentation/aggregation-demo/index.html +++ b/aea-framework-documentation/aggregation-demo/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/abstract_agent/index.html b/aea-framework-documentation/api/abstract_agent/index.html index c7746f5aa..c0717c36b 100644 --- a/aea-framework-documentation/api/abstract_agent/index.html +++ b/aea-framework-documentation/api/abstract_agent/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/aea/index.html b/aea-framework-documentation/api/aea/index.html index 14166546c..6248cc620 100644 --- a/aea-framework-documentation/api/aea/index.html +++ b/aea-framework-documentation/api/aea/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/aea_builder/index.html b/aea-framework-documentation/api/aea_builder/index.html index 5df03b67f..9a5d56117 100644 --- a/aea-framework-documentation/api/aea_builder/index.html +++ b/aea-framework-documentation/api/aea_builder/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/agent/index.html b/aea-framework-documentation/api/agent/index.html index 218e0d078..cd3b54667 100644 --- a/aea-framework-documentation/api/agent/index.html +++ b/aea-framework-documentation/api/agent/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/agent_loop/index.html b/aea-framework-documentation/api/agent_loop/index.html index afcdacba5..5d9a3d9ac 100644 --- a/aea-framework-documentation/api/agent_loop/index.html +++ b/aea-framework-documentation/api/agent_loop/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/common/index.html b/aea-framework-documentation/api/common/index.html index 231d9a323..62043daab 100644 --- a/aea-framework-documentation/api/common/index.html +++ b/aea-framework-documentation/api/common/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/components/base/index.html b/aea-framework-documentation/api/components/base/index.html index b9ceb69b5..94d0819a3 100644 --- a/aea-framework-documentation/api/components/base/index.html +++ b/aea-framework-documentation/api/components/base/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/components/loader/index.html b/aea-framework-documentation/api/components/loader/index.html index 2964c13f7..2322d36bf 100644 --- a/aea-framework-documentation/api/components/loader/index.html +++ b/aea-framework-documentation/api/components/loader/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/components/utils/index.html b/aea-framework-documentation/api/components/utils/index.html index 4af65e1ea..ad4abbb47 100644 --- a/aea-framework-documentation/api/components/utils/index.html +++ b/aea-framework-documentation/api/components/utils/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/configurations/base/index.html b/aea-framework-documentation/api/configurations/base/index.html index 85145f18b..b1c60a652 100644 --- a/aea-framework-documentation/api/configurations/base/index.html +++ b/aea-framework-documentation/api/configurations/base/index.html @@ -21,7 +21,7 @@ - + @@ -102,12 +102,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/configurations/constants/index.html b/aea-framework-documentation/api/configurations/constants/index.html index b6ee660ce..2e6c53024 100644 --- a/aea-framework-documentation/api/configurations/constants/index.html +++ b/aea-framework-documentation/api/configurations/constants/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/configurations/data_types/index.html b/aea-framework-documentation/api/configurations/data_types/index.html index 2aee8e6c0..8cbf9a5e5 100644 --- a/aea-framework-documentation/api/configurations/data_types/index.html +++ b/aea-framework-documentation/api/configurations/data_types/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/configurations/loader/index.html b/aea-framework-documentation/api/configurations/loader/index.html index f48a18059..d5f0b77b4 100644 --- a/aea-framework-documentation/api/configurations/loader/index.html +++ b/aea-framework-documentation/api/configurations/loader/index.html @@ -21,7 +21,7 @@ - + @@ -102,12 +102,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/configurations/manager/index.html b/aea-framework-documentation/api/configurations/manager/index.html index ffeaa9e78..2ffc6778f 100644 --- a/aea-framework-documentation/api/configurations/manager/index.html +++ b/aea-framework-documentation/api/configurations/manager/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/configurations/pypi/index.html b/aea-framework-documentation/api/configurations/pypi/index.html index 0c82c1398..4e4a6fd9b 100644 --- a/aea-framework-documentation/api/configurations/pypi/index.html +++ b/aea-framework-documentation/api/configurations/pypi/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/configurations/utils/index.html b/aea-framework-documentation/api/configurations/utils/index.html index 62a8c3809..ffd132d03 100644 --- a/aea-framework-documentation/api/configurations/utils/index.html +++ b/aea-framework-documentation/api/configurations/utils/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/configurations/validation/index.html b/aea-framework-documentation/api/configurations/validation/index.html index 78c679aba..1897be5c0 100644 --- a/aea-framework-documentation/api/configurations/validation/index.html +++ b/aea-framework-documentation/api/configurations/validation/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/connections/base/index.html b/aea-framework-documentation/api/connections/base/index.html index 9dd22bdd1..0d6e6447f 100644 --- a/aea-framework-documentation/api/connections/base/index.html +++ b/aea-framework-documentation/api/connections/base/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/context/base/index.html b/aea-framework-documentation/api/context/base/index.html index 1453df9ac..6a3083775 100644 --- a/aea-framework-documentation/api/context/base/index.html +++ b/aea-framework-documentation/api/context/base/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/contracts/base/index.html b/aea-framework-documentation/api/contracts/base/index.html index b2549f94d..03eff211b 100644 --- a/aea-framework-documentation/api/contracts/base/index.html +++ b/aea-framework-documentation/api/contracts/base/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/crypto/base/index.html b/aea-framework-documentation/api/crypto/base/index.html index d4df008a2..3ede6fd4e 100644 --- a/aea-framework-documentation/api/crypto/base/index.html +++ b/aea-framework-documentation/api/crypto/base/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/crypto/helpers/index.html b/aea-framework-documentation/api/crypto/helpers/index.html index 1a1b343ba..47cc9e85d 100644 --- a/aea-framework-documentation/api/crypto/helpers/index.html +++ b/aea-framework-documentation/api/crypto/helpers/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/crypto/ledger_apis/index.html b/aea-framework-documentation/api/crypto/ledger_apis/index.html index 7bacf328d..e9f5612ee 100644 --- a/aea-framework-documentation/api/crypto/ledger_apis/index.html +++ b/aea-framework-documentation/api/crypto/ledger_apis/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/crypto/plugin/index.html b/aea-framework-documentation/api/crypto/plugin/index.html index e1cd6fc81..58229f34d 100644 --- a/aea-framework-documentation/api/crypto/plugin/index.html +++ b/aea-framework-documentation/api/crypto/plugin/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/crypto/registries/base/index.html b/aea-framework-documentation/api/crypto/registries/base/index.html index 7bef96182..3474ce370 100644 --- a/aea-framework-documentation/api/crypto/registries/base/index.html +++ b/aea-framework-documentation/api/crypto/registries/base/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/crypto/wallet/index.html b/aea-framework-documentation/api/crypto/wallet/index.html index e1d3983f8..8917edbc6 100644 --- a/aea-framework-documentation/api/crypto/wallet/index.html +++ b/aea-framework-documentation/api/crypto/wallet/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/decision_maker/base/index.html b/aea-framework-documentation/api/decision_maker/base/index.html index 0ff1df1d8..b83b2b3e0 100644 --- a/aea-framework-documentation/api/decision_maker/base/index.html +++ b/aea-framework-documentation/api/decision_maker/base/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/decision_maker/default/index.html b/aea-framework-documentation/api/decision_maker/default/index.html index 13a233a1e..5f1f4b696 100644 --- a/aea-framework-documentation/api/decision_maker/default/index.html +++ b/aea-framework-documentation/api/decision_maker/default/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/decision_maker/gop/index.html b/aea-framework-documentation/api/decision_maker/gop/index.html index 18edb3fc9..9f902a982 100644 --- a/aea-framework-documentation/api/decision_maker/gop/index.html +++ b/aea-framework-documentation/api/decision_maker/gop/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/error_handler/base/index.html b/aea-framework-documentation/api/error_handler/base/index.html index cd09d4141..07cf60282 100644 --- a/aea-framework-documentation/api/error_handler/base/index.html +++ b/aea-framework-documentation/api/error_handler/base/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/error_handler/default/index.html b/aea-framework-documentation/api/error_handler/default/index.html index ce1c6dcfb..593ccf7be 100644 --- a/aea-framework-documentation/api/error_handler/default/index.html +++ b/aea-framework-documentation/api/error_handler/default/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/exceptions/index.html b/aea-framework-documentation/api/exceptions/index.html index 02e526208..9917e8b6a 100644 --- a/aea-framework-documentation/api/exceptions/index.html +++ b/aea-framework-documentation/api/exceptions/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/helpers/acn/agent_record/index.html b/aea-framework-documentation/api/helpers/acn/agent_record/index.html index 1ed48c132..33b02599f 100644 --- a/aea-framework-documentation/api/helpers/acn/agent_record/index.html +++ b/aea-framework-documentation/api/helpers/acn/agent_record/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/helpers/acn/uri/index.html b/aea-framework-documentation/api/helpers/acn/uri/index.html index 8b4620e09..520c60130 100644 --- a/aea-framework-documentation/api/helpers/acn/uri/index.html +++ b/aea-framework-documentation/api/helpers/acn/uri/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/helpers/async_friendly_queue/index.html b/aea-framework-documentation/api/helpers/async_friendly_queue/index.html index fd9d0c7db..5561a8d80 100644 --- a/aea-framework-documentation/api/helpers/async_friendly_queue/index.html +++ b/aea-framework-documentation/api/helpers/async_friendly_queue/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/helpers/async_utils/index.html b/aea-framework-documentation/api/helpers/async_utils/index.html index d64a5217f..4aee657f7 100644 --- a/aea-framework-documentation/api/helpers/async_utils/index.html +++ b/aea-framework-documentation/api/helpers/async_utils/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/helpers/base/index.html b/aea-framework-documentation/api/helpers/base/index.html index 3658d1897..a0b396867 100644 --- a/aea-framework-documentation/api/helpers/base/index.html +++ b/aea-framework-documentation/api/helpers/base/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/helpers/constants/index.html b/aea-framework-documentation/api/helpers/constants/index.html index a30523cc6..7cb6cb4e9 100644 --- a/aea-framework-documentation/api/helpers/constants/index.html +++ b/aea-framework-documentation/api/helpers/constants/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/helpers/env_vars/index.html b/aea-framework-documentation/api/helpers/env_vars/index.html index f385d34ee..5afd8283c 100644 --- a/aea-framework-documentation/api/helpers/env_vars/index.html +++ b/aea-framework-documentation/api/helpers/env_vars/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/helpers/exception_policy/index.html b/aea-framework-documentation/api/helpers/exception_policy/index.html index e57b68487..699308efc 100644 --- a/aea-framework-documentation/api/helpers/exception_policy/index.html +++ b/aea-framework-documentation/api/helpers/exception_policy/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/helpers/exec_timeout/index.html b/aea-framework-documentation/api/helpers/exec_timeout/index.html index 6303a4dc5..e76edb742 100644 --- a/aea-framework-documentation/api/helpers/exec_timeout/index.html +++ b/aea-framework-documentation/api/helpers/exec_timeout/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/helpers/file_io/index.html b/aea-framework-documentation/api/helpers/file_io/index.html index f73cb7e76..1350d78c3 100644 --- a/aea-framework-documentation/api/helpers/file_io/index.html +++ b/aea-framework-documentation/api/helpers/file_io/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/helpers/file_lock/index.html b/aea-framework-documentation/api/helpers/file_lock/index.html index bf716c1dd..d79199e3f 100644 --- a/aea-framework-documentation/api/helpers/file_lock/index.html +++ b/aea-framework-documentation/api/helpers/file_lock/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/helpers/http_requests/index.html b/aea-framework-documentation/api/helpers/http_requests/index.html index bd844b078..9351ae848 100644 --- a/aea-framework-documentation/api/helpers/http_requests/index.html +++ b/aea-framework-documentation/api/helpers/http_requests/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/helpers/install_dependency/index.html b/aea-framework-documentation/api/helpers/install_dependency/index.html index 4e4465434..fbb028630 100644 --- a/aea-framework-documentation/api/helpers/install_dependency/index.html +++ b/aea-framework-documentation/api/helpers/install_dependency/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/helpers/io/index.html b/aea-framework-documentation/api/helpers/io/index.html index 40a04a8a7..2aafb49b3 100644 --- a/aea-framework-documentation/api/helpers/io/index.html +++ b/aea-framework-documentation/api/helpers/io/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/helpers/ipfs/base/index.html b/aea-framework-documentation/api/helpers/ipfs/base/index.html index 2c4678940..6c5a68d75 100644 --- a/aea-framework-documentation/api/helpers/ipfs/base/index.html +++ b/aea-framework-documentation/api/helpers/ipfs/base/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/helpers/ipfs/utils/index.html b/aea-framework-documentation/api/helpers/ipfs/utils/index.html index 5e231c826..9901f44ee 100644 --- a/aea-framework-documentation/api/helpers/ipfs/utils/index.html +++ b/aea-framework-documentation/api/helpers/ipfs/utils/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/helpers/logging/index.html b/aea-framework-documentation/api/helpers/logging/index.html index 2ba52b51d..5c3120848 100644 --- a/aea-framework-documentation/api/helpers/logging/index.html +++ b/aea-framework-documentation/api/helpers/logging/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/helpers/multiaddr/base/index.html b/aea-framework-documentation/api/helpers/multiaddr/base/index.html index b594c8423..8c727cd97 100644 --- a/aea-framework-documentation/api/helpers/multiaddr/base/index.html +++ b/aea-framework-documentation/api/helpers/multiaddr/base/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/helpers/multiple_executor/index.html b/aea-framework-documentation/api/helpers/multiple_executor/index.html index 43c781926..eef7c7797 100644 --- a/aea-framework-documentation/api/helpers/multiple_executor/index.html +++ b/aea-framework-documentation/api/helpers/multiple_executor/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/helpers/pipe/index.html b/aea-framework-documentation/api/helpers/pipe/index.html index 458565162..3418e3d27 100644 --- a/aea-framework-documentation/api/helpers/pipe/index.html +++ b/aea-framework-documentation/api/helpers/pipe/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/helpers/preference_representations/base/index.html b/aea-framework-documentation/api/helpers/preference_representations/base/index.html index 2a60ef8e8..3553fdb83 100644 --- a/aea-framework-documentation/api/helpers/preference_representations/base/index.html +++ b/aea-framework-documentation/api/helpers/preference_representations/base/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/helpers/profiling/index.html b/aea-framework-documentation/api/helpers/profiling/index.html index 70699dc95..8adf19e04 100644 --- a/aea-framework-documentation/api/helpers/profiling/index.html +++ b/aea-framework-documentation/api/helpers/profiling/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/helpers/search/generic/index.html b/aea-framework-documentation/api/helpers/search/generic/index.html index eef008ff0..fd95812d1 100644 --- a/aea-framework-documentation/api/helpers/search/generic/index.html +++ b/aea-framework-documentation/api/helpers/search/generic/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/helpers/search/models/index.html b/aea-framework-documentation/api/helpers/search/models/index.html index 622673bcc..0d3e0cf57 100644 --- a/aea-framework-documentation/api/helpers/search/models/index.html +++ b/aea-framework-documentation/api/helpers/search/models/index.html @@ -21,7 +21,7 @@ - + @@ -102,12 +102,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/helpers/serializers/index.html b/aea-framework-documentation/api/helpers/serializers/index.html index 01593ad65..02f065928 100644 --- a/aea-framework-documentation/api/helpers/serializers/index.html +++ b/aea-framework-documentation/api/helpers/serializers/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/helpers/storage/backends/base/index.html b/aea-framework-documentation/api/helpers/storage/backends/base/index.html index 44118f061..83350f379 100644 --- a/aea-framework-documentation/api/helpers/storage/backends/base/index.html +++ b/aea-framework-documentation/api/helpers/storage/backends/base/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/helpers/storage/backends/sqlite/index.html b/aea-framework-documentation/api/helpers/storage/backends/sqlite/index.html index 1cf63e036..09b67e3f3 100644 --- a/aea-framework-documentation/api/helpers/storage/backends/sqlite/index.html +++ b/aea-framework-documentation/api/helpers/storage/backends/sqlite/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/helpers/storage/generic_storage/index.html b/aea-framework-documentation/api/helpers/storage/generic_storage/index.html index aa17333dd..bab75cc04 100644 --- a/aea-framework-documentation/api/helpers/storage/generic_storage/index.html +++ b/aea-framework-documentation/api/helpers/storage/generic_storage/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/helpers/sym_link/index.html b/aea-framework-documentation/api/helpers/sym_link/index.html index a8e801b87..7c15ade96 100644 --- a/aea-framework-documentation/api/helpers/sym_link/index.html +++ b/aea-framework-documentation/api/helpers/sym_link/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/helpers/transaction/base/index.html b/aea-framework-documentation/api/helpers/transaction/base/index.html index 8aae8550a..0145657cf 100644 --- a/aea-framework-documentation/api/helpers/transaction/base/index.html +++ b/aea-framework-documentation/api/helpers/transaction/base/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/helpers/win32/index.html b/aea-framework-documentation/api/helpers/win32/index.html index 4dfa2f7a2..d74e4c685 100644 --- a/aea-framework-documentation/api/helpers/win32/index.html +++ b/aea-framework-documentation/api/helpers/win32/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/helpers/yaml_utils/index.html b/aea-framework-documentation/api/helpers/yaml_utils/index.html index 5635e1a57..cedbbb462 100644 --- a/aea-framework-documentation/api/helpers/yaml_utils/index.html +++ b/aea-framework-documentation/api/helpers/yaml_utils/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/identity/base/index.html b/aea-framework-documentation/api/identity/base/index.html index 4d2856469..dd631480e 100644 --- a/aea-framework-documentation/api/identity/base/index.html +++ b/aea-framework-documentation/api/identity/base/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/launcher/index.html b/aea-framework-documentation/api/launcher/index.html index 5474dcc44..6fba3147e 100644 --- a/aea-framework-documentation/api/launcher/index.html +++ b/aea-framework-documentation/api/launcher/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/mail/base/index.html b/aea-framework-documentation/api/mail/base/index.html index 34998c7e1..43db660d4 100644 --- a/aea-framework-documentation/api/mail/base/index.html +++ b/aea-framework-documentation/api/mail/base/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/manager/manager/index.html b/aea-framework-documentation/api/manager/manager/index.html index decb6f9a1..ff75f5abe 100644 --- a/aea-framework-documentation/api/manager/manager/index.html +++ b/aea-framework-documentation/api/manager/manager/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/manager/project/index.html b/aea-framework-documentation/api/manager/project/index.html index 19f9f2db8..ec8fd1ae7 100644 --- a/aea-framework-documentation/api/manager/project/index.html +++ b/aea-framework-documentation/api/manager/project/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/manager/utils/index.html b/aea-framework-documentation/api/manager/utils/index.html index 8da3c37c6..95ac7aa43 100644 --- a/aea-framework-documentation/api/manager/utils/index.html +++ b/aea-framework-documentation/api/manager/utils/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/multiplexer/index.html b/aea-framework-documentation/api/multiplexer/index.html index b90d9ad49..71a286078 100644 --- a/aea-framework-documentation/api/multiplexer/index.html +++ b/aea-framework-documentation/api/multiplexer/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/plugins/aea_cli_ipfs/core/index.html b/aea-framework-documentation/api/plugins/aea_cli_ipfs/core/index.html index 92d03a221..16bc2a26b 100644 --- a/aea-framework-documentation/api/plugins/aea_cli_ipfs/core/index.html +++ b/aea-framework-documentation/api/plugins/aea_cli_ipfs/core/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/plugins/aea_cli_ipfs/ipfs_utils/index.html b/aea-framework-documentation/api/plugins/aea_cli_ipfs/ipfs_utils/index.html index d82ce4662..fa75509bb 100644 --- a/aea-framework-documentation/api/plugins/aea_cli_ipfs/ipfs_utils/index.html +++ b/aea-framework-documentation/api/plugins/aea_cli_ipfs/ipfs_utils/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/index.html b/aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/index.html index e9ec0746f..e8f77b3a3 100644 --- a/aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/index.html +++ b/aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/index.html b/aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/index.html index 7ca070718..45407994e 100644 --- a/aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/index.html +++ b/aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/index.html b/aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/index.html index 026ca3e7f..ba2e4975d 100644 --- a/aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/index.html +++ b/aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/plugins/aea_ledger_fetchai/fetchai/index.html b/aea-framework-documentation/api/plugins/aea_ledger_fetchai/fetchai/index.html index 5e69633c2..0e2a47f0e 100644 --- a/aea-framework-documentation/api/plugins/aea_ledger_fetchai/fetchai/index.html +++ b/aea-framework-documentation/api/plugins/aea_ledger_fetchai/fetchai/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/protocols/base/index.html b/aea-framework-documentation/api/protocols/base/index.html index 479e9ca18..e719c2b15 100644 --- a/aea-framework-documentation/api/protocols/base/index.html +++ b/aea-framework-documentation/api/protocols/base/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/protocols/default/custom_types/index.html b/aea-framework-documentation/api/protocols/default/custom_types/index.html index e8e177301..74b144d39 100644 --- a/aea-framework-documentation/api/protocols/default/custom_types/index.html +++ b/aea-framework-documentation/api/protocols/default/custom_types/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/protocols/default/dialogues/index.html b/aea-framework-documentation/api/protocols/default/dialogues/index.html index 921c50812..d2f7b5241 100644 --- a/aea-framework-documentation/api/protocols/default/dialogues/index.html +++ b/aea-framework-documentation/api/protocols/default/dialogues/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/protocols/default/message/index.html b/aea-framework-documentation/api/protocols/default/message/index.html index 4b782d321..67fbd2db7 100644 --- a/aea-framework-documentation/api/protocols/default/message/index.html +++ b/aea-framework-documentation/api/protocols/default/message/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/protocols/default/serialization/index.html b/aea-framework-documentation/api/protocols/default/serialization/index.html index 9d89391cc..5c3ad1e0d 100644 --- a/aea-framework-documentation/api/protocols/default/serialization/index.html +++ b/aea-framework-documentation/api/protocols/default/serialization/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/protocols/dialogue/base/index.html b/aea-framework-documentation/api/protocols/dialogue/base/index.html index 44155968f..bbdb5f112 100644 --- a/aea-framework-documentation/api/protocols/dialogue/base/index.html +++ b/aea-framework-documentation/api/protocols/dialogue/base/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/protocols/generator/base/index.html b/aea-framework-documentation/api/protocols/generator/base/index.html index 53fae458a..f7cba1d84 100644 --- a/aea-framework-documentation/api/protocols/generator/base/index.html +++ b/aea-framework-documentation/api/protocols/generator/base/index.html @@ -21,7 +21,7 @@ - + @@ -102,12 +102,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/protocols/generator/common/index.html b/aea-framework-documentation/api/protocols/generator/common/index.html index c97c74aff..250a59ba1 100644 --- a/aea-framework-documentation/api/protocols/generator/common/index.html +++ b/aea-framework-documentation/api/protocols/generator/common/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/protocols/generator/extract_specification/index.html b/aea-framework-documentation/api/protocols/generator/extract_specification/index.html index bbfbc7aa8..64b5524ba 100644 --- a/aea-framework-documentation/api/protocols/generator/extract_specification/index.html +++ b/aea-framework-documentation/api/protocols/generator/extract_specification/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/protocols/generator/validate/index.html b/aea-framework-documentation/api/protocols/generator/validate/index.html index 5161cb360..44db9ea5b 100644 --- a/aea-framework-documentation/api/protocols/generator/validate/index.html +++ b/aea-framework-documentation/api/protocols/generator/validate/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/protocols/signing/custom_types/index.html b/aea-framework-documentation/api/protocols/signing/custom_types/index.html index 977ec2744..f813c9199 100644 --- a/aea-framework-documentation/api/protocols/signing/custom_types/index.html +++ b/aea-framework-documentation/api/protocols/signing/custom_types/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/protocols/signing/dialogues/index.html b/aea-framework-documentation/api/protocols/signing/dialogues/index.html index 987495e99..44a3341f6 100644 --- a/aea-framework-documentation/api/protocols/signing/dialogues/index.html +++ b/aea-framework-documentation/api/protocols/signing/dialogues/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/protocols/signing/message/index.html b/aea-framework-documentation/api/protocols/signing/message/index.html index 96498b10b..32ffb6667 100644 --- a/aea-framework-documentation/api/protocols/signing/message/index.html +++ b/aea-framework-documentation/api/protocols/signing/message/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/protocols/signing/serialization/index.html b/aea-framework-documentation/api/protocols/signing/serialization/index.html index 598b9365c..0c368dd25 100644 --- a/aea-framework-documentation/api/protocols/signing/serialization/index.html +++ b/aea-framework-documentation/api/protocols/signing/serialization/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/protocols/state_update/dialogues/index.html b/aea-framework-documentation/api/protocols/state_update/dialogues/index.html index fa33ad318..00c041466 100644 --- a/aea-framework-documentation/api/protocols/state_update/dialogues/index.html +++ b/aea-framework-documentation/api/protocols/state_update/dialogues/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/protocols/state_update/message/index.html b/aea-framework-documentation/api/protocols/state_update/message/index.html index 00e5a670a..4eafd12ae 100644 --- a/aea-framework-documentation/api/protocols/state_update/message/index.html +++ b/aea-framework-documentation/api/protocols/state_update/message/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/protocols/state_update/serialization/index.html b/aea-framework-documentation/api/protocols/state_update/serialization/index.html index 262d74e1b..7a94a8ff0 100644 --- a/aea-framework-documentation/api/protocols/state_update/serialization/index.html +++ b/aea-framework-documentation/api/protocols/state_update/serialization/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/registries/base/index.html b/aea-framework-documentation/api/registries/base/index.html index 10b96860c..54a2dd7d7 100644 --- a/aea-framework-documentation/api/registries/base/index.html +++ b/aea-framework-documentation/api/registries/base/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/registries/filter/index.html b/aea-framework-documentation/api/registries/filter/index.html index 4dbfe03cb..12e92eeb6 100644 --- a/aea-framework-documentation/api/registries/filter/index.html +++ b/aea-framework-documentation/api/registries/filter/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/registries/resources/index.html b/aea-framework-documentation/api/registries/resources/index.html index 80bdcaeb4..01759fb6e 100644 --- a/aea-framework-documentation/api/registries/resources/index.html +++ b/aea-framework-documentation/api/registries/resources/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/runner/index.html b/aea-framework-documentation/api/runner/index.html index f42493ce4..4bde6fa15 100644 --- a/aea-framework-documentation/api/runner/index.html +++ b/aea-framework-documentation/api/runner/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/runtime/index.html b/aea-framework-documentation/api/runtime/index.html index 019a9ddaa..09e83a80a 100644 --- a/aea-framework-documentation/api/runtime/index.html +++ b/aea-framework-documentation/api/runtime/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/skills/base/index.html b/aea-framework-documentation/api/skills/base/index.html index 311c6ffbb..06d2e26cb 100644 --- a/aea-framework-documentation/api/skills/base/index.html +++ b/aea-framework-documentation/api/skills/base/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/skills/behaviours/index.html b/aea-framework-documentation/api/skills/behaviours/index.html index 0feba49c5..6ffdeb65b 100644 --- a/aea-framework-documentation/api/skills/behaviours/index.html +++ b/aea-framework-documentation/api/skills/behaviours/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/skills/tasks/index.html b/aea-framework-documentation/api/skills/tasks/index.html index c9f58441d..b13efc913 100644 --- a/aea-framework-documentation/api/skills/tasks/index.html +++ b/aea-framework-documentation/api/skills/tasks/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/test_tools/constants/index.html b/aea-framework-documentation/api/test_tools/constants/index.html index 02ba0a0eb..2ac8fa0b8 100644 --- a/aea-framework-documentation/api/test_tools/constants/index.html +++ b/aea-framework-documentation/api/test_tools/constants/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/test_tools/exceptions/index.html b/aea-framework-documentation/api/test_tools/exceptions/index.html index fad06c216..d98cf5560 100644 --- a/aea-framework-documentation/api/test_tools/exceptions/index.html +++ b/aea-framework-documentation/api/test_tools/exceptions/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/test_tools/generic/index.html b/aea-framework-documentation/api/test_tools/generic/index.html index af15d7524..755f3bfd4 100644 --- a/aea-framework-documentation/api/test_tools/generic/index.html +++ b/aea-framework-documentation/api/test_tools/generic/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/test_tools/test_cases/index.html b/aea-framework-documentation/api/test_tools/test_cases/index.html index 71628bffa..f6df10bc7 100644 --- a/aea-framework-documentation/api/test_tools/test_cases/index.html +++ b/aea-framework-documentation/api/test_tools/test_cases/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/test_tools/test_contract/index.html b/aea-framework-documentation/api/test_tools/test_contract/index.html index 02c492800..858b54241 100644 --- a/aea-framework-documentation/api/test_tools/test_contract/index.html +++ b/aea-framework-documentation/api/test_tools/test_contract/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/api/test_tools/test_skill/index.html b/aea-framework-documentation/api/test_tools/test_skill/index.html index b31bb4a5a..50742dbcf 100644 --- a/aea-framework-documentation/api/test_tools/test_skill/index.html +++ b/aea-framework-documentation/api/test_tools/test_skill/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/application/index.html b/aea-framework-documentation/application/index.html index da77276b9..47d9eaa75 100644 --- a/aea-framework-documentation/application/index.html +++ b/aea-framework-documentation/application/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/aries-cloud-agent-demo/index.html b/aea-framework-documentation/aries-cloud-agent-demo/index.html index 396201b49..c44caed81 100644 --- a/aea-framework-documentation/aries-cloud-agent-demo/index.html +++ b/aea-framework-documentation/aries-cloud-agent-demo/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/build-aea-programmatically/index.html b/aea-framework-documentation/build-aea-programmatically/index.html index 88d9bf12c..ab85da6e8 100644 --- a/aea-framework-documentation/build-aea-programmatically/index.html +++ b/aea-framework-documentation/build-aea-programmatically/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/build-aea-step-by-step/index.html b/aea-framework-documentation/build-aea-step-by-step/index.html index 78a479d49..5f3a38a8c 100644 --- a/aea-framework-documentation/build-aea-step-by-step/index.html +++ b/aea-framework-documentation/build-aea-step-by-step/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/car-park-skills/index.html b/aea-framework-documentation/car-park-skills/index.html index 04cf4f54c..cf108632f 100644 --- a/aea-framework-documentation/car-park-skills/index.html +++ b/aea-framework-documentation/car-park-skills/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/cli-commands/index.html b/aea-framework-documentation/cli-commands/index.html index d013ba6c6..5f389f549 100644 --- a/aea-framework-documentation/cli-commands/index.html +++ b/aea-framework-documentation/cli-commands/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/cli-vs-programmatic-aeas/index.html b/aea-framework-documentation/cli-vs-programmatic-aeas/index.html index b87de08ae..fa8e9e624 100644 --- a/aea-framework-documentation/cli-vs-programmatic-aeas/index.html +++ b/aea-framework-documentation/cli-vs-programmatic-aeas/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/config/index.html b/aea-framework-documentation/config/index.html index 22394d6f9..14ec29aa4 100644 --- a/aea-framework-documentation/config/index.html +++ b/aea-framework-documentation/config/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/connect-a-frontend/index.html b/aea-framework-documentation/connect-a-frontend/index.html index 582d5ed15..4e28d8f8b 100644 --- a/aea-framework-documentation/connect-a-frontend/index.html +++ b/aea-framework-documentation/connect-a-frontend/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/connection/index.html b/aea-framework-documentation/connection/index.html index 1175009b0..73d0ba27f 100644 --- a/aea-framework-documentation/connection/index.html +++ b/aea-framework-documentation/connection/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/contract/index.html b/aea-framework-documentation/contract/index.html index 1d27b3155..7c0012879 100644 --- a/aea-framework-documentation/contract/index.html +++ b/aea-framework-documentation/contract/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/core-components-1/index.html b/aea-framework-documentation/core-components-1/index.html index efda19f08..c48265c73 100644 --- a/aea-framework-documentation/core-components-1/index.html +++ b/aea-framework-documentation/core-components-1/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/core-components-2/index.html b/aea-framework-documentation/core-components-2/index.html index 059f5e83b..925f63681 100644 --- a/aea-framework-documentation/core-components-2/index.html +++ b/aea-framework-documentation/core-components-2/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/core-components/index.html b/aea-framework-documentation/core-components/index.html index e9b9df1e5..e52448a2b 100644 --- a/aea-framework-documentation/core-components/index.html +++ b/aea-framework-documentation/core-components/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/debug/index.html b/aea-framework-documentation/debug/index.html index c4ba7b96a..04138571a 100644 --- a/aea-framework-documentation/debug/index.html +++ b/aea-framework-documentation/debug/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/decision-maker-transaction/index.html b/aea-framework-documentation/decision-maker-transaction/index.html index 50341987c..699845b88 100644 --- a/aea-framework-documentation/decision-maker-transaction/index.html +++ b/aea-framework-documentation/decision-maker-transaction/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/decision-maker/index.html b/aea-framework-documentation/decision-maker/index.html index 801432ac7..fecca96f9 100644 --- a/aea-framework-documentation/decision-maker/index.html +++ b/aea-framework-documentation/decision-maker/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/defining-data-models/index.html b/aea-framework-documentation/defining-data-models/index.html index ccd086285..257f913b6 100644 --- a/aea-framework-documentation/defining-data-models/index.html +++ b/aea-framework-documentation/defining-data-models/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/demos/index.html b/aea-framework-documentation/demos/index.html index 08fe92bd4..65076f93a 100644 --- a/aea-framework-documentation/demos/index.html +++ b/aea-framework-documentation/demos/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/deployment/index.html b/aea-framework-documentation/deployment/index.html index bcca7c1e4..1fad4006c 100644 --- a/aea-framework-documentation/deployment/index.html +++ b/aea-framework-documentation/deployment/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/design-principles/index.html b/aea-framework-documentation/design-principles/index.html index fb77821fb..2066e8193 100644 --- a/aea-framework-documentation/design-principles/index.html +++ b/aea-framework-documentation/design-principles/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/development-setup/index.html b/aea-framework-documentation/development-setup/index.html index 9aba28cec..e74303cb8 100644 --- a/aea-framework-documentation/development-setup/index.html +++ b/aea-framework-documentation/development-setup/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/diagram/index.html b/aea-framework-documentation/diagram/index.html index 033d88c00..fe76a35aa 100644 --- a/aea-framework-documentation/diagram/index.html +++ b/aea-framework-documentation/diagram/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/ecosystem/index.html b/aea-framework-documentation/ecosystem/index.html index 97d70b2e6..eb07bce02 100644 --- a/aea-framework-documentation/ecosystem/index.html +++ b/aea-framework-documentation/ecosystem/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/erc1155-skills/index.html b/aea-framework-documentation/erc1155-skills/index.html index a3ea48762..d77fa7305 100644 --- a/aea-framework-documentation/erc1155-skills/index.html +++ b/aea-framework-documentation/erc1155-skills/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/generic-skills-step-by-step/index.html b/aea-framework-documentation/generic-skills-step-by-step/index.html index 3eaabdddf..881cbad07 100644 --- a/aea-framework-documentation/generic-skills-step-by-step/index.html +++ b/aea-framework-documentation/generic-skills-step-by-step/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/generic-skills/index.html b/aea-framework-documentation/generic-skills/index.html index b79a6396b..1018add9f 100644 --- a/aea-framework-documentation/generic-skills/index.html +++ b/aea-framework-documentation/generic-skills/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/generic-storage/index.html b/aea-framework-documentation/generic-storage/index.html index b7a85e740..726eb3ee9 100644 --- a/aea-framework-documentation/generic-storage/index.html +++ b/aea-framework-documentation/generic-storage/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/glossary/index.html b/aea-framework-documentation/glossary/index.html index 39e89e6a8..d674c3243 100644 --- a/aea-framework-documentation/glossary/index.html +++ b/aea-framework-documentation/glossary/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/gym-example/index.html b/aea-framework-documentation/gym-example/index.html index 00bd0794b..692e4f9ab 100644 --- a/aea-framework-documentation/gym-example/index.html +++ b/aea-framework-documentation/gym-example/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/gym-skill/index.html b/aea-framework-documentation/gym-skill/index.html index 100d5b60e..423da0f18 100644 --- a/aea-framework-documentation/gym-skill/index.html +++ b/aea-framework-documentation/gym-skill/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/http-connection-and-skill/index.html b/aea-framework-documentation/http-connection-and-skill/index.html index b8ac3eacd..aabeadd3e 100644 --- a/aea-framework-documentation/http-connection-and-skill/index.html +++ b/aea-framework-documentation/http-connection-and-skill/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/identity/index.html b/aea-framework-documentation/identity/index.html index 76f8b1370..b62e564e4 100644 --- a/aea-framework-documentation/identity/index.html +++ b/aea-framework-documentation/identity/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/index.html b/aea-framework-documentation/index.html index 6c9dd0a59..0f0ce98b2 100644 --- a/aea-framework-documentation/index.html +++ b/aea-framework-documentation/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/install/index.html b/aea-framework-documentation/install/index.html index 9c16ef344..00a8a2264 100644 --- a/aea-framework-documentation/install/index.html +++ b/aea-framework-documentation/install/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/interaction-protocol/index.html b/aea-framework-documentation/interaction-protocol/index.html index ca2111f94..1924ca5fd 100644 --- a/aea-framework-documentation/interaction-protocol/index.html +++ b/aea-framework-documentation/interaction-protocol/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/known-limits/index.html b/aea-framework-documentation/known-limits/index.html index ab1149aa2..df5b92e2e 100644 --- a/aea-framework-documentation/known-limits/index.html +++ b/aea-framework-documentation/known-limits/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/language-agnostic-definition/index.html b/aea-framework-documentation/language-agnostic-definition/index.html index 53cee7f12..e61a2c6e2 100644 --- a/aea-framework-documentation/language-agnostic-definition/index.html +++ b/aea-framework-documentation/language-agnostic-definition/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/ledger-integration/index.html b/aea-framework-documentation/ledger-integration/index.html index 689a5b478..c1d967be4 100644 --- a/aea-framework-documentation/ledger-integration/index.html +++ b/aea-framework-documentation/ledger-integration/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/limits/index.html b/aea-framework-documentation/limits/index.html index c7d784389..7aaee06c8 100644 --- a/aea-framework-documentation/limits/index.html +++ b/aea-framework-documentation/limits/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/logging/index.html b/aea-framework-documentation/logging/index.html index 2ed5fca4d..84d31f6b2 100644 --- a/aea-framework-documentation/logging/index.html +++ b/aea-framework-documentation/logging/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/message-routing/index.html b/aea-framework-documentation/message-routing/index.html index 7d8b25b45..8cb6e9874 100644 --- a/aea-framework-documentation/message-routing/index.html +++ b/aea-framework-documentation/message-routing/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/ml-skills/index.html b/aea-framework-documentation/ml-skills/index.html index 0af23b6de..f9938049f 100644 --- a/aea-framework-documentation/ml-skills/index.html +++ b/aea-framework-documentation/ml-skills/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/modes/index.html b/aea-framework-documentation/modes/index.html index ce679a4c9..7c258efce 100644 --- a/aea-framework-documentation/modes/index.html +++ b/aea-framework-documentation/modes/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/multi-agent-manager/index.html b/aea-framework-documentation/multi-agent-manager/index.html index c733d6cb1..b61f5330f 100644 --- a/aea-framework-documentation/multi-agent-manager/index.html +++ b/aea-framework-documentation/multi-agent-manager/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/multiplexer-standalone/index.html b/aea-framework-documentation/multiplexer-standalone/index.html index 1d6be0ae4..41ad046d9 100644 --- a/aea-framework-documentation/multiplexer-standalone/index.html +++ b/aea-framework-documentation/multiplexer-standalone/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/oracle-demo/index.html b/aea-framework-documentation/oracle-demo/index.html index 795546d72..abb9305aa 100644 --- a/aea-framework-documentation/oracle-demo/index.html +++ b/aea-framework-documentation/oracle-demo/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/orm-integration/index.html b/aea-framework-documentation/orm-integration/index.html index 7d6d49ef3..19b2801f8 100644 --- a/aea-framework-documentation/orm-integration/index.html +++ b/aea-framework-documentation/orm-integration/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/p2p-connection/index.html b/aea-framework-documentation/p2p-connection/index.html index 480b7171e..b9ef4f51e 100644 --- a/aea-framework-documentation/p2p-connection/index.html +++ b/aea-framework-documentation/p2p-connection/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/package-imports/index.html b/aea-framework-documentation/package-imports/index.html index a46577a95..05a2ff914 100644 --- a/aea-framework-documentation/package-imports/index.html +++ b/aea-framework-documentation/package-imports/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/performance-benchmark/index.html b/aea-framework-documentation/performance-benchmark/index.html index 50567e55f..d5a6147c2 100644 --- a/aea-framework-documentation/performance-benchmark/index.html +++ b/aea-framework-documentation/performance-benchmark/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/por/index.html b/aea-framework-documentation/por/index.html index 94536160f..6b9b7f137 100644 --- a/aea-framework-documentation/por/index.html +++ b/aea-framework-documentation/por/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/prometheus/index.html b/aea-framework-documentation/prometheus/index.html index 13a141245..57ce3d4c0 100644 --- a/aea-framework-documentation/prometheus/index.html +++ b/aea-framework-documentation/prometheus/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/protocol-generator/index.html b/aea-framework-documentation/protocol-generator/index.html index 99139d620..23deb6a7b 100644 --- a/aea-framework-documentation/protocol-generator/index.html +++ b/aea-framework-documentation/protocol-generator/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/protocol/index.html b/aea-framework-documentation/protocol/index.html index d8c85a1d6..1575c4da2 100644 --- a/aea-framework-documentation/protocol/index.html +++ b/aea-framework-documentation/protocol/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/query-language/index.html b/aea-framework-documentation/query-language/index.html index 9c79e5d0a..b0a6464b9 100644 --- a/aea-framework-documentation/query-language/index.html +++ b/aea-framework-documentation/query-language/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/questions-and-answers/index.html b/aea-framework-documentation/questions-and-answers/index.html index c5c2f386f..5327907d0 100644 --- a/aea-framework-documentation/questions-and-answers/index.html +++ b/aea-framework-documentation/questions-and-answers/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/quickstart/index.html b/aea-framework-documentation/quickstart/index.html index 9bda24b71..ea1ed546b 100644 --- a/aea-framework-documentation/quickstart/index.html +++ b/aea-framework-documentation/quickstart/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/raspberry-set-up/index.html b/aea-framework-documentation/raspberry-set-up/index.html index 5afed805b..33b10cf64 100644 --- a/aea-framework-documentation/raspberry-set-up/index.html +++ b/aea-framework-documentation/raspberry-set-up/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/runtime-cost/index.html b/aea-framework-documentation/runtime-cost/index.html index fbb6f7599..d9ad9bf42 100644 --- a/aea-framework-documentation/runtime-cost/index.html +++ b/aea-framework-documentation/runtime-cost/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/scaffolding/index.html b/aea-framework-documentation/scaffolding/index.html index e8b83a2aa..37d15a8bc 100644 --- a/aea-framework-documentation/scaffolding/index.html +++ b/aea-framework-documentation/scaffolding/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/security/index.html b/aea-framework-documentation/security/index.html index 2996aac52..afac3897b 100644 --- a/aea-framework-documentation/security/index.html +++ b/aea-framework-documentation/security/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/setup/index.html b/aea-framework-documentation/setup/index.html index 7e2aa7d54..10500b122 100644 --- a/aea-framework-documentation/setup/index.html +++ b/aea-framework-documentation/setup/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/simple-oef-usage/index.html b/aea-framework-documentation/simple-oef-usage/index.html index fcdf237da..f0f6562ed 100644 --- a/aea-framework-documentation/simple-oef-usage/index.html +++ b/aea-framework-documentation/simple-oef-usage/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/simple-oef/index.html b/aea-framework-documentation/simple-oef/index.html index e3fcf9dac..7fb35a72d 100644 --- a/aea-framework-documentation/simple-oef/index.html +++ b/aea-framework-documentation/simple-oef/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/skill-guide/index.html b/aea-framework-documentation/skill-guide/index.html index bb39bafb8..a35f0bb96 100644 --- a/aea-framework-documentation/skill-guide/index.html +++ b/aea-framework-documentation/skill-guide/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/skill-testing/index.html b/aea-framework-documentation/skill-testing/index.html index 501f431c9..c8cfae77e 100644 --- a/aea-framework-documentation/skill-testing/index.html +++ b/aea-framework-documentation/skill-testing/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/skill/index.html b/aea-framework-documentation/skill/index.html index 94a9ce2f2..f64210f74 100644 --- a/aea-framework-documentation/skill/index.html +++ b/aea-framework-documentation/skill/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/standalone-transaction/index.html b/aea-framework-documentation/standalone-transaction/index.html index eea3d0bce..0c3a53aff 100644 --- a/aea-framework-documentation/standalone-transaction/index.html +++ b/aea-framework-documentation/standalone-transaction/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/step-one/index.html b/aea-framework-documentation/step-one/index.html index 2218b852e..dcf3af97c 100644 --- a/aea-framework-documentation/step-one/index.html +++ b/aea-framework-documentation/step-one/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/tac-skills-contract/index.html b/aea-framework-documentation/tac-skills-contract/index.html index 897eba9a0..6189eb056 100644 --- a/aea-framework-documentation/tac-skills-contract/index.html +++ b/aea-framework-documentation/tac-skills-contract/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/tac-skills/index.html b/aea-framework-documentation/tac-skills/index.html index 5c1045080..9070f7a26 100644 --- a/aea-framework-documentation/tac-skills/index.html +++ b/aea-framework-documentation/tac-skills/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/tac/index.html b/aea-framework-documentation/tac/index.html index 17fd93890..3deb2b13a 100644 --- a/aea-framework-documentation/tac/index.html +++ b/aea-framework-documentation/tac/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/thermometer-skills/index.html b/aea-framework-documentation/thermometer-skills/index.html index ef958a64c..eda5226df 100644 --- a/aea-framework-documentation/thermometer-skills/index.html +++ b/aea-framework-documentation/thermometer-skills/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/trust/index.html b/aea-framework-documentation/trust/index.html index 284e024dc..dbbc52fa4 100644 --- a/aea-framework-documentation/trust/index.html +++ b/aea-framework-documentation/trust/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/upgrading/index.html b/aea-framework-documentation/upgrading/index.html index 57e5bb2c8..1c1199488 100644 --- a/aea-framework-documentation/upgrading/index.html +++ b/aea-framework-documentation/upgrading/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/wealth/index.html b/aea-framework-documentation/wealth/index.html index 7df64ba68..5bf07ebd5 100644 --- a/aea-framework-documentation/wealth/index.html +++ b/aea-framework-documentation/wealth/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/aea-framework-documentation/weather-skills/index.html b/aea-framework-documentation/weather-skills/index.html index c754ee7d0..b9540ddb2 100644 --- a/aea-framework-documentation/weather-skills/index.html +++ b/aea-framework-documentation/weather-skills/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/archives/redelegate-for-decentralization/index.html b/archives/redelegate-for-decentralization/index.html index a2085b8f1..b70b5bde6 100644 --- a/archives/redelegate-for-decentralization/index.html +++ b/archives/redelegate-for-decentralization/index.html @@ -17,7 +17,7 @@ - + @@ -103,12 +103,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/basics/staking/different_ways_to_stake_fet/index.html b/basics/staking/different_ways_to_stake_fet/index.html index 70dcef6b2..323b1131b 100644 --- a/basics/staking/different_ways_to_stake_fet/index.html +++ b/basics/staking/different_ways_to_stake_fet/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/basics/staking/how_to_stake/index.html b/basics/staking/how_to_stake/index.html index c5b8fa973..d2c2ab5da 100644 --- a/basics/staking/how_to_stake/index.html +++ b/basics/staking/how_to_stake/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/basics/staking/redelegation/index.html b/basics/staking/redelegation/index.html index c8320ca00..2e2a3b20a 100644 --- a/basics/staking/redelegation/index.html +++ b/basics/staking/redelegation/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/basics/staking/what_is_staking/index.html b/basics/staking/what_is_staking/index.html index fd81f8f18..c8e819193 100644 --- a/basics/staking/what_is_staking/index.html +++ b/basics/staking/what_is_staking/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/basics/wallet/getting_started/index.html b/basics/wallet/getting_started/index.html index 51c6c6549..28397e794 100644 --- a/basics/wallet/getting_started/index.html +++ b/basics/wallet/getting_started/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/basics/wallet/how_to_use_wallet/index.html b/basics/wallet/how_to_use_wallet/index.html index ff4aa8a3e..59f934766 100644 --- a/basics/wallet/how_to_use_wallet/index.html +++ b/basics/wallet/how_to_use_wallet/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/bug_bounty/index.html b/bug_bounty/index.html index 387714c81..98c5e225e 100644 --- a/bug_bounty/index.html +++ b/bug_bounty/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/colearn/about/index.html b/colearn/about/index.html index 2bbe1920c..3456214e9 100644 --- a/colearn/about/index.html +++ b/colearn/about/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/colearn/demo/index.html b/colearn/demo/index.html index 34d377a76..dd1cf16cd 100644 --- a/colearn/demo/index.html +++ b/colearn/demo/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/colearn/dev_notes/index.html b/colearn/dev_notes/index.html index 4f66a9c7e..d31b4e1fe 100644 --- a/colearn/dev_notes/index.html +++ b/colearn/dev_notes/index.html @@ -17,7 +17,7 @@ - + @@ -103,12 +103,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/colearn/differential_privacy/index.html b/colearn/differential_privacy/index.html index 7ccd7e247..0e085c224 100644 --- a/colearn/differential_privacy/index.html +++ b/colearn/differential_privacy/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/colearn/examples/index.html b/colearn/examples/index.html index 47a6ca30a..1a0676c9f 100644 --- a/colearn/examples/index.html +++ b/colearn/examples/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/colearn/grpc_examples/index.html b/colearn/grpc_examples/index.html index 1bc35525a..a35a73e85 100644 --- a/colearn/grpc_examples/index.html +++ b/colearn/grpc_examples/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/colearn/grpc_tutorial/index.html b/colearn/grpc_tutorial/index.html index 9a0436ac6..b788d0042 100644 --- a/colearn/grpc_tutorial/index.html +++ b/colearn/grpc_tutorial/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/colearn/index.html b/colearn/index.html index dcc389775..7fa20f13b 100644 --- a/colearn/index.html +++ b/colearn/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/colearn/installation/index.html b/colearn/installation/index.html index 92374d1b7..1a64e275a 100644 --- a/colearn/installation/index.html +++ b/colearn/installation/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/colearn/intro_tutorial_keras/index.html b/colearn/intro_tutorial_keras/index.html index c4420de8e..31216a491 100644 --- a/colearn/intro_tutorial_keras/index.html +++ b/colearn/intro_tutorial_keras/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/colearn/intro_tutorial_mli/index.html b/colearn/intro_tutorial_mli/index.html index 2e7faeac7..56b9698fd 100644 --- a/colearn/intro_tutorial_mli/index.html +++ b/colearn/intro_tutorial_mli/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/colearn/intro_tutorial_pytorch/index.html b/colearn/intro_tutorial_pytorch/index.html index 4c182eb8b..6718b0ae3 100644 --- a/colearn/intro_tutorial_pytorch/index.html +++ b/colearn/intro_tutorial_pytorch/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/colearn/mli_factory/index.html b/colearn/mli_factory/index.html index d0b55dad5..06abd510a 100644 --- a/colearn/mli_factory/index.html +++ b/colearn/mli_factory/index.html @@ -17,7 +17,7 @@ - + @@ -103,12 +103,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/colearn/tasks/index.html b/colearn/tasks/index.html index ff66aa906..79a4ad07f 100644 --- a/colearn/tasks/index.html +++ b/colearn/tasks/index.html @@ -17,7 +17,7 @@ - + @@ -103,12 +103,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/create-react-app/example_dapp/index.html b/create-react-app/example_dapp/index.html index a99201423..24def86b9 100644 --- a/create-react-app/example_dapp/index.html +++ b/create-react-app/example_dapp/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/create-react-app/introduction/index.html b/create-react-app/introduction/index.html index 33f884a17..7d84d21ae 100644 --- a/create-react-app/introduction/index.html +++ b/create-react-app/introduction/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/css/style.css b/css/style.css index 69ee20ade..de31240a0 100644 --- a/css/style.css +++ b/css/style.css @@ -280,15 +280,35 @@ /*overriding the announcement style*/ .md-banner { - background-color: #3b82f6; + background-color: #738199; + color: white; } -.md-banner .announcement-link { +.eol-banner { color: white; } -.md-banner .announcement-link:hover { - color: hsla(0, 100%, 100%, 0.7); +.eol-highlight { + font-weight: bold; +} + +.eol-primary { + font-size: 1.25rem; +} + +.eol-secondary { + color: white; + text-decoration: none; +} + +.eol-primary a { + color: white; + text-decoration: none; +} + +.eol-primary a:hover { + color: white; + text-decoration: underline; } /* Adding card styling */ diff --git a/decentralisation/index.html b/decentralisation/index.html index 35020d335..2e7f269f8 100644 --- a/decentralisation/index.html +++ b/decentralisation/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/fetch-wallet/account_management/index.html b/fetch-wallet/account_management/index.html index e3763cd89..5512a8e69 100644 --- a/fetch-wallet/account_management/index.html +++ b/fetch-wallet/account_management/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/fetch-wallet/address_book/index.html b/fetch-wallet/address_book/index.html index 838005780..ae3e58988 100644 --- a/fetch-wallet/address_book/index.html +++ b/fetch-wallet/address_book/index.html @@ -21,7 +21,7 @@ - + @@ -102,12 +102,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/fetch-wallet/connections/index.html b/fetch-wallet/connections/index.html index 148d94c34..cb193df93 100644 --- a/fetch-wallet/connections/index.html +++ b/fetch-wallet/connections/index.html @@ -21,7 +21,7 @@ - + @@ -102,12 +102,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/fetch-wallet/deposit/index.html b/fetch-wallet/deposit/index.html index fa8235b88..e38945680 100644 --- a/fetch-wallet/deposit/index.html +++ b/fetch-wallet/deposit/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/fetch-wallet/index.html b/fetch-wallet/index.html index d324363dd..0bbc1c67f 100644 --- a/fetch-wallet/index.html +++ b/fetch-wallet/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/fetch-wallet/migrate_erc20/index.html b/fetch-wallet/migrate_erc20/index.html index 8b0e87b73..72b945b42 100644 --- a/fetch-wallet/migrate_erc20/index.html +++ b/fetch-wallet/migrate_erc20/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/fetch-wallet/send_tokens/index.html b/fetch-wallet/send_tokens/index.html index 141250680..1ef753f48 100644 --- a/fetch-wallet/send_tokens/index.html +++ b/fetch-wallet/send_tokens/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/fetch-wallet/stake/index.html b/fetch-wallet/stake/index.html index fa1513dfb..e7ac37ea1 100644 --- a/fetch-wallet/stake/index.html +++ b/fetch-wallet/stake/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/fund_form/index.html b/fund_form/index.html index a7bec2b04..45aba2217 100644 --- a/fund_form/index.html +++ b/fund_form/index.html @@ -21,7 +21,7 @@ - + @@ -102,12 +102,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/index.html b/index.html index 011fc4704..c115934ab 100644 --- a/index.html +++ b/index.html @@ -19,7 +19,7 @@ - + @@ -105,12 +105,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/learn_the_concepts/agent-based_and_multi-agents_systems/index.html b/learn_the_concepts/agent-based_and_multi-agents_systems/index.html index b7b151cba..3e535d2e5 100644 --- a/learn_the_concepts/agent-based_and_multi-agents_systems/index.html +++ b/learn_the_concepts/agent-based_and_multi-agents_systems/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/learn_the_concepts/blockchains/consensus/index.html b/learn_the_concepts/blockchains/consensus/index.html index fae03c1f0..1b8c213bc 100644 --- a/learn_the_concepts/blockchains/consensus/index.html +++ b/learn_the_concepts/blockchains/consensus/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/learn_the_concepts/blockchains/decentralized_applications/index.html b/learn_the_concepts/blockchains/decentralized_applications/index.html index 2ff948003..45ff73c36 100644 --- a/learn_the_concepts/blockchains/decentralized_applications/index.html +++ b/learn_the_concepts/blockchains/decentralized_applications/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/learn_the_concepts/blockchains/oracles/index.html b/learn_the_concepts/blockchains/oracles/index.html index 51407226b..f27b78b43 100644 --- a/learn_the_concepts/blockchains/oracles/index.html +++ b/learn_the_concepts/blockchains/oracles/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/learn_the_concepts/blockchains/smart_contracts/index.html b/learn_the_concepts/blockchains/smart_contracts/index.html index 1ad2c9d07..72d33f7b9 100644 --- a/learn_the_concepts/blockchains/smart_contracts/index.html +++ b/learn_the_concepts/blockchains/smart_contracts/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/learn_the_concepts/blockchains/transaction_fees/index.html b/learn_the_concepts/blockchains/transaction_fees/index.html index 38a724154..7b35de0e4 100644 --- a/learn_the_concepts/blockchains/transaction_fees/index.html +++ b/learn_the_concepts/blockchains/transaction_fees/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/learn_the_concepts/blockchains/validators/index.html b/learn_the_concepts/blockchains/validators/index.html index b8740969b..9fb6453c0 100644 --- a/learn_the_concepts/blockchains/validators/index.html +++ b/learn_the_concepts/blockchains/validators/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/learn_the_concepts/blockchains/what_is_a_blockchain/index.html b/learn_the_concepts/blockchains/what_is_a_blockchain/index.html index 5df541294..412560949 100644 --- a/learn_the_concepts/blockchains/what_is_a_blockchain/index.html +++ b/learn_the_concepts/blockchains/what_is_a_blockchain/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/learn_the_concepts/glossary/index.html b/learn_the_concepts/glossary/index.html index f052f5709..64ad35320 100644 --- a/learn_the_concepts/glossary/index.html +++ b/learn_the_concepts/glossary/index.html @@ -21,7 +21,7 @@ - + @@ -102,12 +102,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/learn_the_concepts/peer_to_peer_systems/index.html b/learn_the_concepts/peer_to_peer_systems/index.html index 96f37d247..4707c605b 100644 --- a/learn_the_concepts/peer_to_peer_systems/index.html +++ b/learn_the_concepts/peer_to_peer_systems/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ diff --git a/ledger-subquery/introduction/index.html b/ledger-subquery/introduction/index.html index a82c514bb..e680397ca 100644 --- a/ledger-subquery/introduction/index.html +++ b/ledger-subquery/introduction/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - +
+
+ We are moving! This documentation is migrating to https://fetch.ai/docs +
+

+ We will be not accepting any further updates to this page. We will keep this site up and running for a few weeks until all the content has been migrated. After which, users will be automatically redirected to the new docs site. +

+
+ @@ -8543,89 +8547,89 @@

FilteringExamples

Filtering NativeTransfers for a given sender address:

-
query nativeTransfersFromAddress {
-  nativeTransfers(first: 5, filter: {
-    fromAddress: {
-      equalTo: "fetch1t3qet68dr0qkmrjtq89lrx837qa2t05265qy6s"
-    }
-  }) {
-    nodes {
-      toAddress
-      amounts
-    }
-  }
-}
+
query nativeTransfersFromAddress {
+  nativeTransfers(first: 5, filter: {
+    fromAddress: {
+      equalTo: "fetch1t3qet68dr0qkmrjtq89lrx837qa2t05265qy6s"
+    }
+  }) {
+    nodes {
+      toAddress
+      amounts
+    }
+  }
+}
 

Filtering for Messages from a given sender address:

-
query messagesFromAddress {
-  messages (first: 5, filter:  {
-    transaction: {
-      signerAddress: {
-        equalTo: "fetch1t3qet68dr0qkmrjtq89lrx837qa2t05265qy6s"
-      }
-    }
-  }) {
-    nodes {
-      transaction {
-        signerAddress
-      }
-    }
-  }
-}
+
query messagesFromAddress {
+  messages (first: 5, filter:  {
+    transaction: {
+      signerAddress: {
+        equalTo: "fetch1t3qet68dr0qkmrjtq89lrx837qa2t05265qy6s"
+      }
+    }
+  }) {
+    nodes {
+      transaction {
+        signerAddress
+      }
+    }
+  }
+}
 

Filtering on Eventss within a given timeframe and with a given type:

-
query transferEventsDuring {
-  events(first: 5, filter:  {
-    block: {
-      timestamp: {
-        greaterThanOrEqualTo: "2022-09-15T01:44:13.719",
-        lessThanOrEqualTo: "2022-09-19T02:15:28.632"
-      }
-    },
-    type: {equalTo: "transfer"},
-  }) {
-    nodes {
-      attributes {
-        nodes {
-          key
-          value
-        }
-      }
-    }
-  }
-}
+
query transferEventsDuring {
+  events(first: 5, filter:  {
+    block: {
+      timestamp: {
+        greaterThanOrEqualTo: "2022-09-15T01:44:13.719",
+        lessThanOrEqualTo: "2022-09-19T02:15:28.632"
+      }
+    },
+    type: {equalTo: "transfer"},
+  }) {
+    nodes {
+      attributes {
+        nodes {
+          key
+          value
+        }
+      }
+    }
+  }
+}
 

Order by / Sorting

Each entity, by default, can be sorted by any of its respective fields. Additional support for ordering by certain fields on related entities is facilitated by custom ordering plugins generated from makeAddPgTableOrderByPlugin (see: postgraphile-docs).

Block height

Any entity which relates to Block can be ordered by a related block's height field: -

query contractExecByBlockHeight {
-  contractExecutionMessage (orderBy: EXECUTE_CONTRACT_MESSAGES_BY_BLOCK_HEIGHT_ASC) {
-    nodes {
-      id,
-      ...
-      Block {
-        height
-      }
-    }
-  }
-}
+
query contractExecByBlockHeight {
+  contractExecutionMessage (orderBy: EXECUTE_CONTRACT_MESSAGES_BY_BLOCK_HEIGHT_ASC) {
+    nodes {
+      id,
+      ...
+      Block {
+        height
+      }
+    }
+  }
+}
 

Contract Code ID

The contract entity can be sorted by codeId through the storeMessage and instantiateMessage relations. -

query contractsByRelatedCodeID {
-  contracts (orderBy: CONTRACTS_BY_STORE_CONTRACT_MESSAGES_CODE_ID_ASC) {
-    #  or CONTRACTS_BY_INSTANTIATE_CONTRACT_MESSAGES_CODE_ID_ASC
-    nodes {
-      id,
-      ...
-      storeMessage {
-        codeId
-      }
-    }
-  }
-}
+
query contractsByRelatedCodeID {
+  contracts (orderBy: CONTRACTS_BY_STORE_CONTRACT_MESSAGES_CODE_ID_ASC) {
+    #  or CONTRACTS_BY_INSTANTIATE_CONTRACT_MESSAGES_CODE_ID_ASC
+    nodes {
+      id,
+      ...
+      storeMessage {
+        codeId
+      }
+    }
+  }
+}
 

Order direction

Each of these custom orders are implemented in both directions, ascending and descending. These directions are accessed through the ending characters of the order enum, by choosing either _ASC and _DESC.

@@ -8662,12 +8666,12 @@

Entity relationship diagramsVersioning

The versions of both the GraphQL API and the Indexer itself can be retrieved simply using the following query on the GraphQL playground.

Example:
-
query ReleaseVersionTest {
-  _metadata {
-    queryNodeVersion
-    indexerNodeVersion
-  }
-}
+
query ReleaseVersionTest {
+  _metadata {
+    queryNodeVersion
+    indexerNodeVersion
+  }
+}
 

Each of these version numbers are stored as the value to the key "version" within their relevant module package.json file. These files can be found in the docker/node-cosmos/ and subql/packages/query/ directories for the Indexer and GraphQL versions, respectively.

// The Indexer version number, taken from "docker/node-cosmos/package.json"
@@ -8695,12 +8699,12 @@ 

"_metadata" Entity

Example:

If a developer was curious about the chain-id or whether the Indexer has passed any health checks, using indexerHealthy, these values can be returned within the playground or otherwise connected projects. -

query ReleaseVersionTest {
-  _metadata {
-    chain
-    indexerHealthy
-  }
-}
+
query ReleaseVersionTest {
+  _metadata {
+    chain
+    indexerHealthy
+  }
+}
 

diff --git a/ledger_v2/archived-networks/index.html b/ledger_v2/archived-networks/index.html index 9bd4ef146..9f465a0df 100644 --- a/ledger_v2/archived-networks/index.html +++ b/ledger_v2/archived-networks/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - + +
diff --git a/ledger_v2/block-explorer/index.html b/ledger_v2/block-explorer/index.html index b33c1711f..2c51bb5ef 100644 --- a/ledger_v2/block-explorer/index.html +++ b/ledger_v2/block-explorer/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - + +
diff --git a/ledger_v2/building/index.html b/ledger_v2/building/index.html index 04b89d22f..00ae82fa6 100644 --- a/ledger_v2/building/index.html +++ b/ledger_v2/building/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - + +
diff --git a/ledger_v2/cli-bls/index.html b/ledger_v2/cli-bls/index.html index 45d797922..ee1527d17 100644 --- a/ledger_v2/cli-bls/index.html +++ b/ledger_v2/cli-bls/index.html @@ -17,7 +17,7 @@ - + @@ -103,12 +103,16 @@ - - - - Apply for the $150M Developer Fund - + +
diff --git a/ledger_v2/cli-governance/index.html b/ledger_v2/cli-governance/index.html index bed2c25e9..a5c53a277 100644 --- a/ledger_v2/cli-governance/index.html +++ b/ledger_v2/cli-governance/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - + +
diff --git a/ledger_v2/cli-introduction/index.html b/ledger_v2/cli-introduction/index.html index c65916d4c..1af808440 100644 --- a/ledger_v2/cli-introduction/index.html +++ b/ledger_v2/cli-introduction/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - + +
diff --git a/ledger_v2/cli-keys/index.html b/ledger_v2/cli-keys/index.html index 8d7b2852c..f8a6d8b81 100644 --- a/ledger_v2/cli-keys/index.html +++ b/ledger_v2/cli-keys/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - + +
diff --git a/ledger_v2/cli-multisig/index.html b/ledger_v2/cli-multisig/index.html index 48161421c..e3e69926e 100644 --- a/ledger_v2/cli-multisig/index.html +++ b/ledger_v2/cli-multisig/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - + + diff --git a/ledger_v2/cli-tokens/index.html b/ledger_v2/cli-tokens/index.html index 1ec5ddf3f..67d490349 100644 --- a/ledger_v2/cli-tokens/index.html +++ b/ledger_v2/cli-tokens/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - + + diff --git a/ledger_v2/delegator-guide-cli/index.html b/ledger_v2/delegator-guide-cli/index.html index 2c4d25a8b..bc10452d4 100644 --- a/ledger_v2/delegator-guide-cli/index.html +++ b/ledger_v2/delegator-guide-cli/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - + + diff --git a/ledger_v2/faucet/index.html b/ledger_v2/faucet/index.html index 2262c3d08..cdaf7fcd3 100644 --- a/ledger_v2/faucet/index.html +++ b/ledger_v2/faucet/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - + + diff --git a/ledger_v2/governance/index.html b/ledger_v2/governance/index.html index a12bf9c9d..a82824081 100644 --- a/ledger_v2/governance/index.html +++ b/ledger_v2/governance/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - + + diff --git a/ledger_v2/index.html b/ledger_v2/index.html index 4bc544d38..47ce4255d 100644 --- a/ledger_v2/index.html +++ b/ledger_v2/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - + + diff --git a/ledger_v2/joining-a-testnet/index.html b/ledger_v2/joining-a-testnet/index.html index 8dd5e732d..a1296d9e3 100644 --- a/ledger_v2/joining-a-testnet/index.html +++ b/ledger_v2/joining-a-testnet/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - + + diff --git a/ledger_v2/live-networks/index.html b/ledger_v2/live-networks/index.html index 950584fb4..07e034e93 100644 --- a/ledger_v2/live-networks/index.html +++ b/ledger_v2/live-networks/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - + + diff --git a/ledger_v2/single-node-network/index.html b/ledger_v2/single-node-network/index.html index 47ea3f3d7..3f580fc90 100644 --- a/ledger_v2/single-node-network/index.html +++ b/ledger_v2/single-node-network/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - + + diff --git a/ledger_v2/snapshots/index.html b/ledger_v2/snapshots/index.html index 2db6c57c0..a45b66ec1 100644 --- a/ledger_v2/snapshots/index.html +++ b/ledger_v2/snapshots/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - + + diff --git a/ledger_v2/state-sync/index.html b/ledger_v2/state-sync/index.html index 41eb82dbe..bb5432ba4 100644 --- a/ledger_v2/state-sync/index.html +++ b/ledger_v2/state-sync/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - + + diff --git a/ledger_v2/validators/overview/index.html b/ledger_v2/validators/overview/index.html index 90c86dd13..0ae035a73 100644 --- a/ledger_v2/validators/overview/index.html +++ b/ledger_v2/validators/overview/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - + + diff --git a/ledger_v2/validators/security/index.html b/ledger_v2/validators/security/index.html index 64e1bf974..2fbbcc18c 100644 --- a/ledger_v2/validators/security/index.html +++ b/ledger_v2/validators/security/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - + + diff --git a/ledger_v2/validators/setup/index.html b/ledger_v2/validators/setup/index.html index 155e5667f..3760a36a2 100644 --- a/ledger_v2/validators/setup/index.html +++ b/ledger_v2/validators/setup/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - + + diff --git a/ledger_v2/versions/index.html b/ledger_v2/versions/index.html index ee1a37009..8346640c8 100644 --- a/ledger_v2/versions/index.html +++ b/ledger_v2/versions/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - + + diff --git a/native_and_erc20/how_to_convert_fet/index.html b/native_and_erc20/how_to_convert_fet/index.html index 2486248f4..03ace7195 100644 --- a/native_and_erc20/how_to_convert_fet/index.html +++ b/native_and_erc20/how_to_convert_fet/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - + + diff --git a/native_and_erc20/native_and_erc20_fet/index.html b/native_and_erc20/native_and_erc20_fet/index.html index 9e822aefe..b3ce28c46 100644 --- a/native_and_erc20/native_and_erc20_fet/index.html +++ b/native_and_erc20/native_and_erc20_fet/index.html @@ -21,7 +21,7 @@ - + @@ -102,12 +102,16 @@ - - - - Apply for the $150M Developer Fund - + + diff --git a/native_and_erc20/reconciliation/index.html b/native_and_erc20/reconciliation/index.html index 4678bf0a1..fa251f975 100644 --- a/native_and_erc20/reconciliation/index.html +++ b/native_and_erc20/reconciliation/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - + + diff --git a/search/search_index.json b/search/search_index.json index 47c755fa1..0939ba206 100644 --- a/search/search_index.json +++ b/search/search_index.json @@ -1 +1 @@ -{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Welcome to Fetch.ai Ecosystem","text":""},{"location":"#what-is-fetchai","title":"What is Fetch.ai?","text":"

Our mission is to build the infrastructure required for developing modern, decentralized and peer-to-peer (P2P) applications that are free from centralized rent-seeking.

We achieve this by providing open-source software tools which you can use together with our interoperable decentralized network, to harness the power of AI and automation, and carry out complex tasks in the digital economy.

"},{"location":"#get-started","title":"Get Started","text":"

The fetch ecosystem is made up of various tools and frameworks that help you build and jump-start your very own decentralized applications. Select one below to dive right in!

User facing tools

AEA Registry AEA Manager Fetch Wallet Explorer

Tools that use the chain

AEAFramework ACN \u03bcAgents

Tools to directly interact with the chain

CosmPy Jenesis

The chain

Ledger Cosmwasm

You can also jump straight into our GitHub repos:

  • Fetch Wallet
  • AEA Framework (& ACN)
  • \u03bcAgents
  • CosmPy
  • Jenesis
  • Ledger (fetchd)
"},{"location":"#bug-reports-feature-requests","title":"Bug Reports & Feature Requests","text":"

If you want to report a bug or request a feature:

  • AEA Framework
  • \u03bcAgents
  • Cosmpy
  • Jenesis
  • Ledger (fetchd)
  • Documentation

Want to report a security vulnerability? Visit our Bug Bounty.

"},{"location":"#questions-general-discussions","title":"Questions & General Discussions","text":"

Visit fetch.ai developer forums:

  • Fetch Wallet
  • AEA Framework
  • \u03bcAgents
  • CosmPy
  • Jenesis
  • Ledger (fetchd)

Chat with Fetch.ai developers on discord.

"},{"location":"bug_bounty/","title":"Bug Bounty Program","text":"

The Fetch bug bounty program provides incentives for developers and security experts to report vulnerabilities in the Fetch ecosystem.

The Fetch team has undertaken risk mitigation measures to limit the potential impacts of bugs or intentional misuse of the software and to ensure that all software has been internally audited and thoroughly tested.

In addition to the implemented safety measures, we are offering a bug bounty for the core components of the Fetch ecosystem as outlined under the Scope section below. The bug bounty program ensures that the software lives up to the highest standards possible and that the risk of users losing funds is at a minimum.

"},{"location":"bug_bounty/#scope","title":"Scope","text":"

The scope of the bounty program extends to:

  • FetchD: https://github.com/fetchai/fetchd
  • Fetch Wallet: https://github.com/fetchai/fetch-wallet
  • The Block Explorer: https://github.com/fetchai/cosmos-explorer
  • Documentation:: https://github.com/fetchai/docs
  • CosmPy: https://github.com/fetchai/cosmpy
  • AEA Framework, including the ACN and packages authored by Fetchai: https://github.com/fetchai/agents-aea and https://pypi.org/project/aea
  • AEA Manager Website: https://github.com/fetchai/agents-manager-app-site
"},{"location":"bug_bounty/#classification","title":"Classification","text":""},{"location":"bug_bounty/#critical-bugs-awards-up-to-20000-fet-tokens","title":"Critical Bugs (awards up to 20,000 FET tokens)","text":"

Critical bugs are those that result in loss of funds or lead to a lack of availability of the network. This may be as a result of vulnerabilities found in the deployed and supported versions of the blockchain client, smart contracts or any of the other software outlined within the Scope section.

"},{"location":"bug_bounty/#non-critical-bugs-awards-up-to-10000-fet-tokens","title":"Non-critical Bugs (awards up to 10,000 FET tokens)","text":"

Non-critical bugs are those that cannot cause loss of funds or any other type of economic loss. These types of bugs affect the experience of developers or users of the network and have a perceived or Fetch suggested workaround.

Awards are issued subject to reclassification and verification by the Fetch team.

"},{"location":"bug_bounty/#how-to-report","title":"How to Report","text":"

Please follow the steps listed below to report your bug:

  • In an email, describe the issue clearly with reference to the underlying source code and indicate whether the bug is Critical or Non-critical.
  • Attach all relevant information that is required to reproduce the bug in a test environment.
  • Include the relevant version information associated with the faulty software of the components along with any other relevant system information such as OS versions.
  • Include suggested solutions and/or mitigations (if known).
  • Send this email to bounty@fetch.ai and start the subject with your classification Critical or Non-critical followed by a short title of the bug.

The Fetch team will review your information and your classification of the bug. After reviewing, one of the Fetch developers will set out to reply within 2 working days to confirm whether the bug meets the requirements of the bug bounty program or to request more time to complete this assessment. The Fetch team will also post updates on the #bugs channel on our Discord server: https://discord.gg/M9XmgyWzup.

For non-critical bugs, the Fetch team will create an issue or a pull request allowing you to follow the progress on the bug fix.

For critical bugs that can result in loss of funds, it is important that the Fetch team has an opportunity to deploy a patched version before the exploit is acknowledged publicly. Hence, critical bugs and their fixes will be shared after the code is patched to prevent the targeting of such exploits.

"},{"location":"bug_bounty/#terms-and-conditions","title":"Terms and Conditions","text":"

These include, but are not limited to:

  • Bounty awards are made at the sole discretion of Fetch.ai and are subject to change and verification.
  • We will make every attempt to respond to all submissions promptly and to provide rewards in a timely manner but do not make any guarantees as to how long the processing of claims will take.
  • All users warrant that they are legally able to receive bounties. More specifically: they are of the appropriate age, the work they are submitting is their own and that they are resident in a territory that allows payment of such rewards.
  • Submitters must be willing to undergo any Know Your Customer (KYC) or Anti-Money Laundering (AML) checks as required.
  • This program is not open to Fetch.ai employees or contractors, past or present.
  • Fetch.ai reserves the right to alter or discontinue the Bug Bounty Program without notice.
"},{"location":"decentralisation/","title":"Decentralization","text":""},{"location":"decentralisation/#what-are-decentralized-systems","title":"What are Decentralized Systems?","text":"

A decentralized system is an interconnected system if no single entity (individual, organization, or group) is the sole authority. In a decentralized system, control and decision-making is in the hands of those participating in the system, and they have (more or less) equal standing in terms of influence. This is in contrast to a centralized system where a single authority is above all others in terms of rights and privileges.

Example

A good example of a decentralized system is an ant colony, where control is distributed among the colony's members. Without any form of central control, an ant colony successfully achieves its goals, performs all its required tasks, and effectively responds to the ever changing conditions on the colony's environment through the collective contribution of the individual ants.

Example

Another great example is when you fly internationally from one place to another. There are numerous participants working together and communicating efficiently to make that a seamless experience without there being a central unit that manages and plans it all.

There are two key characteristics of decentralized systems:

  • Equality of participants: all participants contribute towards the system's decision-making and control, and each of them takes an active role in managing the system.

  • Lack of central authorities: It is not that decentralized systems lack the authority to make changes to the management of the network, rather this authority is distributed among the participants. The system is run by the participants for the benefit of the participants.

"},{"location":"decentralisation/#decentralized-vs-distributed","title":"Decentralized vs Distributed","text":"

These are very easy to get confused, and sometimes it seems they are used interchangeably but they are actually quite different.

A decentralized system is one where there is a lack of central authorities.

A distributed system is a system in which the physical location of the participants, the servers, the nodes, and whatever other components involved are in different places. So instead of the system being physically all in one place, different parts of the system are located on physically different places and the processing of tasks are shared between them.

Example

The most obvious example of a distributed system is cloud computing, where tasks are shared among numerous computers or a database hosted on more than one computer.

What determines whether a system is decentralized or not is whether there are central authorities. What determines whether a system is distributed is whether the processing is done in one or multiple physical locations.

"},{"location":"decentralisation/#decentralized-vs-centralized","title":"Decentralized vs Centralized","text":"

In a centralized system, there is a central authority that manages the system, makes decisions about how it is run and exercises control over the other components of the system.

Example

Some examples of centralized systems:

  • Expedia: Hotel owners and people looking for hotel rooms connect to each other through Expedia, rather than communicating and transacting directly together.
  • Messaging services such as WhatsApp: People communicate with each other through a centrally controlled and owned messaging service.
  • Amazon: Buyers and sellers interact commercially through Amazon.
  • Client-server model: Clients interact with each other only through the server, a great example of this is email where communication between two users goes through a server.
"},{"location":"decentralisation/#why-are-decentralized-systems-useful","title":"Why are Decentralized Systems Useful?","text":"

Decentralized systems are useful because they are open and democratic, more transparent, and they are run solely in the interests of their participants by their participants.

"},{"location":"decentralisation/#benefits","title":"Benefits","text":"
  • Democratic: Decentralized systems require participation in order to successfully function, and therefore they are naturally democratic.

  • Open: Participants can freely join or leave as they wish. The system is open to new participants and does not stop existing ones leaving.

  • Public: Because decentralized systems need to be managed and run by their participants (democratic) and anyone can in principle join the system or leave (open), these systems are often public and their inner workings are not kept confidential.

  • Transparent: In order for the participants of a decentralized system to contribute to its liveliness, they must be made aware of how the system works, how it is managed, and so on.

  • Trust minimisation: participants do not have to go through central authorities to interact with others and achieve their goals. Hence, there is no need to trust central entities to handle your needs. Any guarantees needed by the system has to be provided by the system itself and the way it is designed, and (due to transparency) this is typically well known.

"},{"location":"decentralisation/#drawbacks","title":"Drawbacks","text":"
  • Speed: Decentralized systems are often slower than centralized systems. Because there is more than one decision maker, decisions have to be made by consensus in the group and arriving at that consensus takes more time than a single entity making the decision.

  • Efficiency: In decentralized systems, decision-making about the system requires participation. This makes it far slower to make changes to the system as it takes time for participants to agree to change. Also, because the system is open, participants can leave/join as they wish, then the system has to be designed to cope with an often changing number of participants. All this added complexity inevitably affects the efficiency of the system's operation.

  • Control: Because a decentralized system is often run collectively by its participants, controlling how the system is managed and the direction of its development is difficult.

  • Simplicity: For any system, it is often more complex to engineer a decentralized version with more than one decisions maker than a centralized one that achieves the same goals via central decision-makers and enforcers.

"},{"location":"fund_form/","title":"Developer Fund","text":"The Developer Fund

Following the announcement of a new development fund for growing the Fetch.ai ecosystem, we are accepting applications from prospective projects, either from Cosmos or EVM, to build upon the Fetch.ai network or scale using it's tools.

Please fill the form below to apply for the developer grant.

Name * Project/Company Name [If available] * Contact Email (if you're applying on behalf of a project or a company, please enter the email of the main point of contact) * Project links [Please provide Github, Website, Twitter if available] * Which category does your project fall under? * dApps On-chain analytics Automation DeFi Infrastructure NFTs Other: Project description [Please outline in detail the project you're seeking to receive a grant for] * Have you received any funding previously? * Yes: No Please share any details on the project tokenomics [if available] * What do you want to achieve through your project? * Project development timelines * Project Milestones * What is the funding amount you are looking for? Less than $50,000 Between $50,000 to $150,000 More than $150,000 Funding request breakdown [Please describe how the funds will be used] * Additional attachments [Please share links - e.g dropbox/google drive] * Submit"},{"location":"CosmPy/","title":"Getting started","text":"

Cosmpy is a Python library for interacting with Cosmos-based blockchains.

  • A simplified command line tool for querying and sending transactions to Cosmos-SDK blockchains.
  • Features an easy interface for deploying and interacting with Cosmwasm smart contracts.
  • Provides access to lower-level ledger APIs for advanced use-cases.
"},{"location":"CosmPy/#to-install","title":"To install","text":"
pip3 install cosmpy\n
"},{"location":"CosmPy/#version","title":"Version","text":"

.

"},{"location":"CosmPy/#repository","title":"Repository","text":"
https://github.com/fetchai/cosmpy\n
"},{"location":"CosmPy/#to-contribute","title":"To contribute","text":"

Clone the repo:

git clone https://github.com/fetchai/cosmpy.git --recursive && cd cosmpy\n

Set up development environment:

make new_env_dev\n

This creates a new pipenv virtual environment and installs the development dependencies.

Enter the virtual environment:

pipenv shell\n
"},{"location":"CosmPy/auto-compounder/","title":"Stake Auto-Compounder","text":"

When an account delegates tokens to a network's validator, it will start generating rewards proportional to the amount of Stake delegated. But since rewards aren't automatically added to your stake and therefore don't contribute to future rewards, we can perform a compounding strategy to generate exponential rewards.

"},{"location":"CosmPy/auto-compounder/#delegate","title":"Delegate","text":"

The first thing we need to do is delegate some tokens to a validator. You can do so by using a Wallet and specifying the validator address and amount.

validators = ledger_client.query_validators()\n# choose any validator\nvalidator = validators[0]\nkey = PrivateKey(\"FX5BZQcr+FNl2usnSIQYpXsGWvBxKLRDkieUNIvMOV7=\")\nwallet = LocalWallet(key)\n# delegate some tokens to this validator\ntx = ledger_client.delegate_tokens(validator.address, 9000000000000000000, wallet)\ntx.wait_to_complete()\n
"},{"location":"CosmPy/auto-compounder/#auto-compounder","title":"Auto Compounder","text":"

Then we can construct a code that claims rewards and delegates the rewarded tokens back to the validator. This way we keep growing our Stake and therefore we generate compounded rewards. We first need to define the time limit and the compounding period.

It is important to note that each time an account performs a claim or a delegate transaction it has to pay certain fees, therefore the compounding period has to be long enough to generate sufficient rewards to exceed the fees that will be paid in each transaction.

# set time limit and compounding period in seconds\ntime_limit = 600\nperiod = 100\n
Finally, we start a timer that claims rewards and delegates them in each time period. Notice that in the code below we constructed a while loop that will be running until the timer exceeds the time limit. Each loop will last the time specified in period. We query the balance before and after claiming rewards to get the value of the reward after any fees. If the true reward value is positive, we delegate those tokens to the validator, if it is negative, it means that the fees from claiming and delegating transactions exceeded the rewards, and therefore we won't delegate.

time_check = 0\nstart_time = time.monotonic()\ntime.sleep(period)\n# query, claim and delegate rewards after time period\nwhile time_check < time_limit:\nbegin = time.monotonic()\nsummary = ledger_client.query_staking_summary(wallet.address())\nprint(f\"Staked: {summary.total_staked}\")\nbalance_before = ledger_client.query_bank_balance(wallet.address())\ntx = ledger_client.claim_rewards(validator.address, wallet)\ntx.wait_to_complete()\nbalance_after = ledger_client.query_bank_balance(wallet.address())\n# reward after any fees\ntrue_reward = balance_after - balance_before\nif true_reward > 0:\nprint(f\"Staking {true_reward} (reward after fees)\")\ntx = ledger_client.delegate_tokens(validator.address, true_reward, wallet)\ntx.wait_to_complete()\nelse:\nprint(\"Fees from claim rewards transaction exceeded reward\")\nend = time.monotonic()\ntime.sleep(period-(end-begin))\ntime_check = time.monotonic() - start_time\n

You can view the full python example at staking auto-compounder

"},{"location":"CosmPy/connect-to-network/","title":"Connect to a network","text":"

To start interacting with a blockchain, you first need to establish a connection to a network node. You can use LedgerClient as a client object which takes a NetworkConfig as an argument.

from cosmpy.aerial.client import LedgerClient, NetworkConfig\nledger_client = LedgerClient(NetworkConfig.fetch_mainnet())\n

For convenience, some networks' configurations are provided automatically. For example, NetworkConfig.fetch_mainnet() is the configuration for the Fetch ledger. If you want to interact with other chains, you can customise NetworkConfig as shown in the example below:

cfg = NetworkConfig(\nchain_id=\"cosmoshub-4\",\nurl=\"grpc+https://grpc-cosmoshub.blockapsis.com:429\",\nfee_minimum_gas_price=1,\nfee_denomination=\"uatom\",\nstaking_denomination=\"uatom\",\n)\nledger_client = LedgerClient(cfg)\n

A full list of chain identifiers, denominations and end-points can be found at the Cosmos chain registry.

"},{"location":"CosmPy/deploy-a-contract/","title":"Deploy a contract","text":"

You can deploy smart contracts in CosmPy using LedgerContract. For this, you will need the path to where the contract is stored (in this case simple.wasm), a LedgerClient and a Wallet:

from cosmpy.aerial.contract import LedgerContract\nPATH = \"contracts/simple/simple.wasm\"\ncontract = LedgerContract(PATH, ledger_client)\ncontract.deploy({}, wallet)\n

You can now start interacting with the contract. To get the address of where the contract is deployed on the network:

print(f\"Contract deployed at: {contract.address}\")\n

You can query the values of the contract's state variables:

result = contract.query({\"get\": {\"owner\": wallet}})\nprint(\"Initial state:\", result)\n

You can also set these values. The following sets the state variable value to foobar:

contract.execute({\"set\": {\"value\": \"foobar\"}}, wallet).wait_to_complete()\n

Let's check if this was set correctly:

result = contract.query({\"get\": {\"owner\": wallet)}})\nprint(\"State after set:\", result)\n

Similarly, you can clear the state variables:

contract.execute({\"clear\": {}}, wallet).wait_to_complete()\nresult = contract.query({\"get\": {\"owner\": wallet}})\nprint(\"State after clear:\", result)\n
"},{"location":"CosmPy/liquidity-pool/","title":"Liquidity Pool","text":""},{"location":"CosmPy/liquidity-pool/#swap-tokens","title":"Swap Tokens","text":"

You can interact with a liquidity pool by swapping atestfet for CW20 tokens or vice versa. First, perform all the necessary imports:

import base64\nfrom cosmpy.aerial.client import LedgerClient, NetworkConfig\nfrom cosmpy.aerial.contract import LedgerContract\nfrom cosmpy.aerial.faucet import FaucetApi\nfrom cosmpy.aerial.wallet import LocalWallet\n
Set the network configuration, define a local wallet and add some tokens to it using the FaucetApi

# Network configuration\nledger = LedgerClient(NetworkConfig.latest_stable_testnet())\n# Define any wallet\nwallet = LocalWallet.generate()\n# Add tokens to wallet\nfaucet_api = FaucetApi(NetworkConfig.latest_stable_testnet())\nfaucet_api.get_wealth(wallet.address())\n
Define the CW20, pair, and liquidity token contracts with the following addresses:

# Define cw20, pair and liquidity token contracts\ntoken_contract_address = (\n\"fetch1qr8ysysnfxmqzu7cu7cq7dsq5g2r0kvkg5e2wl2fnlkqss60hcjsxtljxl\"\n)\npair_contract_address = (\n\"fetch1vgnx2d46uvyxrg9pc5mktkcvkp4uflyp3j86v68pq4jxdc8j4y0s6ulf2a\"\n)\nliq_token_contract_address = (\n\"fetch1alzhf9yhghud3qhucdjs895f3aek2egfq44qm0mfvahkv4jukx4qd0ltxx\"\n)\ntoken_contract = LedgerContract(\npath=None, client=ledger, address=token_contract_address\n)\npair_contract = LedgerContract(\npath=None, client=ledger, address=pair_contract_address\n)\nliq_token_contract = LedgerContract(\npath=None, client=ledger, address=liq_token_contract_address\n)\n

Swap the defined swap_amountof atestfet for CW20 tokens

# Swap atestfet for CW20 tokens\nswap_amount = \"10000\"\nnative_denom = \"atestfet\"\ntx = pair_contract.execute(\n{\n\"swap\": {\n\"offer_asset\": {\n\"info\": {\"native_token\": {\"denom\": native_denom}},\n\"amount\": swap_amount,\n}\n}\n},\nsender=wallet,\nfunds=swap_amount + native_denom,\n)\ntx.wait_to_complete()\n

You can query your CW20 balance using the following code:

token_contract.query({\"balance\": {\"address\": str(wallet.address())}})\n

To trade 10 CW20 tokens for atestfet you can use the following:

tx = token_contract.execute({\n\"send\": {\n\"contract\": pair_contract_address,\n\"amount\": \"10\",\n\"msg\": \"eyJzd2FwIjp7fX0=\"\n}\n},wallet)\ntx.wait_to_complete()\n
"},{"location":"CosmPy/liquidity-pool/#add-and-remove-liquidity","title":"Add and Remove Liquidity","text":"

You need to increase your wallet's allowance to provide CW20 tokens to the liquidity pool. You don't need to increase the allowance to provide atestfet

# Set the amount of CW20 tokens to be added to liquidity pool\ncw20_liquidity_amount = \"100\"\n# Increase allowance\ntx = token_contract.execute(\n{\n\"increase_allowance\": {\n\"spender\": pair_contract_address,\n\"amount\": cw20_liquidity_amount,\n\"expires\": {\"never\": {}},\n}\n},\nwallet,\n)\ntx.wait_to_complete()\n
To set the amount of atestfet to be added to the liquidity pool and not influence the existing token prices, we need to choose an amount that matches the atestfet:CW20 token ratio already existing in the pool. For this reason, we will query the pair_contract pool to observe the atestfet:CW20 token ratio

# Query Liquidity Pool\npair_contract.query({\"pool\": {}})\n

At the moment the code was run, the ratio was close to 247:10 atestfet:CW20, and since we defined above the amount of CW20 tokens to provide to the liquidity pool as 100, we will match the LP pool ratio by setting the atestfet amount as 2470. It will be difficult to exactly match the current ratio of the pool, but when adding liquidity to the pool, there is a slippage_tolerance parameter that allows a certain percentage change in the price.

# Set the amount of atestfet tokens to be added to liquidity pool\nnative_liquidity_amount = \"2470\"\n# Provide Liquidity\n# Liquidity should be added so that the slippage tolerance parameter isn't exceeded\ntx = pair_contract.execute(\n{\n\"provide_liquidity\": {\n\"assets\": [\n{\n\"info\": {\"token\": {\"contract_addr\": token_contract_address}},\n\"amount\": cw20_liquidity_amount,\n},\n{\n\"info\": {\"native_token\": {\"denom\": native_denom}},\n\"amount\": native_liquidity_amount,\n},\n],\n\"slippage_tolerance\":\"0.1\"\n}\n},\nsender=wallet,\nfunds=native_liquidity_amount + native_denom,\n)\ntx.wait_to_complete()\n

When providing liquidity, you are rewarded with newly minted LP tokens. LP tokens represent the liquidity provider's share in the pool. You can burn your LP tokens to withdraw your share from the liquidity pool, for more information visit Terraswap. The following code shows how to withdraw your share from the LP.

# Query your LP token balance to burn it all\nLP_token_balance = liq_token_contract.query({\"balance\": {\"address\": str(wallet.address())}})[\"balance\"]\n# Convert the withdrawal msg to base64\nwithdraw_msg = '{\"withdraw_liquidity\": {}}'\nwithdraw_msg_bytes = withdraw_msg.encode(\"ascii\")\nwithdraw_msg_base64 = base64.b64encode(withdraw_msg_bytes)\nmsg = str(withdraw_msg_base64)[2:-1]\n# Withdraw Liquidity\ntx = liq_token_contract.execute(\n{\n\"send\": {\n\"contract\": pair_contract_address,\n\"amount\": LP_token_balance,\n\"msg\": msg,\n}\n},\nsender=wallet,\n)\ntx.wait_to_complete()\n

You can now query you LP token balance to observe that it has gone down to zero

liq_token_contract.query({\"balance\": {\"address\": str(wallet.address())}})\n

You can also check the full code example at liquidity-pool

"},{"location":"CosmPy/low-level-api/","title":"Low-level API","text":"

The Cosmpy library provides a high-level API which greatly simplifies the most common use cases when interacting with Cosmos-based chains (e.g. sending tokens, staking, deploying and interacting with contracts). There are documentation and example code covering such use cases.

However, cosmpy also provides low-level access to the entire Cosmos-SDK, enabling the full gamut of functionality to be accessed, albeit with a little more boilerplate.

Here, we aim to help developers navigate the low-level, protobuf-based API functionality, provided by Cosmpy.

"},{"location":"CosmPy/low-level-api/#recap-high-level-api-aerial","title":"Recap: High Level API - Aerial","text":"

As a reminder, here is a quick example of using the high level functionality provided by Cosmpy. In this case, we connect to a testnet, create a wallet, stake some tokens with a validator, then claim our rewards:

from cosmpy.aerial.client import LedgerClient, NetworkConfig\nfrom cosmpy.aerial.wallet import LocalWallet\nfrom cosmpy.crypto.keypairs import PrivateKey\nclient = LedgerClient(NetworkConfig.fetchai_dorado_testnet())\nwallet = LocalWallet(PrivateKey(\"rBDA3Q0vK5T+JVQmXSoooqUY/mSO4mmhMHQJI31+h1o=\"))\ntx = client.delegate_tokens(\"fetchvaloper1rsane988vksrgp2mlqzclmt8wucxv0ej4hrn2k\", 20, wallet)\ntx.wait_to_complete()\ntx = client.claim_rewards(\"fetchvaloper1rsane988vksrgp2mlqzclmt8wucxv0ej4hrn2k\", wallet)\ntx.wait_to_complete()\n

The available high-level helper functions provided by cosmpy can be found by browsing for instance the aerial client package.

"},{"location":"CosmPy/low-level-api/#low-level-api","title":"Low Level API","text":""},{"location":"CosmPy/low-level-api/#simple-messages","title":"Simple Messages","text":"

Not all Cosmos-SDK functionality is encapsulated in the high level aerial packages. In which case, it is necessary to locate and use the definition of the relevant protobuf message.

Analogous to the rewards claim example above, what if a validator operator wanted to claim their commission? At the time of writing, there is no high-level API to achieve this, so the low level API must be used.

In the protos directory, there is a MsgWithdrawValidatorCommission message, which is what we need. It takes a single validator_address parameter which is a utf-8 string.

To send a transaction containing such a message:

from cosmpy.aerial.client import LedgerClient, NetworkConfig\nfrom cosmpy.aerial.wallet import LocalWallet\nfrom cosmpy.aerial.tx import Transaction\nfrom cosmpy.aerial.client.utils import prepare_and_broadcast_basic_transaction\nfrom cosmpy.protos.cosmos.distribution.v1beta1.tx_pb2 import MsgWithdrawValidatorCommission\nfrom cosmpy.crypto.keypairs import PrivateKey\nclient = LedgerClient(NetworkConfig.fetchai_dorado_testnet())\nwallet = LocalWallet(PrivateKey(\"<redacted>private key of dorado validator0\"))\ntx = Transaction()\ntx.add_message(\nMsgWithdrawValidatorCommission(\nvalidator_address=\"fetchvaloper1rsane988vksrgp2mlqzclmt8wucxv0ej4hrn2k\"\n)\n)\ntx = prepare_and_broadcast_basic_transaction(client, tx, wallet)\ntx.wait_to_complete()\n
"},{"location":"CosmPy/low-level-api/#nested-messages","title":"Nested messages","text":"

The above example creates and broadcasts a simple MsgWithdrawValidatorCommission message. However, sometimes it is necessary to include one message in another. For example, what if we wanted to use the above message but execute it from a different account using authz (i.e. use an account which holds minimal funds, whose keys need not be treated with the same level of care as those of the validator itself)?

In this case, we'll need to send an authz MsgExec message, which can be found in tx_pb2.py under cosmos/authz area of cosmpy/protos. This message takes two parameters. The grantee is a simple string address similar to the above. But the msgs field needs to support multiple types of messages and not just MsgWithdrawValidatorCommission.

Protobuf is strongly typed, so to facilitate this flexibility, it is necessary to first pack the nested message into a protobuf.Any message.

Therefore, we arrive at the code looking like:

from cosmpy.aerial.client import LedgerClient, NetworkConfig\nfrom cosmpy.aerial.wallet import LocalWallet\nfrom cosmpy.aerial.tx import Transaction\nfrom cosmpy.aerial.client.utils import prepare_and_broadcast_basic_transaction\nfrom cosmpy.crypto.keypairs import PrivateKey\nfrom cosmpy.protos.cosmos.distribution.v1beta1.tx_pb2 import MsgWithdrawValidatorCommission\nfrom cosmpy.protos.cosmos.authz.v1beta1.tx_pb2 import MsgExec\nfrom google.protobuf import any_pb2\nclient = LedgerClient(NetworkConfig.fetchai_dorado_testnet())\nwallet = LocalWallet(PrivateKey(\"rBDA3Q0vK5T+JVQmXSoooqUY/mSO4mmhMHQJI31+h1o=\"))\nmsg = any_pb2.Any()\nmsg.Pack(\nMsgWithdrawValidatorCommission(\nvalidator_address=\"fetchvaloper1rsane988vksrgp2mlqzclmt8wucxv0ej4hrn2k\"\n),\n\"\",\n)\ntx = Transaction()\ntx.add_message(MsgExec(grantee=str(wallet.address()), msgs=[msg]))\ntx = prepare_and_broadcast_basic_transaction(client, tx, wallet)\ntx.wait_to_complete()\n
"},{"location":"CosmPy/low-level-api/#more-protobuf-examples","title":"More protobuf examples","text":"

Before running the above, the necessary authz grant must first be put in place. For Ledger Nano users (other hardware wallets are also available) that might mean an excursion to the command line. For the Fetchai network using FetchD:

fetchd tx authz grant $(fetchd keys show grantee --output json | jq -r .address) generic --msg-type \"/cosmos.distribution.v1beta1.MsgWithdrawValidatorCommission\" --from=$(fetchd keys show grantor --output json | jq -r .address) --gas auto --gas-adjustment 1.5 --gas-prices 5000000000atestfet\n

By default, the above provides one year's worth of authorization to withdraw validator commission using accounts already present in the keyring.

For those with access to their keys in python:

from cosmpy.aerial.client import LedgerClient, NetworkConfig\nfrom cosmpy.aerial.wallet import LocalWallet\nfrom cosmpy.aerial.tx import Transaction\nfrom cosmpy.aerial.client.utils import prepare_and_broadcast_basic_transaction\nfrom cosmpy.crypto.keypairs import PrivateKey\nfrom cosmpy.protos.cosmos.distribution.v1beta1.tx_pb2 import MsgWithdrawValidatorCommission\nfrom cosmpy.protos.cosmos.authz.v1beta1.tx_pb2 import MsgGrant\nfrom cosmpy.protos.cosmos.authz.v1beta1.authz_pb2 import GenericAuthorization, Grant\nfrom google.protobuf import any_pb2, timestamp_pb2\nfrom datetime import datetime, timedelta\nclient = LedgerClient(NetworkConfig.fetchai_dorado_testnet())\nwallet = LocalWallet(PrivateKey(\"rBDA3Q0vK5T+JVQmXSoooqUY/mSO4mmhMHQJI31+h1o=\"))\nvalidator = LocalWallet(PrivateKey(\"<redacted>private key of dorado validator0\"))\nauthz_any = any_pb2.Any()\nauthz_any.Pack(\nGenericAuthorization(\nmsg=\"/cosmos.distribution.v1beta1.MsgWithdrawValidatorCommission\"\n),\n\"\",\n)\nexpiry = timestamp_pb2.Timestamp()\nexpiry.FromDatetime(datetime.now() + timedelta(seconds=60))\ngrant = Grant(authorization=authz_any, expiration=expiry)\nmsg = MsgGrant(\ngranter=str(validator.address()),\ngrantee=str(wallet.address()),\ngrant=grant,\n)\ntx = Transaction()\ntx.add_message(msg)\ntx = prepare_and_broadcast_basic_transaction(client, tx, validator)\ntx.wait_to_complete()\n
"},{"location":"CosmPy/oracles/","title":"Oracles","text":"

Oracles are entities that can update state variables in smart contracts and whose goal is usually to accurately estimate or predict some real world quantity or quantities. These quantities can then be used in the logic of other smart contracts.

This guide shows how to write a CosmPy script that deploys and updates an oracle contract with a coin price, and another script that deploys a contract that queries this coin price.

"},{"location":"CosmPy/oracles/#preliminaries","title":"Preliminaries","text":"

We will need the binaries for both contracts, which can be downloaded as follows:

wget https://raw.githubusercontent.com/fetchai/agents-aea/develop/packages/fetchai/contracts/oracle/build/oracle.wasm\nwget https://raw.githubusercontent.com/fetchai/agents-aea/develop/packages/fetchai/contracts/oracle_client/build/oracle_client.wasm\n

The scripts also require the following imports:

from time import sleep\nimport requests\nfrom cosmpy.aerial.client import LedgerClient, NetworkConfig\nfrom cosmpy.aerial.contract import LedgerContract\nfrom cosmpy.aerial.wallet import LocalWallet\nfrom cosmpy.crypto.address import Address\nfrom cosmpy.crypto.keypairs import PrivateKey\n

"},{"location":"CosmPy/oracles/#oracle-deployer-and-updater","title":"Oracle deployer and updater","text":"

We first choose a data source for the coin price, the update interval, and the decimal precision for the oracle value:

COIN_PRICE_URL = (\n\"https://api.coingecko.com/api/v3/simple/price?ids=fetch-ai&vs_currencies=usd\"\n)\nUPDATE_INTERVAL_SECONDS = 10\nORACLE_VALUE_DECIMALS = 5\n

Next, we create a wallet and ledger interface to interact with the latest stable testnet:

wallet = LocalWallet(PrivateKey(\"T7w1yHq1QIcQiSqV27YSwk+i1i+Y4JMKhkpawCQIh6s=\"))\nledger = LedgerClient(NetworkConfig.fetchai_stable_testnet())\n

Create the LedgerContract object:

contract = LedgerContract(\"oracle.wasm\", ledger)\n

To deploy the oracle contract, add the fee amount to the instantiation message and call the deploy function:

instantiation_message = {\"fee\": \"100\"}\ncontract.deploy(instantiation_message, wallet, funds=\"1atestfet\")\nprint(f\"Oracle contract deployed at: {contract.address}\")\n

Save the oracle contract address to use for the oracle client script below (ORACLE_CONTRACT_ADDRESS).

As the deployer of the contract, we have permission to grant the oracle to a particular address. In this case, we'll grant the oracle role to our own wallet:

grant_role_message = {\"grant_oracle_role\": {\"address\": wallet)}}\ncontract.execute(grant_role_message, wallet).wait_to_complete()\n

Finally, start updating the contract with the coin price retrieved from the COIN_PRICE_URL:

while True:\nresp = requests.get(COIN_PRICE_URL).json()\nprice = resp[\"fetch-ai\"][\"usd\"]\nvalue = int(price * 10**ORACLE_VALUE_DECIMALS)\nupdate_message = {\n\"update_oracle_value\": {\n\"value\": str(value),\n\"decimals\": str(ORACLE_VALUE_DECIMALS),\n}\n}\ncontract.execute(update_message, wallet).wait_to_complete()\nprint(f\"Oracle value updated to: {price} USD\")\nprint(f\"Next update in {UPDATE_INTERVAL_SECONDS} seconds...\")\nsleep(UPDATE_INTERVAL_SECONDS)\n

For the complete example script, see aerial_oracle.py.

"},{"location":"CosmPy/oracles/#oracle-client","title":"Oracle client","text":"

Now we'll write a script that deploys a contract that can request the oracle value in exchange for the required fee.

We again start by creating a wallet and ledger interface in a new terminal session:

wallet = LocalWallet(PrivateKey(\"CI5AZQcr+FNl2usnSIQYpXsGWvBxKLRDkieUNIvMOV8=\"))\nledger = LedgerClient(NetworkConfig.fetchai_stable_testnet())\n

Set ORACLE_CONTRACT_ADDRESS to the address of the contract deployed in the previous script:

ORACLE_CONTRACT_ADDRESS = \"contract_address_goes_here\"\n

Next, we define the contract object, set the oracle contract address in the instantiation message, and deploy the contract:

contract = LedgerContract(\"oracle_client.wasm\", ledger)\ninstantiation_message = {\"oracle_contract_address\": str(ORACLE_CONTRACT_ADDRESS)}\ncontract.deploy(instantiation_message, wallet)\n

Finally, define a request interval and start a loop that executes the function that requests the oracle value:

REQUEST_INTERVAL_SECONDS = 10\nwhile True:\nrequest_message = {\"query_oracle_value\": {}}\ncontract.execute(\nrequest_message, wallet, funds=\"100atestfet\"\n).wait_to_complete()\nresult = contract.query({\"oracle_value\": {}})\nprint(f\"Oracle value successfully retrieved: {result}\")\nsleep(REQUEST_INTERVAL_SECONDS)\n

For the complete example script, see aerial_oracle_client.py.

"},{"location":"CosmPy/query-balance/","title":"Querying balances","text":"

A LedgerClient object can be used to query the balances associated with a particular address:

address: str = 'fetch12q5gw9l9d0yyq2th77x6pjsesczpsly8h5089x'\nbalances = ledger_client.query_bank_all_balances(address)\n

This will return a List of Coin objects that contain amount and denom variables that correspond to all the funds held at the address and their denominations. This list includes all natively defined coins along with any tokens transferred using the inter-blockchain communication (IBC) protocol.

>>> balances\n[Coin(amount='29263221445595384075', denom='afet')]\n

It's also possible to query the funds associated with a particular denomination by calling

balance = ledger_client.query_bank_balance(address, denom='afet')\n

which will return the value of the (integer) funds held by the address with the specified denomination. If the denom argument is omitted the function will return the fee denomination specified in the NetworkConfig object used to initialise the LedgerClient.

"},{"location":"CosmPy/send-tokens/","title":"Send tokens","text":"

Once you have your wallet configured, you can send transactions to the network. The LedgerClient object provides useful utilities to do common operations. The following example shows how to send 10 atestfet to another address:

destination_address = 'fetch1h2l3cnu7e23whmd5yrfeunacez9tv0plv5rxqy'\ntx = ledger_client.send_tokens(destination_address, 10, \"atestfet\", wallet)\n# block until the transaction has been successful or failed\ntx.wait_to_complete()\n
"},{"location":"CosmPy/stake-optimizer/","title":"Stake Optimizer","text":"

When you delegate tokens to a validator for a determined period, you can use the auto-compounder to get increasing rewards. You can maximize your rewards for a given staking period by selecting an optimal compounding period. To do this, you will need to follow these steps:

  • Set and Query Variables: When calculating staking rewards, you need to set and query variables such as staking parameters, transaction fees, and network parameters
  • Calculate Reward Rate: After you select and query all the variables needed, you will calculate the reward rate
  • Calculate Optimal Compounding Period: You will calculate the optimal compounding period that will maximize your rewards

First, you need to define a network to work with.

from cosmpy.aerial.client import LedgerClient\nfrom cosmpy.aerial.config import NetworkConfig\nledger = LedgerClient(NetworkConfig.fetchai_stable_testnet())\n
"},{"location":"CosmPy/stake-optimizer/#set-and-query-variables","title":"Set and Query Variables","text":""},{"location":"CosmPy/stake-optimizer/#staking-variables","title":"Staking Variables","text":"

First, we need to define the desired amount and the total period that we would like to stake in: initial_stake and total_period variables. Here we will stake 50 TESTFET for 60000 minutes. For this guide, we will work with minutes as a time unit.

initial_stake = 50000000000000000000\ntotal_period = 60000\n
"},{"location":"CosmPy/stake-optimizer/#validator-selection-and-variables","title":"Validator Selection and Variables","text":"

We will now select a validator to delegate our tokens. We will do this by analyzing which one has the lowest commission and a reasonable amount of stake delegated compared to the total stake.

from cosmpy.protos.cosmos.staking.v1beta1.query_pb2 import QueryValidatorsRequest\nreq = QueryValidatorsRequest()\nresp = ledger.staking.Validators(req)\n# Calculate the total stake currently in the testnet\n# Status = 3 means that the validator is bonded\nvalidators_stake = [int(validator.tokens) for validator in resp.validators if validator.status == 3]\ntotal_stake = sum(validators_stake)\n# For every bonded validator, we print commission and percentage of total stake\nprint(\"MONIKER      COMISSION   % of TOTAL STAKE\")\nfor validator in resp.validators:\nif validator.status == 3:\nmoniker = validator.description.moniker\ncomission = int(validator.commission.commission_rates.rate)/1e18*100\nprint(moniker[:10],\" \", comission,\"%     \", round(int(validator.tokens)/total_stake*100,3),\"%\")\n

After running the code above, you will observe each validator commission rate and its percentage delegated of the total stake. The most important parameter to observe in each validator is the commission it will take from the rewards. We will always select a validator with the lower commission as long as it has a reasonable stake compared with the total stake. In this case, at the moment the code was run, all validators had the same commission, therefore, we simply selected the validator with the highest stake, which was validator0. Feel free to select the most convenient validator when you run the code above. We will save the variables commission and the fraction of our initial_stake to the total stake to use them later on.

# get all the active validators on the network\nvalidators = ledger.query_validators()\n# Query info of selected validator\nselected_validator = \"validator0\"\nvalidator = [v for v in validators if v.moniker == selected_validator][0]\nquery_validator = [v for v in resp.validators if v.description.moniker == selected_validator][0]\n# Set the comission %\ncommission = int(query_validator.commission.commission_rates.rate)/1e18\n# Set percentage delegated of total stake\npct_delegated = initial_stake/total_stake\n
"},{"location":"CosmPy/stake-optimizer/#estimate-transaction-fees","title":"Estimate Transaction Fees","text":"

We need to know an estimate of the transaction fees it will cost every time we claim rewards and delegate tokens. For that, both claim rewards and delegate tokens transactions were combined into a single multi-msg transaction to simulate the total fees.

from cosmpy.aerial.client.distribution import create_withdraw_delegator_reward\nfrom cosmpy.aerial.client.staking import create_delegate_msg\nfrom cosmpy.aerial.tx import SigningCfg\nfrom cosmpy.aerial.wallet import LocalWallet\nfrom cosmpy.crypto.keypairs import PrivateKey\nfrom cosmpy.crypto.address import Address\nfrom cosmpy.aerial.tx import Transaction\n# Use any address with at least the amount of initial_stake available\nkey = PrivateKey(\"XZ5BZQcr+FNl2usnSIQYpXsGWvBxKLRDkieUNIvMOV7=\")\nalice = LocalWallet(key)\nalice_address = Address(key)._display\ntx = Transaction()\n# Add delegate msg\ntx.add_message(create_delegate_msg(alice_address,validator.address,initial_stake,\"atestfet\"))\n# Add claim reward msg\ntx.add_message(create_withdraw_delegator_reward(alice_address, validator.address))\naccount = ledger.query_account(alice.address())\ntx.seal(SigningCfg.direct(alice.public_key(), account.sequence),fee=\"\",gas_limit=0)\ntx.sign(alice.signer(), ledger.network_config.chain_id, account.number)\ntx.complete()\n# simulate the fee for the transaction\n_, str_tx_fee = ledger.estimate_gas_and_fee_for_tx(tx)\n
Since the output of this function is a string, we will convert it to an int and round it up to get a more conservative estimate for the fee

denom = \"atestfet\"\ntx_fee = str_tx_fee[:-len(denom)]\n# Add a 20% to the fee estimation to get a more conservative estimate\nfee = int(tx_fee) * 1.20\n
"},{"location":"CosmPy/stake-optimizer/#query-network-variables","title":"Query Network Variables","text":"

There are three network variables that we need to query since they will contribute to the staking rewards calculation: total_supply, inflation and community_tax

# Total Supply of tokens\nreq = QueryTotalSupplyRequest()\nresp = ledger.bank.TotalSupply(req)\ntotal_supply = float(json.loads(resp.supply[0].amount))\n# Inflation\nreq = QueryParamsRequest(subspace=\"mint\", key=\"InflationRate\") \nresp = ledger.params.Params(req)\ninflation = float(json.loads(resp.param.value))\n# Community Tax\nreq = QueryParamsRequest(subspace=\"distribution\", key=\"communitytax\") \nresp = ledger.params.Params(req)\ncommunity_tax = float(json.loads(resp.param.value))\n
"},{"location":"CosmPy/stake-optimizer/#calculate-reward-rate","title":"Calculate Reward Rate","text":"

We can now proceed to calculate a theoretical staking rewards rate using the variables gathered above. These are: inflation, total_supply, pct_delegated, community_tax and commission

# Calculate annual reward\nanual_reward = (inflation * total_supply) *pct_delegated* (1- community_tax)*(1- commission)\n# Convert from annual reward to minute reward\nminute_reward = anual_reward/360/24/60\n# Set the rate\nrate = minute_reward/initial_stake\n
"},{"location":"CosmPy/stake-optimizer/#calculate-optimal-compounding-period","title":"Calculate Optimal Compounding Period","text":"

We can calculate the optimal compounding period that maximizes our staking rewards analytically by using the following formula.

Where:

M = Total stake at time D

S= Initial Stake \\ f = Transaction Fee \\ k = Reward Rate

m = Number Of Compounding Transactions \\ n = Compounding Period

D = m x n = Total Staking Time

We will now find the value that maximizes reward by taking the first derivative with respect to n and finding the root in the interval (0,D]

import numpy as np\nfrom sympy.utilities.lambdify import lambdify, implemented_function\nfrom sympy import *\nfrom scipy.optimize import brentq\nf = fee\nS = initial_stake\nk = rate\nD = total_period\n# x will represent n\nx = Symbol(\"x\")\n# Define the function\nM = (S*(1+(k*x))**(D/x))+((1-((1+(k*x))**(D/x)))/(k*x))*f\nMx = lambdify(x,M)\n# Take the first derivative with respect to x\nM_prime = M.diff(x)\nMx_prime = lambdify(x,M_prime)\n# Find the maximum reward value by finding the root of the function\noptimal_period = brentq(Mx_prime, 0.1, D)\nprint(\"optimal_period: \", analytical_optimal_period, \" minutes\")\n
You can make use of the optimal_period value in the staking auto-compounder to maximize your rewards

You can also plot the function along with the optimal period to observe the results

import matplotlib.pyplot as plt\nplot = plt.figure(0,figsize=(6,4), dpi=100)\ny = np.linspace(1,300, 100)\nplt.plot(y,Mx(y),\"k\", label = 'analytical function')\nplt.axvline(x = optimal_period, color = 'g', linewidth = 2, label = f'optimal period: {round(optimal_period)}')\nplt.legend()\nplt.xlabel(\"Compounding periods\")\nplt.ylabel('Total Reward')\nplt.title('Maximizing Rewards')\nplt.grid()\n

Finally, we can compare the compounding staking rewards to a simple non-compounding strategy

# Compounding Strategy\ncomp_rewards = []\nrewards = 0\nperiod = optimal_period\nS = initial_stake\nfor i in range(total_period):\nrewards = rewards + (S*rate)\nif i%period == 0:\nS = S + rewards - fee\nrewards = 0\ncomp_rewards.append(S)\nS = S + rewards - (fee/2)\ncomp_rewards.append(S)\n# Simple Strategy\ns_reward = initial_stake*rate\nsimple_rewards = [initial_stake+(s_reward*i) for i in range(comp_period)]\n# Plots\nplot = plt.figure(0,figsize=(12,4), dpi=100)\nplt.subplot(1,2,1)\nplt.plot(comp_rewards, label = \"Compounded Rewards\")\nplt.plot(simple_rewards, label = \"Simple Rewards\")\nplt.xlabel(\"time in minutes\")\nplt.ylabel('Reward')\nplt.title('Staking Rewards')\nplt.legend()\nplt.subplot(1,2,2)\nplt.plot(total_rewards, label = \"Compounded Rewards\")\nplt.plot(simple_rewards, label = \"Simple Rewards\")\nplt.xlabel(\"time in minutes\")\nplt.ylabel('Reward')\nplt.title('Staking Rewards (log scale)')\nplt.legend()\nplt.yscale('log')\n

You can view an abbreviated version of the code at stake optimizer

"},{"location":"CosmPy/staking/","title":"Staking","text":"

A big part of the cosmos networks is staking. Staking is the process where you delegate your tokens to the network's validators in order to secure the network. There are three main actions you can take when staking:

  • Delegating: This is the process where you send your tokens to a chosen validator. They are applied immediately and you start earning rewards as soon as this transaction completes. The more tokens you stake, the more rewards you will earn.
  • Redelegating: This is the process where you transfer your staked tokens from one validator to another. This can be for many reasons, such as better returns, more trustworthiness, etc.
  • Undelegating: While your tokens are staked, you cannot spend them or send them to other users. To regain access to them, you must undelegate them. When you initiate this process, the funds will be removed from the validator they were delegated to, and must be left to cool down for a period of time (for example 21 days). After this period, the funds are automatically released into the user's wallet.
"},{"location":"CosmPy/staking/#actions","title":"Actions","text":"

LedgerClient provides useful utilities for interacting with the staking component of the network.

Note

For simplicity, the staking methods do not have an option for specifying the denom field. This is because in almost all networks, there is only one staking denomination. Therefore, the denomination used is the one specified in the NetworkConfig supplied to the LedgerClient object.

"},{"location":"CosmPy/staking/#delegate","title":"Delegate","text":"

To stake 20 tokens with the specific validator using a Wallet:

validator_address = 'fetchvaloper1e4ykjwcwhwtasqxq50d4m7xz9hh7a86e9y8h87'\ntx = ledger_client.delegate_tokens(validator_address, 20, wallet)\n# block until the transaction has been successful or failed\ntx.wait_to_complete()\n
"},{"location":"CosmPy/staking/#redelegate","title":"Redelegate","text":"

To redelegate 10 tokens from an existing validator (with the address validator_address) to another (with the address alternate_validator_address):

validator_address = 'fetchvaloper1e4ykjwcwhwtasqxq50d4m7xz9hh7a86e9y8h87'\nalternate_validator_address = 'fetchvaloper1e4ykjwcwhwtasqxq50d4m7xz9hh7a86e9y8h87'\ntx = ledger_client.redelegate_tokens(validator_address, alternate_validator_address, 10, wallet)\n# block until the transaction has been successful or failed\ntx.wait_to_complete()\n
"},{"location":"CosmPy/staking/#undelegate","title":"Undelegate","text":"

To undelegate 5 tokens and start the cool down process:

tx = ledger_client.undelegate_tokens(validator_address, 5, wallet)\n# block until the transaction has been successful or failed\ntx.wait_to_complete()\n

Note

The cool down is tracked for each invocation of undelegate action. So for example if you trigger 3 undelegate actions on 3 consecutive days. The first batch of tokens will become available 3 days before the final batch.

"},{"location":"CosmPy/staking/#claiming-rewards","title":"Claiming Rewards","text":"

While your funds are staked, you are earning rewards on them. Rewards can be collected at any time and unlike delegations, when collected they become immediately available.

To claim rewards from a specific validator:

tx = ledger_client.claim_rewards(validator_address, wallet)\n# block until the transaction has been successful or failed\ntx.wait_to_complete()\n
"},{"location":"CosmPy/staking/#queries","title":"Queries","text":""},{"location":"CosmPy/staking/#stake-summary","title":"Stake Summary","text":"

At any point you can query the stake information of any particular address. This can be done using the LedgerClient as shown in the example below:

address = 'fetch1h2l3cnu7e23whmd5yrfeunacez9tv0plv5rxqy'\ns = ledger_client.query_staking_summary(address)\nprint(f\"Summary: Staked: {s.total_staked} Unbonding: {s.total_unbonding} Rewards: {s.total_rewards}\")\n
"},{"location":"CosmPy/swap-automation/","title":"Swap Automation","text":"

A mean-reversion strategy expects the prices to return to \u201cnormal\u201d levels or a certain moving average following a temporary price spike. We can construct a similar strategy using the Liquidity Pool, where we will set upper and lower bound prices that will trigger a sell and a buy transaction respectively. If the behavior of the LP prices works as expected always returning to a certain moving average, we could profit by selling high and buying low. We will do this by swapping atestfet and CW20 with the Liquidity Pool, we refer to a sell transaction when we sell atestfet and get CW20 tokens, a buy transaction would be exactly the opposite.

The code will require the following imports:

from time import sleep\nfrom cosmpy.aerial.client import LedgerClient, NetworkConfig\nfrom cosmpy.aerial.contract import LedgerContract\nfrom cosmpy.aerial.faucet import FaucetApi\nfrom cosmpy.aerial.wallet import LocalWallet\n

We will define the swap_native_for_cw20 function that trades swap_amount of atestfet from wallet for CW20 tokens by executing a pair_contract:

def swap_native_for_cw20(swap_amount, pair_contract, wallet):\ntx = pair_contract.execute({\n\"swap\": {\n\"offer_asset\": {\n\"info\": {\n\"native_token\": {\n\"denom\": \"atestfet\"\n}\n},\n\"amount\": str(swap_amount)\n}\n}\n},sender= wallet, funds= str(swap_amount) + \"atestfet\")\nprint(\"swapping native for cw20 tokens\")\ntx.wait_to_complete()\n
Now, we will define the swap_cw20_for_native function that does exactly the opposite of the function defined above: trades swap_amount of CW20 tokens from wallet for atestfet. This time the CW20 token_contract is executed using the pair_contract_address. Finally, you need to include the {\"swap\":{}} message in the \"msg\" field. However, this swap message has to be encoded into base64. When you encode {\"swap\":{}} message into base64 you get: eyJzd2FwIjp7fX0=

def swap_cw20_for_native(swap_amount, pair_contract_address, token_contract, wallet):\ntx = token_contract.execute({\n\"send\": {\n\"contract\": pair_contract_address,\n\"amount\": str(swap_amount),\n\"msg\": \"eyJzd2FwIjp7fX0=\"\n}\n},wallet)\nprint(\"swapping cw20 for native tokens\")\ntx.wait_to_complete()\n
Set the network configuration, define a local wallet and add some tokens to it using the FaucetApi

# Define any wallet\nwallet =  LocalWallet.generate()\n# Network configuration\nledger = LedgerClient(NetworkConfig.latest_stable_testnet())\n# Add tokens to wallet\nfaucet_api = FaucetApi(NetworkConfig.latest_stable_testnet())\nwallet_balance = ledger.query_bank_balance(wallet.address())\nwhile wallet_balance < (10**18):\nprint(\"Providing wealth to wallet...\")\nfaucet_api.get_wealth(wallet.address())\nwallet_balance = ledger.query_bank_balance(wallet.address())\n
Define the CW20, pair, and liquidity token contracts with the following addresses:

# Define cw20, pair and liquidity token contracts\ntoken_contract_address = (\n\"fetch1qr8ysysnfxmqzu7cu7cq7dsq5g2r0kvkg5e2wl2fnlkqss60hcjsxtljxl\"\n)\npair_contract_address = (\n\"fetch1vgnx2d46uvyxrg9pc5mktkcvkp4uflyp3j86v68pq4jxdc8j4y0s6ulf2a\"\n)\nliq_token_contract_address = (\n\"fetch1alzhf9yhghud3qhucdjs895f3aek2egfq44qm0mfvahkv4jukx4qd0ltxx\"\n)\ntoken_contract = LedgerContract(\npath=None, client=ledger, address=token_contract_address\n)\npair_contract = LedgerContract(\npath=None, client=ledger, address=pair_contract_address\n)\nliq_token_contract = LedgerContract(\npath=None, client=ledger, address=liq_token_contract_address\n)\n

We will define a trading wallet named tokens that will keep track of the amount of atestfet or CW20 tokens we hold at each moment. The currency variable will keep track of the token type. We will never have a mixed trading wallet since in this strategy, every time we perform a swap, we sell all the current tokens.

# Trading Wallet\ntokens = 1000000\ncurrency = \"atestfet\"\n

Now we will define the upper and lower price bounds (atestfet/CW20) that will trigger a buy and a sell transaction of atestfet. We also define the commission rate (0.3% in Terraswap) and the interval time step to query the pool's price.

upper_bound = 26\nlower_bound = 24\n# LP commission\ncommission = 0.003\n# Interval in seconds\ninterval = 5\n
Finally, we will initialize a loop, in every step it will:

  • Query the Liquidity Pool status
  • Check if current trading wallet's currency
  • Calculate the atestfet/CW20 price using the tokens received tokens_out if the whole trading wallet's balance tokens was to be swapped with the liquidity pool
  • If atestfet sell/buy price is equal or lower/higher than the lower/upper bound, it will trigger a sell/buy transaction of atestfet to buy/sell CW20 tokens.
  • Update trading wallet token balance and currency
  • Sleep interval and repeat
while True:\n# Query LP status\npool = pair_contract.query({\"pool\": {}})\nnative_amount = int(pool[\"assets\"][1][\"amount\"])\ncw20_amount = int(pool[\"assets\"][0][\"amount\"])\nif currency == \"atestfet\":\n# Calculate received tokens if tokens amount is given to LP\ntokens_out = round(((cw20_amount*tokens)/(native_amount+tokens))*(1-commission))\n# Sell price of atestfet => give atestfet, get cw20\nsell_price = tokens/tokens_out\nprint(\"atestfet sell price: \", sell_price)\nif sell_price <= lower_bound:\nswap_native_for_cw20(tokens, pair_contract, wallet)\ntokens = int(token_contract.query({\"balance\": {\"address\": str(wallet.address())}})[\"balance\"])\ncurrency = \"CW20\"\nelse:\n# Calculate received tokens if tokens amount is given to LP\ntokens_out = round(((native_amount*tokens)/(cw20_amount+tokens))*(1-commission))\n# Buy price of atestfet => give cw20, get atestfet\nbuy_price = tokens_out/tokens\nprint(\"atestfet buy price: \", buy_price)\nif buy_price >= upper_bound:\nswap_cw20_for_native(tokens, pair_contract_address, token_contract, wallet)\ntokens = tokens_out\ncurrency = \"atestfet\"\nsleep(interval)\n

This code assumes other traders performing transactions with the Liquidity Pool that will generate price movements. You can check out the full example at swap-automation

"},{"location":"CosmPy/wallet-topup/","title":"Wallet Top-up","text":"

In a case where you are performing multiple transactions from a certain task_wallet, you can set an algorithm to keep that wallet address topped-up. For this use case, we will use three different wallets: wallet, authz_wallet, and task_wallet. Wallet will be the main wallet address that we don't want to give full access to, therefore we will authorize authz_wallet to send a certain amount of tokens from wallet to task_wallet every time task_wallet balance falls below a certain minimum_balance threshold. This way, task_wallet can keep performing transactions using the main wallet's tokens by being topped-up by authz_wallet. Start by defining wallet, authz_wallet and task_wallet address.

from cosmpy.aerial.wallet import LocalWallet\nfrom cosmpy.crypto.keypairs import PrivateKey\nfrom cosmpy.aerial.client import LedgerClient, NetworkConfig\nledger = LedgerClient(NetworkConfig.latest_stable_testnet())\n# Define wallets with any private keys\nwallet = LocalWallet(PrivateKey(\"F7w1yHq1QIcQiSqV27YSwk+i1i+Y4JMKhkpawCQIh6s=\"))\nauthz_wallet = LocalWallet(\nPrivateKey(\"KI5AZQcr+FNl2usnSIQYpXsGWvBxKLRDkieUNIvMOV8=\")\n)\n# Define any task_wallet address\ntask_wallet_address = 'fetch1ay6grfwhlm00wydwa3nw0x2u44qz4hg2uku8dc'\n
Wallet will need to have enough tokens available to top-up task_wallet, and authz_wallet will need enough tokens to pay for transaction fees. Now you will need to give authorization to authz_wallet to send tokens from wallet. You will define the expiration and the spend limit of the authorization in total_authz_time and spend_amount. The code below shows how to perform this kind of transaction:

from cosmpy.protos.cosmos.base.v1beta1.coin_pb2 import Coin\nfrom cosmpy.aerial.client.utils import prepare_and_broadcast_basic_transaction\nfrom cosmpy.aerial.tx import Transaction\nfrom datetime import datetime, timedelta\nfrom google.protobuf import any_pb2, timestamp_pb2\nfrom cosmpy.protos.cosmos.authz.v1beta1.authz_pb2 import Grant\nfrom cosmpy.protos.cosmos.authz.v1beta1.tx_pb2 import MsgGrant\nfrom cosmpy.protos.cosmos.bank.v1beta1.authz_pb2 import SendAuthorization\n# Set total authorization time and spend amount\ntotal_authz_time = 10000\namount = 1000000000000000000\nspend_amount = Coin(amount=str(amount), denom=\"atestfet\")\n# Authorize authz_wallet to send tokens from wallet\nauthz_any = any_pb2.Any()\nauthz_any.Pack(\nSendAuthorization(spend_limit=[spend_amount]),\n\"\",\n)\nexpiry = timestamp_pb2.Timestamp()\nexpiry.FromDatetime(datetime.now() + timedelta(seconds=total_authz_time * 60))\ngrant = Grant(authorization=authz_any, expiration=expiry)\nmsg = MsgGrant(\ngranter=str(wallet.address()),\ngrantee=str(authz_wallet.address()),\ngrant=grant,\n)\ntx = Transaction()\ntx.add_message(msg)\ntx = prepare_and_broadcast_basic_transaction(ledger, tx, wallet)\ntx.wait_to_complete()\n

Next, you will need to define the amount to top-up, the threshold that will trigger the top-up, and the interval time to query the task_wallet balance. We will define these amounts in the following variables: top_up_amount, minimum_balance and interval_time.

# Top-up amount\namount = 10000000000000000\ntop_up_amount = Coin(amount=str(amount), denom=\"atestfet\")\n# Minimum balance for task_wallet\nminimum_balance = 1000000000000000\n# Interval to query task_wallet's balance in seconds\ninterval_time = 5\n

Finally, run a continuously running loop that will: * Check the main wallet's balance to make sure it has enough tokens to top up the task_wallet_address * Check task_wallet's balance, if it is lower than minimum_balance then authz_wallet will send top_up_amount of tokens from wallet to task_wallet * Sleep interval_time and repeat

import time\nfrom cosmpy.protos.cosmos.authz.v1beta1.tx_pb2 import MsgExec\nfrom cosmpy.protos.cosmos.bank.v1beta1.tx_pb2 import MsgSend\nwhile True:\nwallet_address = str(wallet.address())\nwallet_balance = ledger.query_bank_balance(wallet_address)\nif wallet_balance < amount:\nprint(\"Wallet doesn't have enough balance to top-up task_wallet\")\nbreak\ntask_wallet_balance = ledger.query_bank_balance(task_wallet_address)\nif task_wallet_balance < minimum_balance:\nprint(\"topping up task wallet\")\n# Top-up task_wallet\nmsg = any_pb2.Any()\nmsg.Pack(\nMsgSend(\nfrom_address=wallet_address,\nto_address=task_wallet_address,\namount=[top_up_amount],\n),\n\"\",\n)\ntx = Transaction()\ntx.add_message(MsgExec(grantee=str(authz_wallet.address()), msgs=[msg]))\ntx = prepare_and_broadcast_basic_transaction(ledger, tx, authz_wallet)\ntx.wait_to_complete()\ntime.sleep(interval_time)\n

While the code above keeps running, you can make sure that task_wallet is always topped-up as long as authz_wallet has authorization to send the required tokens and the main wallet has enough balance.

You can also check out the authorization and top-up code examples at authz and top-up respectively.

"},{"location":"CosmPy/wallets-and-keys/","title":"Wallets and Keys","text":"

To make changes on a network, you will need to start sending transactions to it. This in tern involves managing private keys and addresses. Luckily, CosmPy makes this relatively straightforward.

The following code outlines how to both generate a completely new private key and how to recover a previously generated one:

from cosmpy.aerial.wallet import LocalWallet\nfrom cosmpy.crypto.keypairs import PrivateKey\n# To create a random private key:\nprivate_key = PrivateKey()\n# To recover an existing private key:\nprivate_key = PrivateKey('<base64 encoded private key>')\n

The PrivateKey object is one of CosmPy's low level primitives. This is why it is generally paired with a Wallet object in most scenarios. Below, a LocalWallet (a kind of Wallet) is created using the private key:

wallet = LocalWallet(private_key)\n

Creating the wallet allows users to query useful information such as the address from the wallet directly.

print(wallet.address()) # will print the address for the wallet\n
"},{"location":"CosmPy/wallets-and-keys/#existing-account","title":"Existing account","text":"

To use cosmpy with an existing account, extract the private key and convert it into a base64 encoded string.

For example, to do this on macOS or Linux for the Fetch.ai network using its FetchD CLI:

fetchd keys export mykeyname --unsafe --unarmored-hex | xxd -r -p | base64\n
"},{"location":"CosmPy/wallets-and-keys/#from-mnemonic","title":"From mnemonic","text":"

If you have the mnemonic phrase to an account, you can get the associated private key as follows:

from bip_utils import Bip39SeedGenerator, Bip44, Bip44Coins\nmnemonic = \"person knife december tail tortoise jewel warm when worry limit reward memory piece cool sphere kitchen knee embody soft own victory sauce silly page\"\nseed_bytes = Bip39SeedGenerator(mnemonic).Generate()\nbip44_def_ctx = Bip44.FromSeed(seed_bytes, Bip44Coins.COSMOS).DeriveDefaultPath()\nwallet = LocalWallet(PrivateKey(bip44_def_ctx.PrivateKey().Raw().ToBytes()))\n

Danger

Of course in real applications, you should never include a mnemonic in public code.

"},{"location":"CosmPy/wallets-and-keys/#custom-prefix-network","title":"Custom prefix network:","text":"

In case you are using a network other than fetch.ai's, you can provide the custom prefix when creating the wallet:

alice = LocalWallet(PrivateKey(\"L1GsisFk+oaIug3XZlILWk2pJDVFS5aPJsrovvUEDrE=\"), prefix=\"custom_prefix\")\naddress = alice.address()\nprint(f\"Address: {address}\")\nbalance = client.query_bank_balance(address, \"uatom\")\n
"},{"location":"CosmPy/api/aerial/coins/","title":"Parse the coins","text":""},{"location":"CosmPy/api/aerial/coins/#cosmpyaerialcoins","title":"cosmpy.aerial.coins","text":"

Parse the coins.

"},{"location":"CosmPy/api/aerial/coins/#parse_coins","title":"parse_coins","text":"
def parse_coins(value: str) -> List[Coin]\n

Parse the coins.

Arguments:

  • value: coins

Raises:

  • RuntimeError: If unable to parse the value

Returns:

coins

"},{"location":"CosmPy/api/aerial/config/","title":"Network configurations","text":""},{"location":"CosmPy/api/aerial/config/#cosmpyaerialconfig","title":"cosmpy.aerial.config","text":"

Network configurations.

"},{"location":"CosmPy/api/aerial/config/#networkconfigerror-objects","title":"NetworkConfigError Objects","text":"
class NetworkConfigError(RuntimeError)\n

Network config error.

Arguments:

  • RuntimeError: Runtime error

"},{"location":"CosmPy/api/aerial/config/#networkconfig-objects","title":"NetworkConfig Objects","text":"
@dataclass\nclass NetworkConfig()\n

Network configurations.

Raises:

  • NetworkConfigError: Network config error
  • RuntimeError: Runtime error

"},{"location":"CosmPy/api/aerial/config/#validate","title":"validate","text":"
def validate()\n

Validate the network configuration.

Raises:

  • NetworkConfigError: Network config error

"},{"location":"CosmPy/api/aerial/config/#fetchai_dorado_testnet","title":"fetchai_dorado_testnet","text":"
@classmethod\ndef fetchai_dorado_testnet(cls) -> \"NetworkConfig\"\n

Fetchai dorado testnet.

Returns:

Network configuration

"},{"location":"CosmPy/api/aerial/config/#fetchai_alpha_testnet","title":"fetchai_alpha_testnet","text":"
@classmethod\ndef fetchai_alpha_testnet(cls)\n

Get the fetchai alpha testnet.

Raises:

  • RuntimeError: No alpha testnet available

"},{"location":"CosmPy/api/aerial/config/#fetchai_beta_testnet","title":"fetchai_beta_testnet","text":"
@classmethod\ndef fetchai_beta_testnet(cls)\n

Get the Fetchai beta testnet.

Raises:

  • RuntimeError: No beta testnet available

"},{"location":"CosmPy/api/aerial/config/#fetchai_stable_testnet","title":"fetchai_stable_testnet","text":"
@classmethod\ndef fetchai_stable_testnet(cls)\n

Get the fetchai stable testnet.

Returns:

fetchai stable testnet. For now dorado is fetchai stable testnet.

"},{"location":"CosmPy/api/aerial/config/#fetchai_mainnet","title":"fetchai_mainnet","text":"
@classmethod\ndef fetchai_mainnet(cls) -> \"NetworkConfig\"\n

Get the fetchai mainnet configuration.

Returns:

fetch mainnet configuration

"},{"location":"CosmPy/api/aerial/config/#fetch_mainnet","title":"fetch_mainnet","text":"
@classmethod\ndef fetch_mainnet(cls) -> \"NetworkConfig\"\n

Get the fetch mainnet.

Returns:

fetch mainnet configurations

"},{"location":"CosmPy/api/aerial/config/#latest_stable_testnet","title":"latest_stable_testnet","text":"
@classmethod\ndef latest_stable_testnet(cls) -> \"NetworkConfig\"\n

Get the latest stable testnet.

Returns:

latest stable testnet

"},{"location":"CosmPy/api/aerial/exceptions/","title":"Exceptions","text":""},{"location":"CosmPy/api/aerial/exceptions/#cosmpyaerialexceptions","title":"cosmpy.aerial.exceptions","text":"

Exceptions.

"},{"location":"CosmPy/api/aerial/exceptions/#queryerror-objects","title":"QueryError Objects","text":"
class QueryError(RuntimeError)\n

Invalid Query Error.

"},{"location":"CosmPy/api/aerial/exceptions/#notfounderror-objects","title":"NotFoundError Objects","text":"
class NotFoundError(QueryError)\n

Not found Error.

"},{"location":"CosmPy/api/aerial/exceptions/#querytimeouterror-objects","title":"QueryTimeoutError Objects","text":"
class QueryTimeoutError(QueryError)\n

Query timeout Error.

"},{"location":"CosmPy/api/aerial/exceptions/#broadcasterror-objects","title":"BroadcastError Objects","text":"
class BroadcastError(RuntimeError)\n

Broadcast Error.

"},{"location":"CosmPy/api/aerial/exceptions/#__init__","title":"__init__","text":"
def __init__(tx_hash: str, message: str)\n

Init Broadcast error.

Arguments:

  • tx_hash: transaction hash
  • message: message

"},{"location":"CosmPy/api/aerial/exceptions/#outofgaserror-objects","title":"OutOfGasError Objects","text":"
class OutOfGasError(BroadcastError)\n

Insufficient Fess Error.

"},{"location":"CosmPy/api/aerial/exceptions/#__init___1","title":"__init__","text":"
def __init__(tx_hash: str, gas_wanted: int, gas_used: int)\n

Initialize.

Arguments:

  • tx_hash: transaction hash
  • gas_wanted: gas required to complete the transaction
  • gas_used: gas used

"},{"location":"CosmPy/api/aerial/exceptions/#insufficientfeeserror-objects","title":"InsufficientFeesError Objects","text":"
class InsufficientFeesError(BroadcastError)\n

Insufficient Fess Error.

"},{"location":"CosmPy/api/aerial/exceptions/#__init___2","title":"__init__","text":"
def __init__(tx_hash: str, minimum_required_fee: str)\n

Initialize.

Arguments:

  • tx_hash: transaction hash
  • minimum_required_fee: Minimum required fee
"},{"location":"CosmPy/api/aerial/faucet/","title":"Ledger faucet API interface","text":""},{"location":"CosmPy/api/aerial/faucet/#cosmpyaerialfaucet","title":"cosmpy.aerial.faucet","text":"

Ledger faucet API interface.

"},{"location":"CosmPy/api/aerial/faucet/#faucetapi-objects","title":"FaucetApi Objects","text":"
class FaucetApi()\n

Faucet API.

"},{"location":"CosmPy/api/aerial/faucet/#__init__","title":"__init__","text":"
def __init__(net_config: NetworkConfig)\n

Init faucet API.

Arguments:

  • net_config: Ledger network configuration.

Raises:

  • ValueError: Network config has no faucet url set

"},{"location":"CosmPy/api/aerial/faucet/#get_wealth","title":"get_wealth","text":"
def get_wealth(address: Union[Address, str]) -> None\n

Get wealth from the faucet for the provided address.

Arguments:

  • address: the address.

Raises:

  • RuntimeError: Unable to create faucet claim
  • RuntimeError: Failed to check faucet claim status
  • RuntimeError: Failed to get wealth for address
  • ValueError: Faucet claim check timed out
"},{"location":"CosmPy/api/aerial/gas/","title":"Transaction gas strategy","text":""},{"location":"CosmPy/api/aerial/gas/#cosmpyaerialgas","title":"cosmpy.aerial.gas","text":"

Transaction gas strategy.

"},{"location":"CosmPy/api/aerial/gas/#gasstrategy-objects","title":"GasStrategy Objects","text":"
class GasStrategy(ABC)\n

Transaction gas strategy.

"},{"location":"CosmPy/api/aerial/gas/#estimate_gas","title":"estimate_gas","text":"
@abstractmethod\ndef estimate_gas(tx: Transaction) -> int\n

Estimate the transaction gas.

Arguments:

  • tx: Transaction

Returns:

None

"},{"location":"CosmPy/api/aerial/gas/#block_gas_limit","title":"block_gas_limit","text":"
@abstractmethod\ndef block_gas_limit() -> int\n

Get the block gas limit.

Returns:

None

"},{"location":"CosmPy/api/aerial/gas/#simulationgasstrategy-objects","title":"SimulationGasStrategy Objects","text":"
class SimulationGasStrategy(GasStrategy)\n

Simulation transaction gas strategy.

Arguments:

  • GasStrategy: gas strategy

"},{"location":"CosmPy/api/aerial/gas/#__init__","title":"__init__","text":"
def __init__(client: \"LedgerClient\", multiplier: Optional[float] = None)\n

Init the Simulation transaction gas strategy.

Arguments:

  • client: Ledger client
  • multiplier: multiplier, defaults to None

"},{"location":"CosmPy/api/aerial/gas/#estimate_gas_1","title":"estimate_gas","text":"
def estimate_gas(tx: Transaction) -> int\n

Get estimated transaction gas.

Arguments:

  • tx: transaction

Returns:

Estimated transaction gas

"},{"location":"CosmPy/api/aerial/gas/#block_gas_limit_1","title":"block_gas_limit","text":"
def block_gas_limit() -> int\n

Get the block gas limit.

Returns:

block gas limit

"},{"location":"CosmPy/api/aerial/gas/#offlinemessagetablestrategy-objects","title":"OfflineMessageTableStrategy Objects","text":"
class OfflineMessageTableStrategy(GasStrategy)\n

Offline message table strategy.

Arguments:

  • GasStrategy: gas strategy

"},{"location":"CosmPy/api/aerial/gas/#default_table","title":"default_table","text":"
@staticmethod\ndef default_table() -> \"OfflineMessageTableStrategy\"\n

offline message strategy default table.

Returns:

offline message default table strategy

"},{"location":"CosmPy/api/aerial/gas/#__init___1","title":"__init__","text":"
def __init__(fallback_gas_limit: Optional[int] = None,\nblock_limit: Optional[int] = None)\n

Init offline message table strategy.

Arguments:

  • fallback_gas_limit: Fallback gas limit, defaults to None
  • block_limit: Block limit, defaults to None

"},{"location":"CosmPy/api/aerial/gas/#update_entry","title":"update_entry","text":"
def update_entry(transaction_type: str, gas_limit: int)\n

Update the entry of the transaction.

Arguments:

  • transaction_type: transaction type
  • gas_limit: gas limit

"},{"location":"CosmPy/api/aerial/gas/#estimate_gas_2","title":"estimate_gas","text":"
def estimate_gas(tx: Transaction) -> int\n

Get estimated transaction gas.

Arguments:

  • tx: transaction

Returns:

Estimated transaction gas

"},{"location":"CosmPy/api/aerial/gas/#block_gas_limit_2","title":"block_gas_limit","text":"
def block_gas_limit() -> int\n

Get the block gas limit.

Returns:

block gas limit

"},{"location":"CosmPy/api/aerial/tx/","title":"Transaction","text":""},{"location":"CosmPy/api/aerial/tx/#cosmpyaerialtx","title":"cosmpy.aerial.tx","text":"

Transaction.

"},{"location":"CosmPy/api/aerial/tx/#txstate-objects","title":"TxState Objects","text":"
class TxState(Enum)\n

Transaction state.

Arguments:

  • Enum: Draft, Sealed, Final

"},{"location":"CosmPy/api/aerial/tx/#signingmode-objects","title":"SigningMode Objects","text":"
class SigningMode(Enum)\n

Signing mode.

Arguments:

  • Enum: Direct

"},{"location":"CosmPy/api/aerial/tx/#signingcfg-objects","title":"SigningCfg Objects","text":"
@dataclass\nclass SigningCfg()\n

Transaction signing configuration.

"},{"location":"CosmPy/api/aerial/tx/#direct","title":"direct","text":"
@staticmethod\ndef direct(public_key: PublicKey, sequence_num: int) -> \"SigningCfg\"\n

Transaction signing configuration using direct mode.

Arguments:

  • public_key: public key
  • sequence_num: sequence number

Returns:

Transaction signing configuration

"},{"location":"CosmPy/api/aerial/tx/#transaction-objects","title":"Transaction Objects","text":"
class Transaction()\n

Transaction.

"},{"location":"CosmPy/api/aerial/tx/#__init__","title":"__init__","text":"
def __init__()\n

Init the Transactions with transaction message, state, fee and body.

"},{"location":"CosmPy/api/aerial/tx/#state","title":"state","text":"
@property\ndef state() -> TxState\n

Get the transaction state.

Returns:

current state of the transaction

"},{"location":"CosmPy/api/aerial/tx/#msgs","title":"msgs","text":"
@property\ndef msgs()\n

Get the transaction messages.

Returns:

transaction messages

"},{"location":"CosmPy/api/aerial/tx/#fee","title":"fee","text":"
@property\ndef fee() -> Optional[str]\n

Get the transaction fee.

Returns:

transaction fee

"},{"location":"CosmPy/api/aerial/tx/#tx","title":"tx","text":"
@property\ndef tx()\n

Initialize.

Raises:

  • RuntimeError: If the transaction has not been completed.

Returns:

transaction

"},{"location":"CosmPy/api/aerial/tx/#add_message","title":"add_message","text":"
def add_message(msg: Any) -> \"Transaction\"\n

Initialize.

Arguments:

  • msg: transaction message (memo)

Raises:

  • RuntimeError: If the transaction is not in the draft state.

Returns:

transaction with message added

"},{"location":"CosmPy/api/aerial/tx/#seal","title":"seal","text":"
def seal(signing_cfgs: Union[SigningCfg, List[SigningCfg]],\nfee: str,\ngas_limit: int,\nmemo: Optional[str] = None) -> \"Transaction\"\n

Seal the transaction.

Arguments:

  • signing_cfgs: signing configs
  • fee: transaction fee
  • gas_limit: transaction gas limit
  • memo: transaction memo, defaults to None

Returns:

sealed transaction.

"},{"location":"CosmPy/api/aerial/tx/#sign","title":"sign","text":"
def sign(signer: Signer,\nchain_id: str,\naccount_number: int,\ndeterministic: bool = False) -> \"Transaction\"\n

Sign the transaction.

Arguments:

  • signer: Signer
  • chain_id: chain id
  • account_number: account number
  • deterministic: deterministic, defaults to False

Raises:

  • RuntimeError: If transaction is not sealed

Returns:

signed transaction

"},{"location":"CosmPy/api/aerial/tx/#complete","title":"complete","text":"
def complete() -> \"Transaction\"\n

Update transaction state to Final.

Returns:

transaction with updated state

"},{"location":"CosmPy/api/aerial/tx_helpers/","title":"Transaction helpers","text":""},{"location":"CosmPy/api/aerial/tx_helpers/#cosmpyaerialtx_helpers","title":"cosmpy.aerial.tx_helpers","text":"

Transaction helpers.

"},{"location":"CosmPy/api/aerial/tx_helpers/#messagelog-objects","title":"MessageLog Objects","text":"
@dataclass\nclass MessageLog()\n

Message Log.

"},{"location":"CosmPy/api/aerial/tx_helpers/#txresponse-objects","title":"TxResponse Objects","text":"
@dataclass\nclass TxResponse()\n

Transaction response.

Raises:

  • OutOfGasError: Out of gas error
  • InsufficientFeesError: Insufficient fees
  • BroadcastError: Broadcast Exception

"},{"location":"CosmPy/api/aerial/tx_helpers/#is_successful","title":"is_successful","text":"
def is_successful() -> bool\n

Check transaction is successful.

Returns:

transaction status

"},{"location":"CosmPy/api/aerial/tx_helpers/#ensure_successful","title":"ensure_successful","text":"
def ensure_successful()\n

Ensure transaction is successful.

Raises:

  • OutOfGasError: Out of gas error
  • InsufficientFeesError: Insufficient fees
  • BroadcastError: Broadcast Exception

"},{"location":"CosmPy/api/aerial/tx_helpers/#submittedtx-objects","title":"SubmittedTx Objects","text":"
class SubmittedTx()\n

Submitted transaction.

"},{"location":"CosmPy/api/aerial/tx_helpers/#__init__","title":"__init__","text":"
def __init__(client: \"LedgerClient\", tx_hash: str)\n

Init the Submitted transaction.

Arguments:

  • client: Ledger client
  • tx_hash: transaction hash

"},{"location":"CosmPy/api/aerial/tx_helpers/#tx_hash","title":"tx_hash","text":"
@property\ndef tx_hash() -> str\n

Get the transaction hash.

Returns:

transaction hash

"},{"location":"CosmPy/api/aerial/tx_helpers/#response","title":"response","text":"
@property\ndef response() -> Optional[TxResponse]\n

Get the transaction response.

Returns:

response

"},{"location":"CosmPy/api/aerial/tx_helpers/#contract_code_id","title":"contract_code_id","text":"
@property\ndef contract_code_id() -> Optional[int]\n

Get the contract code id.

Returns:

return contract code id if exist else None

"},{"location":"CosmPy/api/aerial/tx_helpers/#contract_address","title":"contract_address","text":"
@property\ndef contract_address() -> Optional[Address]\n

Get the contract address.

Returns:

return contract address if exist else None

"},{"location":"CosmPy/api/aerial/tx_helpers/#wait_to_complete","title":"wait_to_complete","text":"
def wait_to_complete(\ntimeout: Optional[Union[int, float, timedelta]] = None,\npoll_period: Optional[Union[int, float,\ntimedelta]] = None) -> \"SubmittedTx\"\n

Wait to complete the transaction.

Arguments:

  • timeout: timeout, defaults to None
  • poll_period: poll_period, defaults to None

Returns:

Submitted Transaction

"},{"location":"CosmPy/api/aerial/urls/","title":"Parsing the URL","text":""},{"location":"CosmPy/api/aerial/urls/#cosmpyaerialurls","title":"cosmpy.aerial.urls","text":"

Parsing the URL.

"},{"location":"CosmPy/api/aerial/urls/#protocol-objects","title":"Protocol Objects","text":"
class Protocol(Enum)\n

Protocol Enum.

Arguments:

  • Enum: Enum

"},{"location":"CosmPy/api/aerial/urls/#parsedurl-objects","title":"ParsedUrl Objects","text":"
@dataclass\nclass ParsedUrl()\n

Parse URL.

Returns:

Parsed URL

"},{"location":"CosmPy/api/aerial/urls/#host_and_port","title":"host_and_port","text":"
@property\ndef host_and_port() -> str\n

Get the host and port of the url.

Returns:

host and port

"},{"location":"CosmPy/api/aerial/urls/#rest_url","title":"rest_url","text":"
@property\ndef rest_url() -> str\n

Get the rest url.

Returns:

rest url

"},{"location":"CosmPy/api/aerial/urls/#parse_url","title":"parse_url","text":"
def parse_url(url: str) -> ParsedUrl\n

Initialize.

Arguments:

  • url: url

Raises:

  • RuntimeError: If url scheme is unsupported

Returns:

Parsed URL

"},{"location":"CosmPy/api/aerial/wallet/","title":"Wallet Generation","text":""},{"location":"CosmPy/api/aerial/wallet/#cosmpyaerialwallet","title":"cosmpy.aerial.wallet","text":"

Wallet Generation.

"},{"location":"CosmPy/api/aerial/wallet/#wallet-objects","title":"Wallet Objects","text":"
class Wallet(ABC, UserString)\n

Wallet Generation.

Arguments:

  • ABC: ABC abstract method
  • UserString: user string

"},{"location":"CosmPy/api/aerial/wallet/#address","title":"address","text":"
@abstractmethod\ndef address() -> Address\n

get the address of the wallet.

Returns:

None

"},{"location":"CosmPy/api/aerial/wallet/#public_key","title":"public_key","text":"
@abstractmethod\ndef public_key() -> PublicKey\n

get the public key of the wallet.

Returns:

None

"},{"location":"CosmPy/api/aerial/wallet/#signer","title":"signer","text":"
@abstractmethod\ndef signer() -> Signer\n

get the signer of the wallet.

Returns:

None

"},{"location":"CosmPy/api/aerial/wallet/#data","title":"data","text":"
@property\ndef data()\n

Get the address of the wallet.

Returns:

Address

"},{"location":"CosmPy/api/aerial/wallet/#__json__","title":"__json__","text":"
def __json__()\n

Return the address in string format.

Returns:

address in string format

"},{"location":"CosmPy/api/aerial/wallet/#localwallet-objects","title":"LocalWallet Objects","text":"
class LocalWallet(Wallet)\n

Generate local wallet.

Arguments:

  • Wallet: wallet

"},{"location":"CosmPy/api/aerial/wallet/#generate","title":"generate","text":"
@staticmethod\ndef generate(prefix: Optional[str] = None) -> \"LocalWallet\"\n

generate the local wallet.

Arguments:

  • prefix: prefix, defaults to None

Returns:

local wallet

"},{"location":"CosmPy/api/aerial/wallet/#from_mnemonic","title":"from_mnemonic","text":"
@staticmethod\ndef from_mnemonic(mnemonic: str,\nprefix: Optional[str] = None) -> \"LocalWallet\"\n

Generate local wallet from mnemonic.

Arguments:

  • mnemonic: mnemonic
  • prefix: prefix, defaults to None

Returns:

local wallet

"},{"location":"CosmPy/api/aerial/wallet/#from_unsafe_seed","title":"from_unsafe_seed","text":"
@staticmethod\ndef from_unsafe_seed(text: str,\nindex: Optional[int] = None,\nprefix: Optional[str] = None) -> \"LocalWallet\"\n

Generate local wallet from unsafe seed.

Arguments:

  • text: text
  • index: index, defaults to None
  • prefix: prefix, defaults to None

Returns:

Local wallet

"},{"location":"CosmPy/api/aerial/wallet/#__init__","title":"__init__","text":"
def __init__(private_key: PrivateKey, prefix: Optional[str] = None)\n

Init wallet with.

Arguments:

  • private_key: private key of the wallet
  • prefix: prefix, defaults to None

"},{"location":"CosmPy/api/aerial/wallet/#address_1","title":"address","text":"
def address() -> Address\n

Get the wallet address.

Returns:

Wallet address.

"},{"location":"CosmPy/api/aerial/wallet/#public_key_1","title":"public_key","text":"
def public_key() -> PublicKey\n

Get the public key of the wallet.

Returns:

public key

"},{"location":"CosmPy/api/aerial/wallet/#signer_1","title":"signer","text":"
def signer() -> PrivateKey\n

Get the signer of the wallet.

Returns:

signer

"},{"location":"CosmPy/api/aerial/client/__init__/","title":"Client functionality","text":""},{"location":"CosmPy/api/aerial/client/__init__/#cosmpyaerialclient__init__","title":"cosmpy.aerial.client.__init__","text":"

Client functionality.

"},{"location":"CosmPy/api/aerial/client/__init__/#account-objects","title":"Account Objects","text":"
@dataclass\nclass Account()\n

Account.

"},{"location":"CosmPy/api/aerial/client/__init__/#stakingposition-objects","title":"StakingPosition Objects","text":"
@dataclass\nclass StakingPosition()\n

Staking positions.

"},{"location":"CosmPy/api/aerial/client/__init__/#unbondingpositions-objects","title":"UnbondingPositions Objects","text":"
@dataclass\nclass UnbondingPositions()\n

Unbonding positions.

"},{"location":"CosmPy/api/aerial/client/__init__/#validator-objects","title":"Validator Objects","text":"
@dataclass\nclass Validator()\n

Validator.

"},{"location":"CosmPy/api/aerial/client/__init__/#coin-objects","title":"Coin Objects","text":"
@dataclass\nclass Coin()\n

Coins.

"},{"location":"CosmPy/api/aerial/client/__init__/#stakingsummary-objects","title":"StakingSummary Objects","text":"
@dataclass\nclass StakingSummary()\n

Get the staking summary.

"},{"location":"CosmPy/api/aerial/client/__init__/#total_staked","title":"total_staked","text":"
@property\ndef total_staked() -> int\n

Get the total staked amount.

"},{"location":"CosmPy/api/aerial/client/__init__/#total_rewards","title":"total_rewards","text":"
@property\ndef total_rewards() -> int\n

Get the total rewards.

"},{"location":"CosmPy/api/aerial/client/__init__/#total_unbonding","title":"total_unbonding","text":"
@property\ndef total_unbonding() -> int\n

total unbonding.

"},{"location":"CosmPy/api/aerial/client/__init__/#ledgerclient-objects","title":"LedgerClient Objects","text":"
class LedgerClient()\n

Ledger client.

"},{"location":"CosmPy/api/aerial/client/__init__/#__init__","title":"__init__","text":"
def __init__(cfg: NetworkConfig,\nquery_interval_secs: int = DEFAULT_QUERY_INTERVAL_SECS,\nquery_timeout_secs: int = DEFAULT_QUERY_TIMEOUT_SECS)\n

Init ledger client.

Arguments:

  • cfg: Network configurations
  • query_interval_secs: int. optional interval int seconds
  • query_timeout_secs: int. optional interval int seconds

"},{"location":"CosmPy/api/aerial/client/__init__/#network_config","title":"network_config","text":"
@property\ndef network_config() -> NetworkConfig\n

Get the network config.

Returns:

network config

"},{"location":"CosmPy/api/aerial/client/__init__/#gas_strategy","title":"gas_strategy","text":"
@property\ndef gas_strategy() -> GasStrategy\n

Get gas strategy.

Returns:

gas strategy

"},{"location":"CosmPy/api/aerial/client/__init__/#gas_strategy_1","title":"gas_strategy","text":"
@gas_strategy.setter\ndef gas_strategy(strategy: GasStrategy)\n

Set gas strategy.

Arguments:

  • strategy: strategy

Raises:

  • RuntimeError: Invalid strategy must implement GasStrategy interface

"},{"location":"CosmPy/api/aerial/client/__init__/#query_account","title":"query_account","text":"
def query_account(address: Address) -> Account\n

Query account.

Arguments:

  • address: address

Raises:

  • RuntimeError: Unexpected account type returned from query

Returns:

account details

"},{"location":"CosmPy/api/aerial/client/__init__/#query_params","title":"query_params","text":"
def query_params(subspace: str, key: str) -> Any\n

Query Prams.

Arguments:

  • subspace: subspace
  • key: key

Returns:

Query params

"},{"location":"CosmPy/api/aerial/client/__init__/#query_bank_balance","title":"query_bank_balance","text":"
def query_bank_balance(address: Address, denom: Optional[str] = None) -> int\n

Query bank balance.

Arguments:

  • address: address
  • denom: denom, defaults to None

Returns:

bank balance

"},{"location":"CosmPy/api/aerial/client/__init__/#query_bank_all_balances","title":"query_bank_all_balances","text":"
def query_bank_all_balances(address: Address) -> List[Coin]\n

Query bank all balances.

Arguments:

  • address: address

Returns:

bank all balances

"},{"location":"CosmPy/api/aerial/client/__init__/#send_tokens","title":"send_tokens","text":"
def send_tokens(destination: Address,\namount: int,\ndenom: str,\nsender: Wallet,\nmemo: Optional[str] = None,\ngas_limit: Optional[int] = None) -> SubmittedTx\n

Send tokens.

Arguments:

  • destination: destination address
  • amount: amount
  • denom: denom
  • sender: sender
  • memo: memo, defaults to None
  • gas_limit: gas limit, defaults to None

Returns:

prepare and broadcast the transaction and transaction details

"},{"location":"CosmPy/api/aerial/client/__init__/#query_validators","title":"query_validators","text":"
def query_validators(\nstatus: Optional[ValidatorStatus] = None) -> List[Validator]\n

Query validators.

Arguments:

  • status: validator status, defaults to None

Returns:

List of validators

"},{"location":"CosmPy/api/aerial/client/__init__/#query_staking_summary","title":"query_staking_summary","text":"
def query_staking_summary(address: Address) -> StakingSummary\n

Query staking summary.

Arguments:

  • address: address

Returns:

staking summary

"},{"location":"CosmPy/api/aerial/client/__init__/#delegate_tokens","title":"delegate_tokens","text":"
def delegate_tokens(validator: Address,\namount: int,\nsender: Wallet,\nmemo: Optional[str] = None,\ngas_limit: Optional[int] = None) -> SubmittedTx\n

Delegate tokens.

Arguments:

  • validator: validator address
  • amount: amount
  • sender: sender
  • memo: memo, defaults to None
  • gas_limit: gas limit, defaults to None

Returns:

prepare and broadcast the transaction and transaction details

"},{"location":"CosmPy/api/aerial/client/__init__/#redelegate_tokens","title":"redelegate_tokens","text":"
def redelegate_tokens(current_validator: Address,\nnext_validator: Address,\namount: int,\nsender: Wallet,\nmemo: Optional[str] = None,\ngas_limit: Optional[int] = None) -> SubmittedTx\n

Redelegate tokens.

Arguments:

  • current_validator: current validator address
  • next_validator: next validator address
  • amount: amount
  • sender: sender
  • memo: memo, defaults to None
  • gas_limit: gas limit, defaults to None

Returns:

prepare and broadcast the transaction and transaction details

"},{"location":"CosmPy/api/aerial/client/__init__/#undelegate_tokens","title":"undelegate_tokens","text":"
def undelegate_tokens(validator: Address,\namount: int,\nsender: Wallet,\nmemo: Optional[str] = None,\ngas_limit: Optional[int] = None) -> SubmittedTx\n

Undelegate tokens.

Arguments:

  • validator: validator
  • amount: amount
  • sender: sender
  • memo: memo, defaults to None
  • gas_limit: gas limit, defaults to None

Returns:

prepare and broadcast the transaction and transaction details

"},{"location":"CosmPy/api/aerial/client/__init__/#claim_rewards","title":"claim_rewards","text":"
def claim_rewards(validator: Address,\nsender: Wallet,\nmemo: Optional[str] = None,\ngas_limit: Optional[int] = None) -> SubmittedTx\n

claim rewards.

Arguments:

  • validator: validator
  • sender: sender
  • memo: memo, defaults to None
  • gas_limit: gas limit, defaults to None

Returns:

prepare and broadcast the transaction and transaction details

"},{"location":"CosmPy/api/aerial/client/__init__/#estimate_gas_for_tx","title":"estimate_gas_for_tx","text":"
def estimate_gas_for_tx(tx: Transaction) -> int\n

Estimate gas for transaction.

Arguments:

  • tx: transaction

Returns:

Estimated gas for transaction

"},{"location":"CosmPy/api/aerial/client/__init__/#estimate_fee_from_gas","title":"estimate_fee_from_gas","text":"
def estimate_fee_from_gas(gas_limit: int) -> str\n

Estimate fee from gas.

Arguments:

  • gas_limit: gas limit

Returns:

Estimated fee for transaction

"},{"location":"CosmPy/api/aerial/client/__init__/#estimate_gas_and_fee_for_tx","title":"estimate_gas_and_fee_for_tx","text":"
def estimate_gas_and_fee_for_tx(tx: Transaction) -> Tuple[int, str]\n

Estimate gas and fee for transaction.

Arguments:

  • tx: transaction

Returns:

estimate gas, fee for transaction

"},{"location":"CosmPy/api/aerial/client/__init__/#wait_for_query_tx","title":"wait_for_query_tx","text":"
def wait_for_query_tx(tx_hash: str,\ntimeout: Optional[timedelta] = None,\npoll_period: Optional[timedelta] = None) -> TxResponse\n

Wait for query transaction.

Arguments:

  • tx_hash: transaction hash
  • timeout: timeout, defaults to None
  • poll_period: poll_period, defaults to None

Raises:

  • QueryTimeoutError: timeout

Returns:

transaction response

"},{"location":"CosmPy/api/aerial/client/__init__/#query_tx","title":"query_tx","text":"
def query_tx(tx_hash: str) -> TxResponse\n

query transaction.

Arguments:

  • tx_hash: transaction hash

Raises:

  • NotFoundError: Tx details not found
  • grpc.RpcError: RPC connection issue

Returns:

query response

"},{"location":"CosmPy/api/aerial/client/__init__/#simulate_tx","title":"simulate_tx","text":"
def simulate_tx(tx: Transaction) -> int\n

simulate transaction.

Arguments:

  • tx: transaction

Raises:

  • RuntimeError: Unable to simulate non final transaction

Returns:

gas used in transaction

"},{"location":"CosmPy/api/aerial/client/__init__/#broadcast_tx","title":"broadcast_tx","text":"
def broadcast_tx(tx: Transaction) -> SubmittedTx\n

Broadcast transaction.

Arguments:

  • tx: transaction

Returns:

Submitted transaction

"},{"location":"CosmPy/api/aerial/client/bank/","title":"Bank send message","text":""},{"location":"CosmPy/api/aerial/client/bank/#cosmpyaerialclientbank","title":"cosmpy.aerial.client.bank","text":"

Bank send message.

"},{"location":"CosmPy/api/aerial/client/bank/#create_bank_send_msg","title":"create_bank_send_msg","text":"
def create_bank_send_msg(from_address: Address, to_address: Address,\namount: int, denom: str) -> MsgSend\n

Create bank send message.

Arguments:

  • from_address: from address
  • to_address: to address
  • amount: amount
  • denom: denom

Returns:

bank send message

"},{"location":"CosmPy/api/aerial/client/distribution/","title":"Distribution","text":""},{"location":"CosmPy/api/aerial/client/distribution/#cosmpyaerialclientdistribution","title":"cosmpy.aerial.client.distribution","text":"

Distribution.

"},{"location":"CosmPy/api/aerial/client/distribution/#create_withdraw_delegator_reward","title":"create_withdraw_delegator_reward","text":"
def create_withdraw_delegator_reward(delegator: Address, validator: Address)\n

Create withdraw delegator reward.

Arguments:

  • delegator: delegator address
  • validator: validator address

Returns:

withdraw delegator reward message

"},{"location":"CosmPy/api/aerial/client/staking/","title":"Staking functionality","text":""},{"location":"CosmPy/api/aerial/client/staking/#cosmpyaerialclientstaking","title":"cosmpy.aerial.client.staking","text":"

Staking functionality.

"},{"location":"CosmPy/api/aerial/client/staking/#validatorstatus-objects","title":"ValidatorStatus Objects","text":"
class ValidatorStatus(Enum)\n

Validator status.

"},{"location":"CosmPy/api/aerial/client/staking/#from_proto","title":"from_proto","text":"
@classmethod\ndef from_proto(cls, value: int) -> \"ValidatorStatus\"\n

Get the validator status from proto.

Arguments:

  • value: value

Raises:

  • RuntimeError: Unable to decode validator status

Returns:

Validator status

"},{"location":"CosmPy/api/aerial/client/staking/#create_delegate_msg","title":"create_delegate_msg","text":"
def create_delegate_msg(delegator: Address, validator: Address, amount: int,\ndenom: str) -> MsgDelegate\n

Create delegate message.

Arguments:

  • delegator: delegator
  • validator: validator
  • amount: amount
  • denom: denom

Returns:

Delegate message

"},{"location":"CosmPy/api/aerial/client/staking/#create_redelegate_msg","title":"create_redelegate_msg","text":"
def create_redelegate_msg(delegator_address: Address,\nvalidator_src_address: Address,\nvalidator_dst_address: Address, amount: int,\ndenom: str) -> MsgBeginRedelegate\n

Create redelegate message.

Arguments:

  • delegator_address: delegator address
  • validator_src_address: source validation address
  • validator_dst_address: destination validation address
  • amount: amount
  • denom: denom

Returns:

Redelegate message

"},{"location":"CosmPy/api/aerial/client/staking/#create_undelegate_msg","title":"create_undelegate_msg","text":"
def create_undelegate_msg(delegator_address: Address,\nvalidator_address: Address, amount: int,\ndenom: str) -> MsgUndelegate\n

Create undelegate message.

Arguments:

  • delegator_address: delegator address
  • validator_address: validator address
  • amount: amount
  • denom: denom

Returns:

Undelegate message

"},{"location":"CosmPy/api/aerial/client/utils/","title":"Helper functions","text":""},{"location":"CosmPy/api/aerial/client/utils/#cosmpyaerialclientutils","title":"cosmpy.aerial.client.utils","text":"

Helper functions.

"},{"location":"CosmPy/api/aerial/client/utils/#prepare_and_broadcast_basic_transaction","title":"prepare_and_broadcast_basic_transaction","text":"
def prepare_and_broadcast_basic_transaction(\nclient: \"LedgerClient\",\ntx: \"Transaction\",\nsender: \"Wallet\",\naccount: Optional[\"Account\"] = None,\ngas_limit: Optional[int] = None,\nmemo: Optional[str] = None) -> SubmittedTx\n

Prepare and broadcast basic transaction.

Arguments:

  • client: Ledger client
  • tx: The transaction
  • sender: The transaction sender
  • account: The account
  • gas_limit: The gas limit
  • memo: Transaction memo, defaults to None

Returns:

broadcast transaction

"},{"location":"CosmPy/api/aerial/client/utils/#ensure_timedelta","title":"ensure_timedelta","text":"
def ensure_timedelta(interval: Union[int, float, timedelta]) -> timedelta\n

Return timedelta for interval.

Arguments:

  • interval: timedelta or seconds in int or float

Returns:

timedelta

"},{"location":"CosmPy/api/aerial/client/utils/#get_paginated","title":"get_paginated","text":"
def get_paginated(\ninitial_request: Any,\nrequest_method: Callable,\npages_limit: int = 0,\nper_page_limit: Optional[int] = DEFAULT_PER_PAGE_LIMIT) -> List[Any]\n

Get pages for specific request.

Arguments:

  • initial_request: request supports pagination
  • request_method: function to perform request
  • pages_limit: max number of pages to return. default - 0 unlimited
  • per_page_limit: Optional int: amount of records per one page. default is None, determined by server

Returns:

List of responses

"},{"location":"CosmPy/api/aerial/contract/__init__/","title":"Cosmwasm contract functionality","text":""},{"location":"CosmPy/api/aerial/contract/__init__/#cosmpyaerialcontract__init__","title":"cosmpy.aerial.contract.__init__","text":"

cosmwasm contract functionality.

"},{"location":"CosmPy/api/aerial/contract/__init__/#ledgercontract-objects","title":"LedgerContract Objects","text":"
class LedgerContract(UserString)\n

Ledger contract.

"},{"location":"CosmPy/api/aerial/contract/__init__/#__init__","title":"__init__","text":"
def __init__(path: Optional[str],\nclient: LedgerClient,\naddress: Optional[Address] = None,\ndigest: Optional[bytes] = None,\nschema_path: Optional[str] = None,\ncode_id: Optional[int] = None)\n

Initialize the Ledger contract.

Arguments:

  • path: Path
  • client: Ledger client
  • address: address, defaults to None
  • digest: digest, defaults to None
  • schema_path: path to contract schema, defaults to None
  • code_id: optional int. code id of the contract stored

"},{"location":"CosmPy/api/aerial/contract/__init__/#path","title":"path","text":"
@property\ndef path() -> Optional[str]\n

Get contract path.

Returns:

contract path

"},{"location":"CosmPy/api/aerial/contract/__init__/#digest","title":"digest","text":"
@property\ndef digest() -> Optional[bytes]\n

Get the contract digest.

Returns:

contract digest

"},{"location":"CosmPy/api/aerial/contract/__init__/#code_id","title":"code_id","text":"
@property\ndef code_id() -> Optional[int]\n

Get the code id.

Returns:

code id

"},{"location":"CosmPy/api/aerial/contract/__init__/#address","title":"address","text":"
@property\ndef address() -> Optional[Address]\n

Get the contract address.

Returns:

contract address

"},{"location":"CosmPy/api/aerial/contract/__init__/#store","title":"store","text":"
def store(sender: Wallet,\ngas_limit: Optional[int] = None,\nmemo: Optional[str] = None) -> int\n

Store the contract.

Arguments:

  • sender: sender wallet address
  • gas_limit: transaction gas limit, defaults to None
  • memo: transaction memo, defaults to None

Raises:

  • RuntimeError: Runtime error

Returns:

code id

"},{"location":"CosmPy/api/aerial/contract/__init__/#instantiate","title":"instantiate","text":"
def instantiate(args: Any,\nsender: Wallet,\nlabel: Optional[str] = None,\ngas_limit: Optional[int] = None,\nadmin_address: Optional[Address] = None,\nfunds: Optional[str] = None) -> Address\n

Instantiate the contract.

Arguments:

  • args: args
  • sender: sender wallet address
  • label: label, defaults to None
  • gas_limit: transaction gas limit, defaults to None
  • admin_address: admin address, defaults to None
  • funds: funds, defaults to None

Raises:

  • RuntimeError: Unable to extract contract code id

Returns:

contract address

"},{"location":"CosmPy/api/aerial/contract/__init__/#upgrade","title":"upgrade","text":"
def upgrade(args: Any,\nsender: Wallet,\nnew_path: str,\ngas_limit: Optional[int] = None) -> SubmittedTx\n

Store new contract code and migrate the current contract address.

Arguments:

  • args: args
  • sender: sender wallet address
  • new_path: path to new contract
  • gas_limit: transaction gas limit, defaults to None

Raises:

  • RuntimeError: Unable to extract contract code id

Returns:

contract address

"},{"location":"CosmPy/api/aerial/contract/__init__/#migrate","title":"migrate","text":"
def migrate(args: Any,\nsender: Wallet,\nnew_code_id: int,\ngas_limit: Optional[int] = None) -> SubmittedTx\n

Migrate the current contract address to new code id.

Arguments:

  • args: args
  • sender: sender wallet address
  • new_code_id: Code id of the newly deployed contract
  • gas_limit: transaction gas limit, defaults to None

Raises:

  • RuntimeError: Unable to extract contract code id

Returns:

contract address

"},{"location":"CosmPy/api/aerial/contract/__init__/#deploy","title":"deploy","text":"
def deploy(args: Any,\nsender: Wallet,\nlabel: Optional[str] = None,\nstore_gas_limit: Optional[int] = None,\ninstantiate_gas_limit: Optional[int] = None,\nadmin_address: Optional[Address] = None,\nfunds: Optional[str] = None) -> Address\n

Deploy the contract.

Arguments:

  • args: args
  • sender: sender address
  • label: label, defaults to None
  • store_gas_limit: store gas limit, defaults to None
  • instantiate_gas_limit: instantiate gas limit, defaults to None
  • admin_address: admin address, defaults to None
  • funds: funds, defaults to None

Returns:

instantiate contract details

"},{"location":"CosmPy/api/aerial/contract/__init__/#execute","title":"execute","text":"
def execute(args: Any,\nsender: Wallet,\ngas_limit: Optional[int] = None,\nfunds: Optional[str] = None) -> SubmittedTx\n

execute the contract.

Arguments:

  • args: args
  • sender: sender address
  • gas_limit: transaction gas limit, defaults to None
  • funds: funds, defaults to None

Raises:

  • RuntimeError: Contract appears not to be deployed currently

Returns:

transaction details broadcast

"},{"location":"CosmPy/api/aerial/contract/__init__/#query","title":"query","text":"
def query(args: Any) -> Any\n

Query on contract.

Arguments:

  • args: args

Raises:

  • RuntimeError: Contract appears not to be deployed currently

Returns:

query result

"},{"location":"CosmPy/api/aerial/contract/__init__/#data","title":"data","text":"
@property\ndef data()\n

Get the contract address.

Returns:

contract address

"},{"location":"CosmPy/api/aerial/contract/__init__/#__json__","title":"__json__","text":"
def __json__()\n

Get the contract details in json.

Returns:

contract details in json

"},{"location":"CosmPy/api/aerial/contract/cosmwasm/","title":"Cosmwasm contract store, instantiate, execute messages","text":""},{"location":"CosmPy/api/aerial/contract/cosmwasm/#cosmpyaerialcontractcosmwasm","title":"cosmpy.aerial.contract.cosmwasm","text":"

Cosmwasm contract store, instantiate, execute messages.

"},{"location":"CosmPy/api/aerial/contract/cosmwasm/#create_cosmwasm_store_code_msg","title":"create_cosmwasm_store_code_msg","text":"
def create_cosmwasm_store_code_msg(contract_path: str,\nsender_address: Address) -> MsgStoreCode\n

Create cosmwasm store code message.

Arguments:

  • contract_path: contract path
  • sender_address: sender address

Returns:

cosmwasm store code message

"},{"location":"CosmPy/api/aerial/contract/cosmwasm/#create_cosmwasm_instantiate_msg","title":"create_cosmwasm_instantiate_msg","text":"
def create_cosmwasm_instantiate_msg(\ncode_id: int,\nargs: Any,\nlabel: str,\nsender_address: Address,\nfunds: Optional[str] = None,\nadmin_address: Optional[Address] = None) -> MsgInstantiateContract\n

Create cosmwasm instantiate message.

Arguments:

  • code_id: code id
  • args: args
  • label: label
  • sender_address: sender address
  • funds: funds, defaults to None
  • admin_address: admin address, defaults to None

Returns:

cosmwasm instantiate message

"},{"location":"CosmPy/api/aerial/contract/cosmwasm/#create_cosmwasm_migrate_msg","title":"create_cosmwasm_migrate_msg","text":"
def create_cosmwasm_migrate_msg(code_id: int, args: Any,\ncontract_address: Address,\nsender_address: Address) -> MsgMigrateContract\n

Create cosmwasm migrate message.

Arguments:

  • code_id: code id
  • args: args
  • contract_address: sender address
  • sender_address: sender address

Returns:

cosmwasm migrate message

"},{"location":"CosmPy/api/aerial/contract/cosmwasm/#create_cosmwasm_execute_msg","title":"create_cosmwasm_execute_msg","text":"
def create_cosmwasm_execute_msg(\nsender_address: Address,\ncontract_address: Address,\nargs: Any,\nfunds: Optional[str] = None) -> MsgExecuteContract\n

Create cosmwasm execute message.

Arguments:

  • sender_address: sender address
  • contract_address: contract address
  • args: args
  • funds: funds, defaults to None

Returns:

cosmwasm execute message

"},{"location":"Jenesis/","title":"Introduction","text":"

Jenesis is a command line tool for rapid contract and service development for the Fetch.ai blockchain ecosystem and other CosmWasm-enabled blockchains.

"},{"location":"Jenesis/#system-requirements","title":"System Requirements","text":"

Jenesis currently requires:

  • OS: Linux, MacOS
  • Python: 3.8 to 3.10
  • Docker: 20.10.22 or higher recommended
  • git: Any
"},{"location":"Jenesis/#installation","title":"Installation","text":"

Install via PyPI:

pip install jenesis\n
"},{"location":"Jenesis/#getting-started","title":"Getting started","text":"

There are multiple commands integrated into jenesis that allow you to perform a variety of tasks these commands are:

  • new
  • init
  • add
  • update
  • attach
  • compile
  • keys
  • deploy
  • run
  • shell
  • network
"},{"location":"Jenesis/#create-a-new-project","title":"Create a new project","text":"

Create a project using the new command

jenesis new my_project [--profile my_profile] [--network network_name]\n

This will create a new directory called my_project. You can use --profile and --network optional arguments; when they aren't used, profile and network will be set to testing and fetchai-testnet respectively. Inside this directory a jenesis.toml file will be created containing the following information:

[project]\nname = \"my_project\"\nauthors = [ \"Alice Tyler <alice@mail.com>\"]\nkeyring_backend = \"os\"\n[profile.my_profile]\ndefault = true\n[profile.my_profile.network]\nname = \"fetchai-testnet\"\nchain_id = \"dorado-1\"\nfee_minimum_gas_price = 5000000000\nfee_denomination = \"atestfet\"\nstaking_denomination = \"atestfet\"\nurl = \"grpc+https://grpc-dorado.fetch.ai\"\nfaucet_url = \"https://faucet-dorado.fetch.ai\"\nis_local = false\n[profile.my_profile.contracts]\n

The project name is the argument passed to the new command while the authors field is populated by querying the user's GitHub username and email address. The profile's network will be filled with the relevant configuration variables. The contracts field will remain empty until new contracts are added. This my_profile profile will be set as the default profile, this means that every time you use a jenesis command without specifying a profile, my_profile will be used.

An empty contracts folder will also be created inside my_project directory that will eventually contain all the information needed to compile and deploy the desired contracts.

The init command is similar to the new command, but in this case, you won't need a project name argument since this command is intended to run inside an existing project directory.

jenesis init [--profile my_profile] [--network network_name]\n

This command will create the same files and folders inside your project directory as the ones described for the new command.

If using a cargo workspace, you just need to navigate to the top level of your project and run the init command shown above. This will create the jenesis.toml configuration file inside your workspace including all the relevant information from existing contracts.

"},{"location":"Jenesis/#configure-a-network","title":"Configure a network","text":"

By default, jenesis will configure the project to run on the latest stable Fetch.ai testnet. Use fetchai-mainnet to configure for the Fetch.ai mainnet or directly edit the jenesis.toml file to configure for other networks.

To test on a local node, pass the argument --network fetchai-localnode when creating a project:

jenesis new my_project --network fetchai-localnode\n
or
jenesis init --network fetchai-localnode\n

The configuration can be found under the network heading in the jenesis.toml file and can be changed as desired:

[profile.testing.network]\nname = \"fetchai-localnode\"\nchain_id = \"localnode\"\nfee_minimum_gas_price = 5000000000\nfee_denomination = \"atestfet\"\nstaking_denomination = \"atestfet\"\nurl = \"grpc+http://127.0.0.1:9090/\"\nis_local = true\nkeep_running = false\ncli_binary = \"fetchd\"\nvalidator_key_name = \"validator\"\nmnemonic = \"gap bomb bulk border original scare assault pelican resemble found laptop skin gesture height inflict clinic reject giggle hurdle bubble soldier hurt moon hint\"\npassword = \"12345678\"\nmoniker = \"test-node\"\ngenesis_accounts = [ \"fetch1vas6cc9650z0s08230ytqjphgzl5tcq9crqhhu\",]\ntimeout_commit = \"5s\"\ndebug_trace = true\n
In particular, to fund some accounts for testing, replace the genesis_accounts field with the addresses to be funded.

When running any of the commands deploy, run, shell, and attach, jenesis will check for a currently running local node, and if there is none, a new one will be created in a docker container. If you wish to keep a local node running, you need to set the keep_running parameter to true. Otherwise, nodes will be stopped after any of the command mentioned above finish running.

At any time, you can start or stop a local node by running:

jenesis network start [--profile my_profile]\n
or
jenesis network stop [--profile my_profile]\n

To view the logs from the local node, run:

jenesis network logs [--profile my_profile]\n

"},{"location":"Jenesis/add-contracts/","title":"Add contract templates","text":"

Once you have successfully created your project, you can add contract templates. You first need to navigate to your project's directory and run the following command:

jenesis add contract <template> <contract_name>\n
You can find all the contract templates available in Jenesis Templates. An example of how to add the template cw20-base with the name my_token is given below:

jenesis add contract cw20-base my_token\n

If you need multiple deployments of the same contract, you can use the --deployments or -d flag to specify multiple deployments and name them.

jenesis add contract <template> <contract_name> [--deployments <deployments>]\n
Jenesis will add the deployments to all profiles for the specified contract. In the example below, token_1 and token_2 deployments have been added. This will allow you to deploy my_token contract with two different configurations. You can add as many deployments as you wish.
jenesis add contract cw20-base my_token -d token_1 token_2\n

If no deployments are selected when adding a contract, the default deployment name will be equal to the contract name.

This add contract command will add a contract template to your jenesis project inside contracts/my_token/ folder. It will also update the jenesis.toml configuration file with the contract information.

[profile.testing.contracts.token_1]\nname = \"token_1\"\ncontract = \"my_token\"\nnetwork = \"fetchai-testnet\"\ndeployer_key = \"\"\ninit_funds = \"\"\n[profile.testing.contracts.token_2]\nname = \"token_2\"\ncontract = \"my_token\"\nnetwork = \"fetchai-testnet\"\ndeployer_key = \"\"\ninit_funds = \"\"\n[profile.testing.contracts.token_1.init]\n[profile.testing.contracts.token_2.init]\n

The deployer_key field can be manually specified, you can choose any private key locally available to deploy any specific contract. You can also leave this field empty since the deploy command has an optional argument to deploy all contracts inside a specified profile with the same key, overriding this deployer_key argument in the jenesis.toml file. See deploy contracts for more information.

Finally, the init section contains the parameters needed in the instantiation message for this contract to be deployed. The required parameters are taken from the schema file inside the contracts directory. Since this contract template doesn't include a schema, it will be generated when compiling the my_token contract loading the init fields to the jenesis.toml file. You will need to manually add the values for these parameters in their correct variable type, which are listed on the schema file. For this my_token contract, we need to fill the following init fields for each deployment after compiling. Here is an example:

[profile.testing.contracts.token_1.init]\ndecimals = 6\nname = \"my_token_name\"\nsymbol = \"SYMBOL\"\ninitial_balances = [{ address = \"fetch1d25ap9danl4726uk2nt307y630v87h3h2vq6pl\", amount =  \"5000\"}]\n\n[profile.testing.contracts.token_2.init]\ndecimals = 6\nname = \"my_token_name_2\"\nsymbol = \"SYMBOL\"\ninitial_balances = [{ address = \"fetch1d25ap9danl4726uk2nt307y630v87h3h2vq6pl\", amount =  \"2000\"}]\n

If your contract requires nested instantiation messages you may add fields following this structure:

[profile.testing.contracts.example-nested-contract.init]\nprice = {amount = 1000, denom = DLS}\ninfo = {performance = {max_speed = 200, unit = kph}, fuel = {consumption = 7, unit = kmpl}}\n

NOTE: Before editing the jenesis.toml configuration file with the desired deployer_key and init parameters, make sure to first compile your contract. All configuration parameters will restart every time a contract is compiled if the schema has changed.

You can also add contracts manually by copying and pasting the contract directory from another project you may have, however, they need to follow the same directory structure as the starter template mentioned above.

When you add a contract manually, you need to update the jenesis.toml file with the contract information by running:

jenesis update\n
The update command will automatically detect which contract is missing in the jenesis.toml configuration file by revising the contracts directory.

"},{"location":"Jenesis/add-contracts/#add-contract-deployments","title":"Add contract deployments","text":"

You can also add further deployments for a given contract by specifying the contract name and the deployment name. If we want to add a third token called token_3 using my_token contract, we can run:

jenesis add deployment my_token token_3\n
This will automatically create another deployment entry called token_3.

"},{"location":"Jenesis/add-contracts/#attach-deployed-contracts","title":"Attach deployed contracts","text":"

If you add a contract into the project's contract folder that has already been deployed in the network, you can attach the deployment to your project for future interaction using the attach command.

To add a deployment to yout project you can run:

jenesis add contract cw20-base my_token -d token_1\n

Then compile the contract:

jenesis compile\n

To attach the contract, you will need to specify the deployment's name and address. You can optionally specify the profile where you wish to insert the contract into. If this is not specified, the deployment will be attached to the default profile, which is the first profile created in your project, unless the default settings are manually changed.

jenesis attach token_1 fetch18xs97q6h9zgh4sz730a42pp0dqa9sh4eef7eutfkv69q3v2y3x8s72pkua\n

This will add the relevant deployment information into a jenesis.lock file and you will now be able to interact with token_1 using contract interactions.

"},{"location":"Jenesis/add-profile/","title":"Add profiles","text":"

You can add more profiles than the one specified using the new command by running the following add profile command:

jenesis add profile my_second_profile\n
By default, the profile's network will be set to fetchai-testnet, but you can specify it using the --network optional argument. The following will be added to the existing information in your jenesis.toml file:

[profile.my_second_profile.network]\nname = \"fetchai-testnet\"\nchain_id = \"dorado-1\"\nfee_minimum_gas_price = 5000000000\nfee_denomination = \"atestfet\"\nstaking_denomination = \"atestfet\"\nurl = \"grpc+https://grpc-dorado.fetch.ai\"\nfaucet_url = \"https://faucet-dorado.fetch.ai\"\nis_local = false\n[profile.my_second_profile.contracts]\n

Currently available network configurations are fetchai-testnet, fetchai-mainnet, and fetchai-localnode, but Jenesis is easily configurable for other networks by directly editing the jenesis.toml file.

"},{"location":"Jenesis/compile-contracts/","title":"Compile contracts","text":""},{"location":"Jenesis/compile-contracts/#compile-contracts","title":"Compile contracts","text":"

Compile your contracts by running the following command inside your project directory:

jenesis compile [--optimize] [--rebuild] [--no-log]\n
This will compile all packages in your project's contracts directory and output the wasm code under the artifacts directory. If using a cargo workspace, jenesis will automatically detect this and the compiled contracts will appear in the contracts/artifacts/. Otherwise, they will go to the artifacts directory under the individual contracts.

By default, the contracts are simply compiled and not optimized. For an optimized build, use the flag --optimize or -o. To force a rebuild, use the flag --rebuild or -r. To suppress contract compilation logs, use the flag --no-log. In case of compilation failure, the logs will show by default.

Note: jenesis compile requires that docker is running and configured with permissions for your user.

"},{"location":"Jenesis/deploy-contracts/","title":"Deploy contracts","text":"

Once you have successfully compiled your contracts, make sure to fill out the necessary instantiation message information under the init field in the jenesis.toml file.

*Note: jenesis deploy currently requires that each contract's directory name matches the .wasm file name under the artifacts directory.

To deploy all the contracts inside a profile you have two options:

  1. Fill the deployer_key field for each contract inside the jenesis.toml file (keys can be different for each contract) and run the following command:

jenesis deploy [--profile profile_name]\n
Each contract inside the specified profile will be deployed with the specified key.

  1. Simply specify a certain key as an argument of the deploy command:
jenesis deploy key_name [--profile profile_name]\n

The deployer_key field will be ignored in this case and all contracts inside the specified profile will be deployed using the key key_name.

After running either of the commands mentioned above, all the deployment information will be saved in the jenesis.lock file inside your project's directory

[profile.testing.my_first_contract]\nchecksum = \"ecf640a7512be3777c72ec42aff01fdb22897b71953011af3c41ee5dbf3d3bc5\"\ndigest = \"be4a4bdfeb4ed8f504c7b7ac84e31ad3876627398a6586b49cac586633af8b85\"\naddress = \"fetch16l239ggyr4z7pvsxec0ervlyw03mn6pz62l9ss6la94cf06awv0q36cq7u\"\ncode_id = 2594\n
"},{"location":"Jenesis/deploy-contracts/#deploy-contracts-that-depend-on-other-deployments","title":"Deploy contracts that depend on other deployments","text":"

You can point to other contract addresses in any contract's instantiation message if required. For example: if you have contracts A, B, and C within your project, but contract A requires contract's B deployment address in its instantiation message and contract B requires contract's C deployment address, they will need to be deployed in the following order: C, B, A. In order to provide this information to Jenesis you will need to specify where exactly these contract addresses need to be inserted inside the instantiation messages. You can do this by writing the $ symbol followed by the contract name in the corresponding field in the init parameters:

[profile.testing.contracts.A.init]\nname = \"A\"\ntoken_contract_address = \"$B\"\n[profile.testing.contracts.B.init]\ntoken_name = \"my_token\"\nliquidity_contract_address = \"$C\"\n[profile.testing.contracts.C.init]\ncount = 5\n

Finally, Jenesis will detect this information and deploy the contracts in the correct order: C, B, A.

"},{"location":"Jenesis/keys/","title":"Keys","text":"

With the keys command you can either list all the locally available keys or show the address of a specific key. To list all the keys available run the following command:

jenesis keys list\n

To look up the address for a specified key you can use the show command and pass the key name as an argument:

jenesis keys show my_key\n
To access other key functionalities such as adding new keys, looking up an address, and recovering keys you can use fetchd CLI - Managing Keys

"},{"location":"Jenesis/use-contracts/","title":"Contract Interaction","text":"

You can interact with your project's contracts by using the shell or run commands.

"},{"location":"Jenesis/use-contracts/#previous-steps","title":"Previous steps","text":"

To reproduce the examples in this document, add and compile a basic starter contract and a cw20 token contract to your project with the following commands:

jenesis add contract starter my_first_contract -d deployment_1\njenesis add contract cw20-base my_token -d token_1\njenesis compile\n

For more contract template examples visit Jenesis Templates

"},{"location":"Jenesis/use-contracts/#interactive-shell","title":"Interactive Shell","text":"

To open a shell where you can easily interact with your contracts, run:

jenesis shell\n
If a profile is not selected, the default profile will be selected automatically. You can specify any profile using the --profile optional argument:

jenesis shell --profile my_profile\n

You will observe the following text indicating the available contracts in your project.

Network: fetchai-testnet\nDetecting contracts...\nC deployment_1\nC token_1\nDetecting contracts...complete\n

NOTE: jenesis shell currently requires that contract names use accepted python variable names. For example, using token-1 instead of token_1 will generate an error when trying to interact with it.

In this case, we can see that deployment_1 and token_1 deployments are available for this project. If these contracts have been already deployed you can directly interact with them by performing contract querys and executions such as:

>>> deployment_1.query(args = {'msg_name': {...}}\n>>> deployment_1.execute(args = {'msg_name': {...}}\n

A ledger client (ledger) and your locally stored wallet keys will also be available in the shell. For example, if you have a local key named alice, you will find this under wallets['alice'] and you can query the balance as follows:

>>> ledger.query_bank_balance(wallets['alice'])\n10000000000000000000\n

If the ledger is a testnet with a faucet url, you can get funds using the faucet:

>>> faucet.get_wealth(wallets['alice'])\n

"},{"location":"Jenesis/use-contracts/#dynamic-methods","title":"Dynamic Methods","text":"

Jenesis also attaches the contract query, execution and deploy messages as dynamic methods.

For example, the following query

>>> token_1.query({\"balance\": {\"address\": str(wallet.address())}}))\n
can also be run with:
>>> token_1.balance(address=str(wallet.address()))\n{'balance': '4000'}\n

Similarly, instead of using token_1.execute... , a transfer can be executed with:

>>> token_1.transfer(amount='1000', recipient=str(wallet2.address()), sender=wallet)\n

Jensesis also has an autocompletion helper for query, execution and deployment arguments. It will show automatically when typing in the shell.

We will now show an example assuming that the token_1 deployment contract has only been compiled and not yet deployed, going through deployment, execution, and querying using dynamic methods.

For this example, we will first generate two wallets. We provide wealth to the sender wallet in atestfet so it can pay for transaction fees.

>>> wallet = LocalWallet.generate()\n>>> wallet2 = LocalWallet.generate()\n>>> faucet.get_wealth(wallet)\n

We now proceed to deploy my_token contract using token_1 deployment configuration, we define the arguments for the cw20 token: name, symbol, decimal, and the addresses that will be funded with these cw20 tokens. In this case we will fund wallet's address with 5000 tokens.

>>> token_1.deploy(name=\"Crab Coin\", symbol=\"CRAB\", decimals=6, initial_balances=[{ \"address\": str(wallet.address()), \"amount\" :  \"5000\"}], sender=wallet)\n

We can query wallet balance to make sure it has been funded with cw20 tokens

>>> token_1.balance(address=str(wallet.address()))\n{'balance': '5000'}\n

We now execute a cw20 token transfer of 1000 tokens from wallet to wallet2

>>> token_1.transfer(amount='1000', recipient=str(wallet2.address()), sender=wallet)\n

Finally, we query both wallet's balance

>>> token_1.balance(address=str(wallet.address()))\n{'balance': '4000'}\n>>> token_1.balance(address=str(wallet2.address()))\n{'balance': '1000'}\n
We can observe that wallet has sent 1000 tokens to wallet2.

"},{"location":"Jenesis/use-contracts/#executing-scripts","title":"Executing Scripts","text":"

You can also assemble the above commands into a script that is executable by the run command:

from cosmpy.aerial.wallet import LocalWallet\nwallet = LocalWallet.generate()\nfaucet.get_wealth(wallet.address())\nwallet2 = LocalWallet.generate()\ntoken_1.deploy(name=\"Crab Coin\", symbol=\"CRAB\", decimals=6, initial_balances=[{ \"address\": str(wallet.address()), \"amount\" :  \"5000\"}], sender=wallet)\nprint(\"wallet initial cw20 MT balance: \", token_1.balance(address=str(wallet.address())))\ntx = token_1.transfer(amount='1000', recipient=str(wallet2.address()), sender=wallet)\nprint(\"transfering 1000 cw20 MT tokens from wallet to wallet2\")\ntx.wait_to_complete()\nprint(\"wallet final cw20 MT balance: \", token_1.balance(address=str(wallet.address())))\nprint(\"wallet2 final cw20 MT balance: \", token_1.balance(address=str(wallet2.address())))\n

If we paste the above code into the file script.py inside the project's directory, we can run it with:

jenesis run script.py\n

And you will observe the same output as before. You can also specify the profile as an optional argument using --profile.

Finally, you can pass arguments to the script just as you would to a standard python script by placing all the arguments to the script after the -- delimiter:

jenesis run script.py [--profile profile_name] -- arg1 arg2 --key1 value1 --key2 value2\n

You can visit CosmPy for more contract interaction examples.

"},{"location":"aea-framework-documentation/","title":"AEA Framework Documentation","text":"

Vision

Our aim with the AEA framework is to enable businesses of all sizes, from independent developers to large corporations and consortiums, to create and deploy agent-based solutions in various domains, thus contributing to and advancing a decentralized mixed-initiative economy: one whose actors are both humans and machines.

"},{"location":"aea-framework-documentation/#what-is-an-aea","title":"What is an AEA?","text":"

Definition

An Autonomous Economic Agent (AEA) is an intelligent agent that acts on its owner's behalf, with limited or no interference, and whose goal is to generate economic value for its owner.

Breaking it down:

AGENT: An AEA represents an individual, organisation or object and looks after their interests.

AUTONOMOUS: AEAs operate independently of constant input from their owners and act autonomously to achieve their goals.

ECONOMIC: AEAs have a narrow and specific focus: creating economic value for their owner.

"},{"location":"aea-framework-documentation/#what-can-you-do-with-aeas","title":"What Can You Do with AEAs?","text":"

Some examples of the kinds of applications you can build with AEAs:

Automation

AEAs can automate well-defined processes in different domains, such as supply chain, mobility, finance, ...

Micro-transactions

AEAs make it economically viable to execute trade involving small values. An example is use-cases with many small sellers (e.g. of data) on the supply side.

Wallet

AEAs can simplify interactions with blockchains. By acting as \"smart wallets\", they can hide away the majority of the complexities involved in using blockchains for end users.

IoT

Agents representing objects in the IoT (Internet of Things) space. For example, AEAs paired with hardware devices such as drones, laptops, heat sensors, etc., providing control and receiving data from the device. An example is a thermometer agent.

Web 2.0 <--> Web 3.0 interface

Agents that interface and bridge the gap between existing (Web 2.0) and new (Web 3.0) economic models. An example is an AEA that communicates with HTTP clients/servers.

Traders

Agents with access to some data sources that sell the data, access to the data, or access to the usage of the data. An example is an AEA that continuously sells data to another AEA, who in turn uses it to improve their reinforcement learning model.

"},{"location":"aea-framework-documentation/#who-is-this-for","title":"Who is This For?","text":"

The AEA technology is for anyone who wants to build or contribute to a \"mixed-initiative economy\": one whose actors are humans as well as machines.

This includes (amongst others): developers, data scientists and machine learning experts, economists, students, academics and researchers (in Artificial Intelligence, Machine Learning, Multi-Agent Systems, etc), engineers, and so forth.

"},{"location":"aea-framework-documentation/#the-aea-framework","title":"The AEA Framework","text":"

The AEA framework is a development suite which equips you with an efficient and accessible set of tools for building and running AEAs and their components.

The framework attempts to make agent development as straightforward an experience as possible, similar to what popular web frameworks enable for web development.

Some of the characteristics of the AEA framework are:

  • Python: Using Python as an approachable programming language improves the on-boarding for those who just want to get started with agent development.
  • Open source: The framework is open source and licensed under Apache 2.0.
  • Modular: Modularity is at the heart of the framework's design. This makes it easy to extend the framework, add new functionality, and re-use others' contributions, therefore reducing the development cost.
  • Blockchain ready: Integration with blockchains is baked into the framework, enabling the creation of agents that take full advantage of the blockchain technology.
  • Modern: The framework is built from and can be integrated with the latest technologies (e.g. asynchronous programming, blockchains and smart contracts, machine-learning ready, ...).
"},{"location":"aea-framework-documentation/#the-ecosystem","title":"The Ecosystem","text":"

Though they can work in isolation, AEAs are truly valuable when situated in a wider ecosystem consisting of tools and infrastructure that enable them to cooperate and compete, and interact with services as well as traditional or modern systems. These include:

  • The Agent Communication Network (ACN): A peer-to-peer communication infrastructure that enables AEAs to directly communicate with one another without any intermediaries.
  • The sOEF: A search and discovery system allowing AEAs to register themselves and the services they offer, and search for agents who offer specific services.
  • The AEA Registry: A space to store and share AEAs or individual agent components for anyone to find and use.
  • Blockchains: AEAs can use blockchains as a financial and commitment layer. Each ledger plug-in provided by the framework adds the ability for AEAs to interact with a specific ledger, such as the Fetch.ai blockchain or Ethereum.
  • Smart Contracts: Contract packages are wrappers around smart contracts that allow AEAs to interact with them through a common interface.
"},{"location":"aea-framework-documentation/#how-to-get-involved","title":"How to get involved?","text":"

There are many ways for you to get involved. You can create agents, develop new agent components, extend existing components, and contribute to the development of the framework or other related tools. Please refer to the Contribution and Development guides.

"},{"location":"aea-framework-documentation/#next-steps","title":"Next Steps","text":"

To get started developing your own AEA, check out the getting started section.

To learn more about some of the distinctive characteristics of agent-oriented development, check out the guide on agent-oriented development.

If you would like to develop an AEA in a language different to Python then check out our language agnostic AEA definition.

If you want to run a demo, check out the demo guides.

"},{"location":"aea-framework-documentation/#help-us-improve","title":"Help us Improve","text":"

Note

This developer documentation is a work in progress. If you spot any errors please open an issue on Github or contact us in the developer Discord channel.

"},{"location":"aea-framework-documentation/12-factor/","title":"Relationship with the Twelve-Factor App Methodology","text":"

The Twelve-Factor App is a set of best practices to build modern web applications, or software-as-a-service.

In this section, we will see how the AEA framework facilitates the achievement of those in the development, release and deployment phases of an AEA project.

Note that an AEA instance, as a software agent, can be seen as a more general case of a web app, as it not only shows reactive behaviour, but it is also proactive, depending on the goals assigned to it.

"},{"location":"aea-framework-documentation/12-factor/#codebase","title":"Codebase","text":"

Factor 1

One codebase tracked in revision control, many deploys

Support: Excellent

The framework does not impose any particular requirement or convention on the type of version control software to be used to store an AEA project.

"},{"location":"aea-framework-documentation/12-factor/#dependencies","title":"Dependencies","text":"

Factor 2

Explicitly declare and isolate dependencies

Support: Good

The framework allows an AEA project to explicitly declare the AEA package dependencies, and the PyPI dependencies needed to proper working.

However, it does not provide built-in support for checking platform-specific dependencies, e.g. specific Python version, or needed system-wide available libraries. Nevertheless, this can be indirectly achieved by means of build scripts called on aea build, which can do the checks manually according to the specific requirements of the project.

"},{"location":"aea-framework-documentation/12-factor/#configuration","title":"Configuration","text":"

Factor 3

Store configuration in the environment

Support: Good

An AEA project can specify an environment configuration file .env, stored in the project root, that the framework will use to update environment variables before the execution of the AEA instance.

The CLI tool command aea run accepts the option --env PATH to change the default configuration file. However, the framework does not automatically switch between, nor allows to add, different types of configuration files, one for each deployment step (e.g. development, staging, production), without using the --env option.

"},{"location":"aea-framework-documentation/12-factor/#backing-services","title":"Backing Services","text":"

Factor 4

Treat backing services as attached resources

Support: Good

A persistent storage of an AEA can be seen as an attached resource in the 12-factor terminology. The default storage is SQLite, but the interface AbstractStorageBacked allows to implement specific wrappers to other backing services, without changing the AEA project code. The support for integrating different storage back-end implementations in an AEA project by using a plug-in mechanism is currently missing.

Moreover, new adapters to backing services can be implemented as custom connections, which can connect to attached resources. This does not usually require a change in the skill code, especially in the case when a custom protocol can abstract the details of the interaction with the specific resource.

"},{"location":"aea-framework-documentation/12-factor/#build-release-run","title":"Build, Release, Run","text":"

Factor 5

Strictly separate build and run stages

Support: Excellent

The phases of build, release and run of an AEA project are neatly separated, both for programmatic usage and through the usage of the CLI tool, as each of them corresponds to different subcommands.

"},{"location":"aea-framework-documentation/12-factor/#processes","title":"Processes","text":"

Factor 6

Execute the app as one or more stateless processes

Support: Excellent

Whether the process is stateless depends on the specific AEA. No strict enforcement is applied by the framework. Moreover, dialogue histories can be stored with persistent storage, if enabled by the developer.

"},{"location":"aea-framework-documentation/12-factor/#port-binding","title":"Port Binding","text":"

Factor 7

Export services via port binding

Support: Excellent

An AEA project may not need to expose services via HTTP. This property depends on the specific choices of the project developer, and the framework does not impose any restriction.

One of the provided package, the \"HTTP server\" connection, relies on aiohttp, which makes the connection completely self-contained\u2014therefore, it satisfies the requirement.

Another relevant example is the ACN node, which exposes its service to the Libp2p AEA connection

"},{"location":"aea-framework-documentation/12-factor/#concurrency","title":"Concurrency","text":"

Factor 8

Scale out via the process model

Support: Not Supported

The framework does not easily allow to scale up an AEA instance with multiple processes, as it is bound to a process. However, note that its attached services can live in a different process, which could give better scalability.

"},{"location":"aea-framework-documentation/12-factor/#disposability","title":"Disposability","text":"

Factor 9

Maximize robustness with fast startup and graceful shutdown

Support: Good

Disposability of an AEA instance depends, in general, on the AEA itself; whether the connections can be quickly connected and disconnected, whether skills can be easily torn down or not, whether other resources can be detached successfully like the persistent storage, just to name a few examples.

There has been put some effort into reducing startup time, and to ensure that a graceful shut-down can happen when the process receives a SIGTERM under normal circumstances, but robustness cannot be ensured for individual components, as it depends on their implementation.

Additionally, the framework does provide some features to control some aspects of AEA disposability, e.g. the possibility to change execution timeout for behaviours or handlers, implementation of an effective exception propagation from a component code to the main agent loop.

"},{"location":"aea-framework-documentation/12-factor/#devprod-parity","title":"Dev/Prod Parity","text":"

Factor 10

Keep development, staging, and production as similar as possible

Support: Good

This aspect mostly depends on the specific AEA project, and the framework does not impose particular restrictions on best deployment practices (e.g. continuous integration, same backing services between development and production stages).

"},{"location":"aea-framework-documentation/12-factor/#logs","title":"Logs","text":"

Factor 11

Treat logs as event streams

Support: Excellent

Thanks to the seamless integration with the Python standard library logging, the developer or the deployer has great control on the routing and filtering of log records. The behaviour can be changed by providing a proper configuration in the AEA project configuration file, according to the standard library specification. The framework facilitates this by creating ad-hoc logger names that can be used for finer-grained routing or filtering; for example, each AEA instance uses its own logging namespace to send logging events. Integration with other log handlers is delegated to extensions of the standard library, hence not necessarily coupled with the AEA framework.

"},{"location":"aea-framework-documentation/12-factor/#admin-processes","title":"Admin Processes","text":"

Factor 12

Run admin/management tasks as one-off processes

Support: Good

The CLI tool provides commands to manage private keys and ledger related operations, and it is possible to extend it with a plugin to manage databases of AEA's persistent storage for maintenance operations.

Moreover, the Python programming language makes it easy to run one-off scripts or running a console (also known as REPL) to do management tasks. It follows that it is also easy to ensure dependency isolation and same configurations of the running AEA instance.

"},{"location":"aea-framework-documentation/acn-internals/","title":"ACN Internals","text":"

The aim of this document is to describe at a high-level the main implementation of the Agent Communication Network (ACN).

In particular:

  • the libp2p_node Golang library;
  • the p2p_libp2p AEA connection written in Python, that implements the direct connection with an ACN peer;
  • the p2p_libp2p_client AEA connection written in Python, which implements the delegate connection with an ACN peer.

It is assumed the reader already knows what is the ACN and its purposes; if not, we suggest reading this page.

This documentation page is structured as follows:

  • Firstly, the ACN protocol is described: all the messages and data structures involved, as well as some example of interaction protocol with these messages;
  • Then, it is explained how a peer can join an existing ACN network, and the message exchange involved;
  • It follows the description of the journey of an envelope in the ACN network: from the agent connection to its contact peer, between ACN peers, and then from the contact peer of the destination agent to the target agent;
  • The following section describes the functionalities of the AEA connections that allow to communicate through the ACN: fetchai/p2p_libp2p and fetchia/p2p_libp2p_delegate;
  • The documentation ends with a section of known issues and limitations of the current implementation.
"},{"location":"aea-framework-documentation/acn-internals/#messages-and-data-structures","title":"Messages and Data Structures","text":"

At the foundation of the ACN there is the ACN protocol. The protocol messages and the reply structure are generated from this protocol specification, using the protocol generator. Therefore, it uses Protocol Buffers as a serialization format, and the definition of the data structures involved is defined in this .proto file.

To know more about the protocol generator, refer to the relevant section of the documentation: Protocol Generator.

"},{"location":"aea-framework-documentation/acn-internals/#agent-record","title":"Agent Record","text":"

An agent record is a data structure containing information about an agent and its Proof-of-Representation (PoR) to be used by a peer for other peers. This data structure is used as a payload in other ACN messages (see below).

The AgentRecord data structure contains the following fields:

  • service_id: a string describing the service identifier.
  • ledger_id: a string. It is the identifier of the ledger this agent record is associated to. Currently, the allowed values are:
    • fetchai, the identifier for the Fetch.AI ledger;
    • ethereum, the identifier for the Ethereum ledger;
    • cosmos, the identifier for the Cosmos ledger;
  • address: a string. It is the public key of a public-private key pair. It is used as an identifier for routing purposes.
  • public_key: a string. The representative's public key. Used in case of (PoR).
  • peer_public_key: a string. The public key of the peer.
  • signature: a string. The signature for PoR.
  • not_before: a string. Specify the lower bound for certificate validity. If it is a string, it must follow the format: YYYY-MM-DD. It will be interpreted as time zone UTC-0
  • not_after: a string. Specify the upper bound for certificate validity. If it is a string, it must follow the format: YYYY-MM-DD. It will be interpreted as time zone UTC-0.
"},{"location":"aea-framework-documentation/acn-internals/#acn-message","title":"ACN Message","text":"

Entities in the ACN (i.e. either agents or peers) exchange ACN messages. An ACN message contains a payload field, which is the actual content of the message.

There are different types of payloads:

  • Status
  • Register
  • LookupRequest
  • LookupResponse
  • AeaEnvelope
"},{"location":"aea-framework-documentation/acn-internals/#status","title":"Status","text":"

The Status payload is used as a response message to inform the sender about the handling of certain requests. The payload contains:

  • the status_code, a positive integer among the ones in the Protobuf file.
  • a list of error messages (string).

A status code 0, identified as SUCCESS, means that the request has been processed successfully. Status codes greater than 0 can be:

  • Generic errors: errors that occur under generic circumstances.

    • ERROR_UNSUPPORTED_VERSION, with integer value 1: the receiver of the message does not support the protocol version of the sender;
    • ERROR_UNEXPECTED_PAYLOAD, with integer value 2: the payload could not be deserialized on the receiver side;
    • ERROR_GENERIC, with integer value 3: an internal error;
    • ERROR_SERIALIZATION, with integer value 4: a serialization error occurred on the receiving end;
  • Register errors: errors that occur during agent registration operations in the ACN.

    • ERROR_WRONG_AGENT_ADDRESS, with integer value 10: the PoR by a peer from another peer does not match the destination address of the envelope to be routed by the receiving peer.
    • ERROR_WRONG_PUBLIC_KEY, with integer value 11: the representative peer public key does not match the one in the agent record;
    • ERROR_INVALID_PROOF, with integer value 12: the signature is invalid;
    • ERROR_UNSUPPORTED_LEDGER, with integer value 13: the ledger of the PoR is not supported by the peer;
  • Lookup and delivery errors: errors that occur during lookup to the DHT and envelope delivery operations in the ACN.

    • ERROR_UNKNOWN_AGENT_ADDRESS, with integer value 20: the requested agent address has not been found in the local DHT of the peer;
    • ERROR_AGENT_NOT_READY, with integer value 21: the agent is not ready for envelope delivery.
"},{"location":"aea-framework-documentation/acn-internals/#register","title":"Register","text":"

The Register payload is used to request a peer to register an agent among his known ones. The payload contains the field record, which is an instance of AgentRecord.

"},{"location":"aea-framework-documentation/acn-internals/#lookuprequest","title":"LookupRequest","text":"

The LookupRequest payload is sent between peer to look-up addresses in the Distributed Hash Table (DHT). It contains the agent address (a string) that the sender needs to correctly route an envelope.

"},{"location":"aea-framework-documentation/acn-internals/#lookupresponse","title":"LookupResponse","text":"

The LookupResponse payload is the response sent by a peer that received a LookupRequest. It contains the AgentRecord associated to the requested address.

"},{"location":"aea-framework-documentation/acn-internals/#aeaenvelope","title":"AeaEnvelope","text":"

The AeaEnvelope payload contains the envelope sent by an agent and to be delivered to another agent. It contains:

  • envelope: the envelope to be forwarded, in byte representation;
  • an AgentRecord (see above).
"},{"location":"aea-framework-documentation/acn-internals/#acn-protocol-interactions","title":"ACN Protocol Interactions","text":"

The ACN protocol specifies three different possible interactions:

  • the registration interaction
  • the look-up interaction
  • the routing interaction
"},{"location":"aea-framework-documentation/acn-internals/#registration-interaction","title":"\"Registration\" Interaction","text":"

The registration interaction is used by delegate agents or relayed peers to register themselves to another peer.

    sequenceDiagram\n        participant Agent/RelayedPeer\n        participant Peer\n        Agent/RelayedPeer->>Peer: Register(AgentRecord)\n        alt success\n            note over Peer: check PoR\n            Peer->>Agent/RelayedPeer: Status(SUCCESS)\n        else wrong agent address\n            Peer->>Agent/RelayedPeer: Status(ERROR_WRONG_AGENT_ADDRESS)\n        else wrong public key\n            Peer->>Agent/RelayedPeer: Status(ERROR_WRONG_PUBLIC_KEY)\n        else invalid proof of representation\n            Peer->>Agent/RelayedPeer: Status(ERROR_INVALID_PROOF)\n        else unsupported ledger\n            Peer->>Agent/RelayedPeer: Status(ERROR_UNSUPPORTED_LEDGER)\n        end
"},{"location":"aea-framework-documentation/acn-internals/#look-up-interaction","title":"\"Look-up\" Interaction","text":"

The look-up interaction is used by a peer to request information to another peer about an agent address.

    sequenceDiagram\n        participant Peer1\n        participant Peer2\n        Peer1->>Peer2: LookupRequest(address)\n        alt success\n            Peer2->>Peer1: LookupResponse(AgentRecord)\n        else unknown agent address\n            Peer2->>Peer1: Status(ERROR_UNKNOWN_AGENT_ADDRESS)\n        end
"},{"location":"aea-framework-documentation/acn-internals/#routing-interaction","title":"\"Routing\" Interaction","text":"

The routing interaction is used by agents and peers to route the envelope through the ACN.

    sequenceDiagram\n        participant Peer1\n        participant Peer2\n        Peer1->>Peer2: AeaEnvelope(envelope, AgentRecord)\n        alt success\n            note over Peer2: check PoR\n            Peer2->>Peer1: Status(SUCCESS)\n        else error on decoding of Envelope payload\n            Peer2->>Peer1: Status(ERROR_SERIALIZATION)\n        else PoR errors\n            note over Peer1,Peer2: see above \n        end
"},{"location":"aea-framework-documentation/acn-internals/#joining-the-acn-network","title":"Joining the ACN network","text":"

When an ACN peer wants to join the network, it has to start from a list of bootstrap peers, i.e. a list of ACN peers to connect with (at least one).

Each node handles four different types of libp2p streams:

  • the notification stream, identified by the URI /aea-notif/: this stream is used by new peers to notify their existence to
  • the address stream, identified by the URI /aea-address/: used to send look-up requests and look-up responses;
  • the envelope stream, identified by the URI /aea/: used to forward and to receive ACN envelopes;
  • the register relay stream, identified by the URI /aea-register/: this is to receive messages from clients that want to register their agents addresses; this peer, and then it can register their addresses.

To begin with, the node process initializes the transport connections with the bootstrap peers, the local copy of the Kademlia Distributed Hash Table (DHT), the persistent storage for agent records, and performs other non-functional operations like setting up the Prometheus monitoring system. Optionally, can also start listening for relay connections and delegate connections.

Then, it sets up the notification stream and notifies the bootstrap peers (if any).

    sequenceDiagram\n        participant Peer1\n        participant Peer2\n        participant Peer3\n        note over Peer1: notify<br/>bootstrap peers\n        Peer1->>Peer2: notify\n        Peer2->>Peer2: wait until notifying peer <br/>added to DHT\n        activate Peer2\n        Peer1->>Peer3: notify\n        Peer3->>Peer3: wait until notifying peer <br/>added to DHT\n        activate Peer3\n        note over Peer2,Peer3: Peer1 registered to DHT\n        deactivate Peer2\n        deactivate Peer3\n        loop for each local/relay/delegate address \n            Peer1->>Peer1: compute CID from address\n            Peer1->>Peer2: register address\n            Peer1->>Peer3: register address\n        end\n        note over Peer1: set up:<br/>- address stream<br/>- envelope stream<br/>- register relay stream
"},{"location":"aea-framework-documentation/acn-internals/#relay-connections","title":"Relay Connections","text":"

If the ACN node is configured to run the relay service, it sets up the register relay stream, waiting for registration requests.

The following diagram shows an example of the message exchanged during a registration request:

    sequenceDiagram\n        participant Agent\n        participant Peer\n        Agent->>Peer: Register\n        alt decoding error of ACN message\n            Peer->>Agent: Status(ERROR_SERIALIZATION)\n        else wrong payload\n            Peer->>Agent: Status(ERROR_UNEXPECTED_PAYLOAD)\n        else PoR check fails\n            alt wrong agent address\n                Peer->>Agent: Status(ERROR_WRONG_AGENT_ADDRESS)\n            else unsupported ledger\n                Peer->>Agent: Status(ERROR_UNSUPPORTED_LEDGER)\n            else agent address and public key don't match\n                Peer->>Agent: Status(ERROR_WRONG_AGENT_ADDRESS)\n            else invalid proof\n                Peer->>Agent: Status(ERROR_INVALID_PROOF)\n            end\n        else PoR check succeeds\n            Peer->>Agent: Status(SUCCESS)\n            note over Peer: announce agent address<br/>to other peers\n        end
"},{"location":"aea-framework-documentation/acn-internals/#delegate-connections","title":"Delegate Connections","text":"

If the ACN node is configured to run the delegate service, it starts listening from a TCP socket at a configurable URI.

To see a diagram of the message exchanged during a registration request read this section.

"},{"location":"aea-framework-documentation/acn-internals/#acn-transport","title":"ACN Transport","text":"

In the following sections, we describe the main three steps of the routing of an envelope through the ACN:

  • ACN entrance: when an envelope sent by an agent enters the peer-to-peer network via the peer the agent is connected to i.e. agent-to-peer communication;
  • ACN routing: when an envelope gets routed through the peer-to-peer network, i.e. peer-to-peer communication;
  • ACN exit: when an envelope gets delivered to the receiving agent through its representative peer, i.e. peer-to-agent communication.
"},{"location":"aea-framework-documentation/acn-internals/#acn-envelope-entrance-agent-peer","title":"ACN Envelope Entrance: Agent -> Peer","text":"

In this section, we will describe the interaction protocols between agents and peers for the messages sent by the agent to the ACN network; in particular, the communication from the contact peer of an agent to the agent.

The following diagram explains the exchange of messages on entering an envelope in the ACN.

In the case of direct connection, Agent is a Python process, whereas Peer is in a separate (Golang) process. The logic of the Python Agent client is implemented in the AEA connection fetchai/p2p_libp2p The communication between Agent and Peer is done through an OS pipe for Inter-Process Communication (IPC) between the AEA's process and the libp2p node process; then, the message gets enqueued to an output queue by an input coroutine. Finally, the envelope ends up in an output queue, which is processed by an output coroutine and routed to the next peer.

In the case of delegate connection, the message exchange is very similar; however, instead of using pipes, the communication is done through the network, i.e. TCP, with a peer which has the delegate service enabled. The logic of the Agent client connected with a delegate connection is implemented in the AEA connection fetchai/p2p_libp2p_client

    sequenceDiagram\n        participant Agent\n        participant Peer\n        loop until Status(success) received\n            Agent->>Peer: AeaEnvelope\n            Agent->>Agent: wait\n            note left of Agent: Wait until<br/>Status(success)\n            alt successful case\n                Peer->>Agent: Status(success)\n                note over Agent: break loop\n            else ack-timeout OR conn-error\n                note left of Agent: continue: Try to<br/>resend/reconnect\n            else version not supported\n                Peer->>Agent: Status(ERROR_UNSUPPORTED_VERSION)\n            else error on decoding of ACN message\n                Peer->>Agent: Status(ERROR_SERIALIZATION)\n            else error on decoding of Envelope payload\n                Peer->>Agent: Status(ERROR_SERIALIZATION)\n            else the payload cannot be handled\n                Peer->>Agent: Status(ERROR_UNEXPECTED_PAYLOAD)\n            end\n        end\n        note over Peer: route envelope<br/>to next peer
"},{"location":"aea-framework-documentation/acn-internals/#acn-envelope-routing","title":"ACN Envelope Routing","text":"

In this section, we describe the interaction between peers when it comes to envelope routing.

Assume an envelope arrives from an agent to peer Peer1, i.e. Peer1 is the first hop of the routing. Let Agent be the local agent directly connected to Peer1, Peer2 a direct peer of peer Peer1.

When the envelope is leaving Peer1, we may have different scenario:

  1. In case of direct connection, and the field sender of the envelope is not the local agent address: the message is considered invalid, and it is dropped.

        sequenceDiagram\n        participant Agent\n        participant Peer1\n        participant Peer2\n        Agent->>Peer1: AeaEnvelope\n        alt envelope sender not registered locally\n            note over Peer1: stop, log error\n        end
  2. the target of the envelope is the local agent connected to the peer: the envelope is routed to the local agent.

        sequenceDiagram\n        participant Agent\n        participant Peer1\n        participant Peer2\n        Agent->>Peer1: AeaEnvelope\n        alt target == peer1.my_agent\n            note over Peer1: envelope destinated<br/> to local agent,<br/> not routing\n            loop agent not ready\n                note over Peer1: sleep for 100ms\n            end\n            Peer1->>Agent: AeaEnvelope\n            Agent->>Peer1: Status(Success)\n        end
  3. the target is a delegate client. Send the envelope via TCP.

        sequenceDiagram\n        participant Delegate\n        participant Peer1\n        participant Peer2\n        Delegate->>Peer1: AeaEnvelope\n        alt destination is a delegate\n            note over Peer1: send envelope<br/> to delegate via TCP\n            Peer1->>Delegate: AeaEnvelope\n            Delegate->>Peer1: Status(Success)\n        end
  4. Otherwise, look up the local DHT. If an entry is found, use it; otherwise, send a look-up request to connected peers.

    sequenceDiagram\n        participant Agent\n        participant Peer1\n        participant Peer2\n        Agent->>Peer1: AeaEnvelope\n        alt address found in DHT\n            note over Peer1: destination is a<br/>relay client\n        else lookup address in DHT\n            note over Peer1: send lookup request<br/> to all peers\n            Peer1->>Peer2: LookupRequest\n            alt generic error\n                Peer2->>Peer1: Status(GENERIC_ERROR)\n            else look-up response\n                Peer2->>Peer1: LookupResponse\n                note over Peer1: Check PoR\n            else not found\n                Peer2->>Peer1:Status(UNKNOWN_AGENT_ADDRESS)\n            end\n        end\n        note over Peer1,Peer2: Now Peer1 knows the contact peer<br/>is PeerX

In particular, when a peer receives a LookupRequest message, it does the following:

    sequenceDiagram\n        participant Peer1\n        participant Peer2\n        Peer1->>Peer2: LookupRequest\n        alt error\n            Peer2->>Peer1: Status(Error)\n        else local agent/relay/delegate\n            note over Peer2: requested address is<br/>a local agent<br/>OR<br/>requested address is<br/>in my relay clients<br/>OR<br/>requested address is<br/>in my delegate clients\n            Peer2->>Peer1: LookupResponse\n            note over Peer1: Check PoR\n        else not found locally\n            note over Peer2: send lookup request<br/>to other peers...\n            alt found\n                Peer2->>Peer1: LookupResponse\n                note over Peer1: Check PoR\n            else not found\n                Peer2->>Peer1:Status(UNKNOWN_AGENT_ADDRESS)\n            end\n        end

Let Peer3 the contact peer of the recipient of the envelope. The following diagram shows how the contact peer of the envelope recipient handles the incoming envelope:

    sequenceDiagram\n        participant Peer1\n        participant Peer3\n        Peer1->>Peer3: AeaEnvelope\n        alt decoding error of ACN message\n            Peer3->>Peer1: Status(ERROR_SERIALIZATION)\n        else unexpected payload\n            Peer3->>Peer1: Status(ERROR_UNEXPECTED_PAYLOAD)\n        else decoding error of envelope payload\n            Peer3->>Peer1: Status(ERROR_SERIALIZATION)        \n        else PoR check fails\n            alt wrong agent address\n                Peer3->>Peer1: Status(ERROR_WRONG_AGENT_ADDRESS)\n            else unsupported ledger\n                Peer3->>Peer1: Status(ERROR_UNSUPPORTED_LEDGER)\n            else agent address and public key don't match\n                Peer3->>Peer1: Status(ERROR_WRONG_AGENT_ADDRESS)\n            else invalid proof\n                Peer3->>Peer1: Status(ERROR_INVALID_PROOF)\n            end\n        else PoR check succeeds\n            alt target is delegate, not ready\n                Peer3->>Peer1: Status(ERROR_AGENT_NOT_READY)\n            else exists delegate, ready\n                note over Peer3: forward envelope via<br/>delegate connection\n                Peer3->>Peer1: Status(SUCCESS)\n            else target is local agent, not ready\n                Peer3->>Peer1: Status(ERROR_AGENT_NOT_READY)\n            else target is local agent, ready\n                note over Peer3: forward envelope via<br/>direct connection\n                Peer3->>Peer1: Status(SUCCESS)\n            else agent does not exist\n                Peer3->>Peer1: Status(ERROR_UNKNOWN_AGENT_ADDRESS)\n            end\n        end
"},{"location":"aea-framework-documentation/acn-internals/#acn-envelope-exit-peer-agent","title":"ACN Envelope Exit: Peer -> Agent","text":"

The following diagram explains the exchange of messages on exiting an envelope in the ACN. That is, the communication from the contact peer of an agent to the agent.

The same message exchange is done both in the case of direct connection and delegate connection, similarly for what has been described for the envelope entrance (see above).

    sequenceDiagram\n        participant Agent\n        participant Peer\n        Peer->>Agent: AeaEnvelope\n        alt successful case\n            Agent->>Peer: Status(success)\n        else ack-timeout OR conn-error\n            note left of Peer: do nothing\n        else error on decoding of ACN message\n            Agent->>Peer: Status(GENERIC_ERROR)\n        else error on decoding of Envelope payload\n            Agent->>Peer: Status(GENERIC_ERROR)\n        else wrong payload\n            Agent->>Peer: Status(GENERIC_ERROR)\n        end
"},{"location":"aea-framework-documentation/acn-internals/#connect-your-aea-to-the-acn","title":"Connect your AEA to the ACN","text":"

To connect the AEA to the ACN network, there are two AEA connections available:

  • the fetchai/p2p_libp2p, that implements a direct connection, and
  • the fetchai/p2p_libp2p_delegate connection, that implements the delegate connection.

For more information on the AEA connection package type, refer to this guide.

"},{"location":"aea-framework-documentation/acn-internals/#the-fetchaip2p_libp2p-connection","title":"The fetchai/p2p_libp2p Connection","text":"

The source code of the fetchai/p2p_libp2p connection can be downloaded from the AEA Registry, or from the main AEA framework repository.

The package provides the connection class P2PLibp2pConnection, which implements the Connection interface and therefore can be used by the Multiplexer as any other connection.

  • The connect method of this connection spawns a new instance of the libp2p_node program (i.e. an ACN peer node) and connects to it through OS pipes. Then, it sets up the message receiving loop, which enqueues messages in the input queue to be read by read method calls, and the message sending loop, which dequeues messages from the output queue and forwards them to the Libp2p node. The loops are run concurrently in the Multiplexer thread, using the Python asynchronous programming library asyncio.
    sequenceDiagram\n        participant Libp2p Connection\n        participant sending loop\n        participant receiving loop\n        participant Libp2p Node\n        Libp2p Connection->>Libp2p Node: spawn process\n        activate Libp2p Node\n        Libp2p Connection->>sending loop: start recv loop\n        sending loop->>sending loop: wait messages from output queue\n        activate sending loop\n        Libp2p Connection->>receiving loop: start send loop\n        receiving loop->>receiving loop: wait messages from input queue\n        activate receiving loop\n        deactivate Libp2p Node\n        deactivate sending loop\n        deactivate receiving loop
  • The send method enqueues a message in the output queue. The message is then dequeued by the sending loop, and then sent to the Libp2p node.
    sequenceDiagram\n        participant Libp2p Connection\n        participant sending loop\n        participant Libp2p Node\n        activate sending loop\n        Libp2p Connection->>Libp2p Connection: enqueue message to output queue\n        sending loop->>sending loop: dequeue message from output queue\n        deactivate sending loop\n        sending loop->>Libp2p Node: AeaEnvelope\n        sending loop->>sending loop: wait for status\n        activate sending loop\n        alt success\n            note over Libp2p Node: route envelope\n            Libp2p Node->>sending loop: Status(SUCCESS)\n            deactivate sending loop\n            note over sending loop: OK\n        else timed out\n            note over sending loop: raise with error\n        else acn message decoding error \n            Libp2p Node->>sending loop: Status(ERROR_SERIALIZATION)\n        else unexpected payload\n            Libp2p Node->>sending loop: Status(ERROR_UNEXPECTED_PAYLOAD)\n        else envelope decoding error \n            Libp2p Node->>sending loop: Status(ERROR_SERIALIZATION)\n        end
  • The receive method dequeues a message from the input queue. The queue is populated by the receiving loop, which receives messages from the Libp2p node.
    sequenceDiagram\n        participant Libp2p Connection\n        participant receiving loop\n        participant Libp2p Node\n        activate receiving loop\n        Libp2p Node->>receiving loop: AeaEnvelope\n        deactivate receiving loop\n        Libp2p Node->>Libp2p Node: wait for status\n        activate Libp2p Node\n        alt success\n            note over receiving loop: enqueue envelope<br/>to input queue\n            receiving loop->>Libp2p Node: Status(SUCCESS)\n            deactivate Libp2p Node\n            note over receiving loop: OK\n        else timed out\n            note over Libp2p Node: ignore\n        else acn message decoding error \n            receiving loop->>Libp2p Node: Status(ERROR_SERIALIZATION)\n        else unexpected payload\n            receiving loop->>Libp2p Node: Status(ERROR_UNEXPECTED_PAYLOAD)\n        else envelope decoding error \n            receiving loop->>Libp2p Node: Status(ERROR_SERIALIZATION)\n        end\n        Libp2p Connection->>receiving loop: read message from output queue\n        note over Libp2p Connection: return message<br/>to Multiplexer
  • the disconnect method stops both the receiving loop and the sending loop, and stops the Libp2p node.
"},{"location":"aea-framework-documentation/acn-internals/#the-fetchaip2p_libp2p_delegate-connection","title":"The fetchai/p2p_libp2p_delegate Connection","text":"

The source code of the fetchai/p2p_libp2p_delegate connection can be downloaded from the main AEA framework repository. or from the main AEA framework repository.

The package provides the connection class P2PLibp2pClientConnection, which implements the Connection interface and therefore can be used by the Multiplexer as any other connection.

  • The connect method of this connection will set up a TCP connection to the URI of the delegate peer. Then, it will send a Register request to register the agent among the peer's client connections. On registration success, it sets up the message receiving loop, which enqueues messages in the input queue to be read by read method calls, and the message sending loop, which dequeues messages from the output queue and forwards them to the Libp2p node. The loops are run concurrently in the Multiplexer thread, using the Python asynchronous programming library asyncio.
    sequenceDiagram\n        participant Libp2p Client Connection\n        participant Libp2p Node\n        activate Libp2p Node\n        Libp2p Node->>Libp2p Node: listening for TCP connections\n        Libp2p Client Connection->>Libp2p Node: Register (via TCP)\n        deactivate Libp2p Node\n        alt decoding error of ACN message\n            Libp2p Node->>Libp2p Client Connection: Status(ERROR_SERIALIZATION)\n        else wrong payload\n            Libp2p Node->>Libp2p Client Connection: Status(ERROR_UNEXPECTED_PAYLOAD)\n        else PoR check fails\n            alt wrong agent address\n                Libp2p Node->>Libp2p Client Connection: Status(ERROR_WRONG_AGENT_ADDRESS)\n            else unsupported ledger\n                Libp2p Node->>Libp2p Client Connection: Status(ERROR_UNSUPPORTED_LEDGER)\n            else agent address and public key don't match\n                Libp2p Node->>Libp2p Client Connection: Status(ERROR_WRONG_AGENT_ADDRESS)\n            else invalid proof\n                Libp2p Node->>Libp2p Client Connection: Status(ERROR_INVALID_PROOF)\n            end\n        else PoR check succeeds\n            Libp2p Node->>Libp2p Client Connection: Status(SUCCESS)\n            note over Libp2p Node: announce agent<br/>address to<br/>other peers\n            Libp2p Node->>Libp2p Node: wait data from socket \n            activate Libp2p Node\n            deactivate Libp2p Node\n        end
  • The send method and the receive methods behave similarly to the send and receive methods of the p2p_libp2p connection, in terms of message exchange; however, the communication is done via TCP rather than pipes.

  • The disconnect method interrupts the connection with the delegate peer, without explicitly unregistering.

"},{"location":"aea-framework-documentation/acn-internals/#known-issues-and-limitations","title":"Known Issues and Limitations","text":"

In this section, we provide a list of known issues and limitations of the current implementation of the ACN, considering both the ACN nodes (written in Golang) and the AEA connections, for the Python AEA framework, to interact with them.

"},{"location":"aea-framework-documentation/acn-internals/#delegate-client-on-client-disconnectionreconnection","title":"Delegate Client on Client Disconnection/Reconnection","text":"

In case of disconnection/reconnection, delegate client record will be removed. This can cause two problems: either the delegate client is not found, or connection is closed during the send operation.

Possible solutions:

  • Create more complicated structure for clients storage;
  • Keep the delegate client record for longer;
  • Clean up the record by timeout, per client queues.

Code references:

  • record removed: https://github.com/fetchai/agents-aea/blob/1db1720081969bcec1be5a2000ca176475d2b487/libs/go/libp2p_node/dht/dhtpeer/dhtpeer.go#L864
  • send code: https://github.com/fetchai/agents-aea/blob/1db1720081969bcec1be5a2000ca176475d2b487/libs/go/libp2p_node/dht/dhtpeer/dhtpeer.go#L955
"},{"location":"aea-framework-documentation/acn-internals/#golang-node-python-client-libp2p-connection","title":"Golang Node <> Python Client libp2p Connection","text":"

In case of connection between the Golang side (i.e. ACN node) and the Python side (i.e. the libp2p AEA connection) is broken, there is no reconnection attempt. The Golang side connect to the Python server opened, but if the connection is broken Golang can try to reconnect; however, the Python side does not know about this and will restart the node completely.

Possible solutions: the problem requires updates on both sides and assume possible timeouts on broken connection. If connection is broken, the Python side awaits for reconnection from Golang side, and restart node completely after timeout.

"},{"location":"aea-framework-documentation/acn-internals/#what-a-peer-should-do-if-it-receives-an-acknowledgement-with-an-error","title":"What a Peer Should Do if it Receives an Acknowledgement with an Error?","text":"

If an ACN response is the Status with error code different from SUCCESS, the forwarding to other peers is not repeated.

A possible solution is to resend the message; however, not clear why it should help in case of healthy connection, how many times the sender should retry, and how it would help.

Discussion on GitHub: https://github.com/fetchai/agents-aea/pull/2509#discussion_r642628983

"},{"location":"aea-framework-documentation/acn-internals/#no-possibility-of-switching-peers","title":"No Possibility of Switching Peers","text":"

In case of a peer becoming unavailable, a delegate client or relay client currently has no means to automatically switch the peer. In particular, the DHT should be updated when a client switches peers.

"},{"location":"aea-framework-documentation/acn/","title":"Agent Communication Network","text":"

The agent communication network (ACN) provides a system for agents to find each other and communicate, solely based on their wallet addresses. It addresses the message delivery problem.

"},{"location":"aea-framework-documentation/acn/#message-delivery-problem","title":"Message Delivery Problem","text":"

Agents need to contact each others. Given the wallet address of a target agent, how can the originator agent deliver a message to it whilst guaranteeing certain properties?

The properties we would like to have, are:

  • Reliability: with guarantees on message reception
  • Authentication: to prevent impersonation
  • Confidentiality: to prevent exposing sensitive information within the message
  • Availability: some guarantees about the liveness of the service (tampering detection)

The problem statement and the agent framework context impose a number of design constraints:

  • Distributed environment: no assumption are placed about the location of the agent, they can be anywhere in the publicly reachable internet
  • Decentralized environment: no trusted central authority
  • Support for resource-constrained devices

The ACN solves the above problem whilst providing the above guarantees and satisfying the constraints.

"},{"location":"aea-framework-documentation/acn/#peers","title":"Peers","text":"

The ACN is maintained by peers. Peers are not to be equated with agents. They are processes (usually distributed and decentralized) that together maintain the service. To use the service, agents need to associate themselves with peers. Thanks to digital signatures, the association between a given peer and agent can be verified by any participant in the system.

"},{"location":"aea-framework-documentation/acn/#distributed-hash-table","title":"Distributed Hash Table","text":"

At its core, the ACN implements a distributed hash table (DHT). A DHT is similar to a regular hash table in that it stores key-value pairs. However, storage is distributed across the participating machines (peers) with an efficient lookup operation. This is enabled by:

  • Consistent hashing: decide responsibility for assignment of the DHT key-value storage
  • Structured overlays: organize the participating peers in a well-defined topology for efficient routing

For the ACN, we use the DHT to store and maintain association between an agent address and the (network) location of its peer.

"},{"location":"aea-framework-documentation/acn/#n-tier-architecture","title":"N-Tier Architecture","text":"

To satisfy different resource constraints and flexible deployment the ACN is implemented as a multi-tier architecture. As such, it provides an extension of the client-server model. The agent framework exploits this by implementing different tiers as different Connections:

Note

The p2p_libp2p_mailbox connection is not available yet.

"},{"location":"aea-framework-documentation/acn/#trust-and-security","title":"Trust and Security","text":"

An agent can choose which connection to use depending on the resource and trust requirements:

  • p2p_libp2p connection: the agent maintains a peer of the ACN. The agent has full control over the peer and does not need to trust any other entity.
  • p2p_libp2p_client connection: the agent maintains a client connection to a server which is operated by a peer of the ACN. The agent does need to trust the entity operating the peer.

All communication protocols use public cryptography to ensure security (authentication, confidentiality, and availability) using TLS handshakes with pre-shared public keys.

"},{"location":"aea-framework-documentation/aea-vs-mvc/","title":"AEA and Web Frameworks","text":"

The AEA framework borrows several concepts from popular web frameworks like Django and Ruby on Rails.

"},{"location":"aea-framework-documentation/aea-vs-mvc/#mvc","title":"MVC","text":"

Both aforementioned web frameworks use the MVC (model-view-controller) architecture.

  • Models: contain business logic and data representations
  • View: contain the HTML templates
  • Controller: deals with the request-response handling
"},{"location":"aea-framework-documentation/aea-vs-mvc/#comparison-with-the-aea-framework","title":"Comparison with the AEA Framework","text":"

The AEA framework is based on asynchronous messaging and other agent-oriented development assumptions. Hence, there is not a direct one-to-one relationship between MVC based architectures and the AEA framework. Nevertheless, there are some parallels which can help a developer familiar with MVC make quick progress in the AEA framework, in particular the development of Skills:

  • Handler: receives messages for the protocol it is registered against and is supposed to handle these messages. Handlers are the reactive parts of a skill and can be thought of as similar to the Controller in MVC. They can also send new messages.
  • Behaviour: a behaviour encapsulates proactive components of the agent. Since web apps do not have any goals or intentions, they do not proactively pursue an objective. Therefore, there is no equivalent concept in MVC. Behaviours also can, but do not have to, send messages.
  • Task: they are meant to deal with long-running executions and can be thought of as the equivalent of background tasks in traditional web apps.
  • Model: they implement business logic and data representation, and as such, they are similar to the Model in MVC.

The View concept is probably best compared to the Message of a given Protocol in the AEA framework. Whilst views represent information to the client, messages represent information sent to other agents, other agent components and services.

"},{"location":"aea-framework-documentation/aea-vs-mvc/#next-steps","title":"Next Steps","text":"

We recommend you continue with the next step in the 'Getting Started' series:

  • Build a skill for an AEA
"},{"location":"aea-framework-documentation/aeas/","title":"Autonomous Economic Agents (AEAs)","text":""},{"location":"aea-framework-documentation/aeas/#what-is-an-aea","title":"What is an AEA?","text":"

Definition

An Autonomous Economic Agent (AEA) is an intelligent agent that acts on its owner's behalf, with limited or no interference, and whose goal is to generate economic value for its owner.

Let's break down the term Autonomous Economic Agent (AEA):

  • Agent: An AEA is first and foremost an agent, representing an individual, organisation or object (a.k.a. its \"owner\") in the digital world. An AEA looks after its owner's interests and has their preferences in mind when acting on their behalf.
  • Autonomous: AEAs operate independently of constant input from their owners and act autonomously to achieve their goals.
  • Economic: AEAs have a narrow and specific focus: creating economic value for their owner.

Some of the other characteristics AEAs typically have:

  • Proactive: AEAs are proactive; they take the initiative and perform actions that take them closer to their goals.
  • Reactive: AEAs are also reactive; they are aware of the environment they are in, perceive changes in the environment, and react to these changes in accordance to their goals.
  • Self-interested: An AEA primarily looks after its own interests (which is aligned with those of its owner) and not necessarily the interests of other agents or the larger system.
"},{"location":"aea-framework-documentation/aeas/#what-is-not-an-aea","title":"What is NOT an AEA?","text":"
  • Any agent: AEAs are NOT meant to address any needs their owners might have. They have a clear and well-defined focus, which is generating economic value for their owner and this is manifested in a variety of different ways in their design.
  • Digital twins: An AEA is NOT it's owner's twin in the digital world; i.e. mirroring their preferences, values, and priorities. An AEA can be given whatever preference, value, and priority its owner wants them to have.
  • APIs or Sensors: These do NOT have any agency, nor proactiveness. They just \"sit there\" and respond to requests or changes in the environment.
  • Smart contracts: Similar to APIs and sensors, smart contracts do NOT display any proactiveness; they are purely reactive to external requests (in their case, contract calls and transactions).
  • An agent with Artificial General Intelligence (AGI): AEAs have a well-defined, narrow, and goal directed focus that involves some economic gain.

Agents and AEAs

In the rest of the documentation, unless specified, we use the terms AEA and Agent interchangeably to mean AEA as defined above description.

"},{"location":"aea-framework-documentation/agent-oriented-development/","title":"Agent-Oriented Development","text":"

In this section, we discuss some of the most fundamental characteristics of an agent-oriented approach to solution development, which might be different from existing paradigms and methodologies that you may be used to. We hope that with this, we can guide you towards the right mindset when designing your own agent-based solutions to real world problems.

"},{"location":"aea-framework-documentation/agent-oriented-development/#decentralisation","title":"Decentralisation","text":"

Multi-Agent Systems (MAS) are inherently decentralized. The vision is of an environment in which every agent is able to directly connect with everyone else and interact with them without having to rely on a third party acting as an intermediary or match-maker. This is in direct contrast to centralized systems in which a single entity is the central point of authority, through which all interactions happen. The conventional client-server model is an example of a centralized architecture where clients interact with one another regarding specific services (e.g. communication, commerce) only through a server.

This is not to say that facilitators and middlemen have no place in a multi-agent system; rather it is the 'commanding reliance on middlemen' that MAS rejects.

Division of responsibilities: In a decentralized system, every agent is equally privileged, and (in principle) should be able to interact with any other agent. The idea is very much aligned with the peer-to-peer paradigm, in which it is the voluntary participation and contribution of the peers that create the infrastructure. Therefore, in a decentralized system, there is no central 'enforcer'. This means all the work that would typically fall under the responsibilities of a central entity must be performed by individual parties in a decentralized system. Blockchain-based cryptocurrencies are a good example of this. A notable characteristic of cryptocurrencies is the absence of central trusted entities (e.g. banks). But this in turn means that most security precautions related to the handling of digital assets and the execution of transactions are the responsibility of individuals.

Decentralisation vs distribution: It is important to emphasise that by decentralisation we do not mean distribution; although multi-agent systems typically do also tend to be distributed. A distributed system is one whose components are physically located in different places and connected over a network. A fully centralized system, owned and operated by a single entity, may in fact be highly distributed. Google or Microsoft's cloud infrastructure are examples of this, where their components are distributed across the globe yet designed to work together harmoniously and function in unison. Decentralisation on the other hand refers to a system whose components may be owned, operated, and managed by different stakeholders, each with their own personal objectives, interests, and preferences which may not necessarily be aligned with one another or the system itself. Therefore, distribution refers to the physical placement of a system's components, whereas decentralisation refers to a. the diversity of ownership and control over a system's constituents, and b. the absence of central authorities between them.

Example: To better illustrate the distinction between centralized and decentralized systems, consider another example: search and discoverability in a commerce environment. In a centralized system (say Amazon), there is a single search service -- provided, owned and run by the commerce company itself -- which takes care of all search-related functionality for every product within their domain. So to be discoverable in this system, all sellers must register their products with this particular service. However, in a decentralized system, there may not necessarily be a single search service provider. There may be multiple such services, run by different, perhaps competing entities. Each seller has the freedom to register with (i.e. make themselves known to) one or a handful of services. On the buyers side, the more services they contact and query, the higher their chances of finding the product they are looking for.

"},{"location":"aea-framework-documentation/agent-oriented-development/#conflicting-environment","title":"Conflicting Environment","text":"

As discussed above, the notion of decentralisation extends as far as ownership and control. Therefore, the different components that make up a decentralized system may each be owned by a different entity, designed according to very different principles and standards, with heterogeneous software and hardware, and each with internal objectives that may be fundamentally inconsistent, worse yet contradictory, with those of others.

As such, a distinctive characteristic of a multi-agent environment, is that it is inhabited by more than one agent (as the name suggests), where each agent may be owned potentially by a different stakeholder (individual, company, government). Since by design, each agent represents and looks after the interests of its owner(s), and because different stakeholders may have unaligned, conflicting, or contradictory interests, it is very common to have multi-agent systems in which the agents' objectives, values and preferences are unaligned, conflicting, or contradictory.

In practice: There are practical implications that follow from the above when it comes to designing an agent. For example, it is not rational for an agent to automatically rely on the information it receives from other agents. The information could be:

  • Incomplete: what is unrevealed may have been deemed private for strategic reasons.
  • Uncertain: it may be the result of an inaccurate prediction.
  • Incorrect: it could be an outright lie, due to the adversarial nature of the environment.

Therefore, one can argue that there is a degree of uncertainty attached to almost all information an agent receives or infers in a multi-agent system. It wouldn't then be illogical for an agent to take a sceptical approach: treating everything as uncertain, unless proved otherwise.

"},{"location":"aea-framework-documentation/agent-oriented-development/#asynchronization","title":"Asynchronization","text":"

The conflicting nature of multi-agent systems, consisting of self-interested autonomous agents, points to asynchronization as the preferred method of designing and managing processes and interactions.

Synchronisation vs asynchronization: In general, asynchronization refers to the decoupling of events that do interact with one another but do not occur at predetermined intervals, not necessarily relying on each other's existence to function. This is in contrast with synchronous systems in which processes are aware of one another, where one's execution depends in some way on the other.

Asynchronization in MAS: In the context of multi-agent systems, the decentralized and potentially conflicting nature of the environment creates uncertainty over the behaviour of the whole system, in particular of other agents. For example, suppose an agent i sends a message requesting some resources from an agent j. Since MAS often tends to be distributed, there is the usual uncertainties with communication over a network: j may never receive i's request, or may receive it after a long delay. Furthermore, j could receive the request in time and respond immediately, but as mentioned in the last section, its answer might be incomplete (gives only some of the requested resources), uncertain (promises to give the resources, but cannot be fully trusted), or incorrect (sends a wrong resource). In addition, since agents are self-interested, j may decide to reply much later, to the point that the resource is no longer useful to agent i, or j may simply decide not to respond at all. There might be a myriad of reasons why it may choose to do that; it could be because j assigns a low priority to answering i over its other tasks. But that's beside the point. The takeaway is that agents' autonomy strongly influences what can be expected of them, and of an environment inhabited by them. As such, developing for a system whose constituents are autonomous, e.g. agents in a multi-agent system, is fundamentally different from one whose constituents aren't, e.g. objects in an object-oriented system.

Objects vs agents: In object-oriented systems, objects are entities that encapsulate state and perform actions, i.e. call methods, on this state. In object-oriented languages, like C++ and Java, it is common practice to declare methods as public, so they can be invoked by other objects in the system whenever they wish. This implies that an object does not control its own behaviour. If an object\u2019s method is public, the object has no control over whether that method is executed.

We cannot take for granted that an agent j will execute an action (the equivalent of a method in object-oriented systems) just because another agent i wants it to; this action may not be in the best interests of agent j. So we do not think of agents as invoking methods on one another, rather as requesting actions. If i requests j to perform an action, then j may or may not perform the action. It may choose to do it later or do it in exchange for something. The locus of control is therefore different in object-oriented and agent-oriented systems. In the former, the decision lies with the object invoking the method, whereas in the latter, the decision lies with the agent receiving the request. This distinction could be summarised by the following slogan (from An Introduction to MultiAgent Systems by Michael Wooldridge):

objects do it for free; agents do it because they want to.

All of this makes asynchronization the preferred method for designing agent processes and interactions. An agent's interactions should be independent of each other, as much as possible, and of the agent's decision-making processes and actions. This means the success or failure of, or delay in any single interaction does not block the agent's other tasks.

"},{"location":"aea-framework-documentation/agent-oriented-development/#time","title":"Time","text":"

Closely related with the discussion of asynchronicity, is the idea that in multi-agent systems, time is not a universally agreed notion. Agents may not necessarily share the same clock and this fact must be taken into account when designing agent-based systems. For example, you cannot necessarily expect agents to synchronise their behaviour according to time (e.g. perform a certain task at a time X).

Another related issue, is that unlike some agent-based simulation (ABS) systems where there is a global tick rate for all agents, in AEA-based systems tick rates may be different for different agents. This is due to the fundamental difference that ABS systems control some aspects of all of their agents' executions while in AEA-based systems, agents are truly decoupled from one another - most likely distributed and running on different machines and networks - and there is absolutely no central unit that moderates any aspect of their behaviour.

"},{"location":"aea-framework-documentation/agent-oriented-development/#complex-incomplete-inconsistent-and-uncertain","title":"Complex, Incomplete, Inconsistent and Uncertain","text":"

The fourth characteristic(s) relate to the environment in which agents are expected to operate in, and these have been mentioned a number of times in the previous sections.

The environment agents are suited for typically tend to be complex, to the point that it is usually impossible for any single agent to perceive the whole of the environment on its own. This means that at any point in time, any agent has a limited knowledge about the state of the environment. In other words, the agents;' information tend to be incomplete due to the complexity and sophistication of the world in which they reside.

Consider an agent which represents a driver-less vehicle. The complexity of the problem of driving on the road makes it impossible for a single vehicle to have an accurate and up-to-date knowledge of the overall state of the world . This means that an agent's model of the world is at best uncertain. For instance, the vehicle, through its sensor may detect green light at a junction, and by being aware of what it means, it may infer that it is safe to cross a junction. However, that simply may not be true as another car in the opposite direction may still cross the junction violating their red light. Therefore, there is uncertainty associated with the knowledge \"it is safe to cross the road because the light is green\", and the agent must recognise that.

Furthermore, the often conflicting nature of the environment means information obtained from multiple sources (agents) may be inconsistent. Again, this must be taken into consideration when designing an agent which is expected to operate successfully in a potentially conflicting environment.

"},{"location":"aea-framework-documentation/agent-oriented-development/#further-reading","title":"Further Reading","text":"
  • Wooldridge, M. (2009). An Introduction to MultiAgent Systems. Wiley, Second edition.
  • Shoham, Y. and Leyton-Brown, K. (2008). Multiagent Systems: Algorithmic, Game-Theoretic, and Logical Foundations. Cambridge University Press
"},{"location":"aea-framework-documentation/agent-vs-aea/","title":"AEAs vs Agents","text":"

AEAs are more than just agents.

In this guide, we show some of the differences in terms of code.

The Build an AEA programmatically guide shows how to programmatically build an AEA. We can build an agent of the Agent class programmatically as well.

First, import the python and application specific libraries. (Get the packages directory from the AEA repository svn export https://github.com/fetchai/agents-aea.git/trunk/packages.)

import os\nimport time\nfrom threading import Thread\nfrom typing import List\nfrom aea.agent import Agent\nfrom aea.configurations.base import ConnectionConfig\nfrom aea.connections.base import Connection\nfrom aea.helpers.file_io import write_with_lock\nfrom aea.identity.base import Identity\nfrom aea.mail.base import Envelope\nfrom packages.fetchai.connections.stub.connection import StubConnection\nfrom packages.fetchai.protocols.default.message import DefaultMessage\n

Unlike an AEA, an Agent does not require a Wallet, LedgerApis or Resources module.

However, we need to implement 4 abstract methods:

  • setup()
  • act()
  • handle_envelope()
  • teardown()

When we run an agent, start() calls setup() and then the main agent loop. The main agent loop calls act(), react() and update() on each tick. When the agent is stopped via stop() then teardown() is called.

Such a lightweight agent can be used to implement simple logic.

"},{"location":"aea-framework-documentation/agent-vs-aea/#code-an-agent","title":"Code an Agent","text":"

We define our Agent which simply receives envelopes, prints the sender address and protocol_id and returns it unopened.

INPUT_FILE = \"input_file\"\nOUTPUT_FILE = \"output_file\"\nclass MyAgent(Agent):\n\"\"\"A simple agent.\"\"\"\ndef __init__(self, identity: Identity, connections: List[Connection]):\n\"\"\"Initialise the agent.\"\"\"\nsuper().__init__(identity, connections)\ndef setup(self):\n\"\"\"Setup the agent.\"\"\"\ndef act(self):\n\"\"\"Act implementation.\"\"\"\nprint(\"Act called for tick {}\".format(self.tick))\ndef handle_envelope(self, envelope: Envelope) -> None:\n\"\"\"\n        Handle envelope.\n        :param envelope: the envelope received\n        :return: None\n        \"\"\"\nprint(\"React called for tick {}\".format(self.tick))\nif (\nenvelope is not None\nand envelope.protocol_specification_id\n== DefaultMessage.protocol_specification_id\n):\nsender = envelope.sender\nreceiver = envelope.to\nenvelope.to = sender\nenvelope.sender = receiver\nenvelope.message = DefaultMessage.serializer.decode(envelope.message_bytes)\nenvelope.message.sender = receiver\nenvelope.message.to = sender\nprint(\n\"Received envelope from {} with protocol_specification_id={}\".format(\nsender, envelope.protocol_specification_id\n)\n)\nself.outbox.put(envelope)\ndef teardown(self):\n\"\"\"Teardown the agent.\"\"\"\n
"},{"location":"aea-framework-documentation/agent-vs-aea/#instantiate-an-agent","title":"Instantiate an Agent","text":"
    # Ensure the input and output files do not exist initially\nif os.path.isfile(INPUT_FILE):\nos.remove(INPUT_FILE)\nif os.path.isfile(OUTPUT_FILE):\nos.remove(OUTPUT_FILE)\n# Create an addresses identity:\nidentity = Identity(\nname=\"my_agent\", address=\"some_address\", public_key=\"public_key\"\n)\n# Set up the stub connection\nconfiguration = ConnectionConfig(\ninput_file_path=INPUT_FILE,\noutput_file_path=OUTPUT_FILE,\nconnection_id=StubConnection.connection_id,\n)\nstub_connection = StubConnection(\nconfiguration=configuration, data_dir=\".\", identity=identity\n)\n# Create our Agent\nmy_agent = MyAgent(identity, [stub_connection])\n
"},{"location":"aea-framework-documentation/agent-vs-aea/#start-the-agent","title":"Start the Agent","text":"

We run the agent from a different thread so that we can still use the main thread to pass it messages.

    # Set the agent running in a different thread\ntry:\nt = Thread(target=my_agent.start)\nt.start()\n# Wait for everything to start up\ntime.sleep(3)\n
"},{"location":"aea-framework-documentation/agent-vs-aea/#send-and-receive-an-envelope","title":"Send and Receive an Envelope","text":"

We use the input and output text files to send an envelope to our agent and receive a response

        # Create a message inside an envelope and get the stub connection to pass it into the agent\nmessage_text = b\"my_agent,other_agent,fetchai/default:1.0.0,\\x12\\r\\x08\\x01*\\t*\\x07\\n\\x05hello,\"\nwith open(INPUT_FILE, \"wb\") as f:\nwrite_with_lock(f, message_text)\n# Wait for the envelope to get processed\ntime.sleep(2)\n# Read the output envelope generated by the agent\nwith open(OUTPUT_FILE, \"rb\") as f:\nprint(\"output message: \" + f.readline().decode(\"utf-8\"))\n
"},{"location":"aea-framework-documentation/agent-vs-aea/#shutdown","title":"Shutdown","text":"

Finally, stop our agent and wait for it to finish

    finally:\n# Shut down the agent\nmy_agent.stop()\nt.join()\n
"},{"location":"aea-framework-documentation/agent-vs-aea/#your-turn","title":"Your Turn","text":"

Now it is your turn to develop a simple agent with the Agent class.

"},{"location":"aea-framework-documentation/agent-vs-aea/#entire-code-listing","title":"Entire Code Listing","text":"

If you just want to copy and paste the entire script in you can find it here:

Click here to see full listing
import os\nimport time\nfrom threading import Thread\nfrom typing import List\nfrom aea.agent import Agent\nfrom aea.configurations.base import ConnectionConfig\nfrom aea.connections.base import Connection\nfrom aea.helpers.file_io import write_with_lock\nfrom aea.identity.base import Identity\nfrom aea.mail.base import Envelope\nfrom packages.fetchai.connections.stub.connection import StubConnection\nfrom packages.fetchai.protocols.default.message import DefaultMessage\nINPUT_FILE = \"input_file\"\nOUTPUT_FILE = \"output_file\"\nclass MyAgent(Agent):\n\"\"\"A simple agent.\"\"\"\ndef __init__(self, identity: Identity, connections: List[Connection]):\n\"\"\"Initialise the agent.\"\"\"\nsuper().__init__(identity, connections)\ndef setup(self):\n\"\"\"Setup the agent.\"\"\"\ndef act(self):\n\"\"\"Act implementation.\"\"\"\nprint(\"Act called for tick {}\".format(self.tick))\ndef handle_envelope(self, envelope: Envelope) -> None:\n\"\"\"\n        Handle envelope.\n        :param envelope: the envelope received\n        :return: None\n        \"\"\"\nprint(\"React called for tick {}\".format(self.tick))\nif (\nenvelope is not None\nand envelope.protocol_specification_id\n== DefaultMessage.protocol_specification_id\n):\nsender = envelope.sender\nreceiver = envelope.to\nenvelope.to = sender\nenvelope.sender = receiver\nenvelope.message = DefaultMessage.serializer.decode(envelope.message_bytes)\nenvelope.message.sender = receiver\nenvelope.message.to = sender\nprint(\n\"Received envelope from {} with protocol_specification_id={}\".format(\nsender, envelope.protocol_specification_id\n)\n)\nself.outbox.put(envelope)\ndef teardown(self):\n\"\"\"Teardown the agent.\"\"\"\ndef run():\n\"\"\"Run demo.\"\"\"\n# Ensure the input and output files do not exist initially\nif os.path.isfile(INPUT_FILE):\nos.remove(INPUT_FILE)\nif os.path.isfile(OUTPUT_FILE):\nos.remove(OUTPUT_FILE)\n# Create an addresses identity:\nidentity = Identity(\nname=\"my_agent\", address=\"some_address\", public_key=\"public_key\"\n)\n# Set up the stub connection\nconfiguration = ConnectionConfig(\ninput_file_path=INPUT_FILE,\noutput_file_path=OUTPUT_FILE,\nconnection_id=StubConnection.connection_id,\n)\nstub_connection = StubConnection(\nconfiguration=configuration, data_dir=\".\", identity=identity\n)\n# Create our Agent\nmy_agent = MyAgent(identity, [stub_connection])\n# Set the agent running in a different thread\ntry:\nt = Thread(target=my_agent.start)\nt.start()\n# Wait for everything to start up\ntime.sleep(3)\n# Create a message inside an envelope and get the stub connection to pass it into the agent\nmessage_text = b\"my_agent,other_agent,fetchai/default:1.0.0,\\x12\\r\\x08\\x01*\\t*\\x07\\n\\x05hello,\"\nwith open(INPUT_FILE, \"wb\") as f:\nwrite_with_lock(f, message_text)\n# Wait for the envelope to get processed\ntime.sleep(2)\n# Read the output envelope generated by the agent\nwith open(OUTPUT_FILE, \"rb\") as f:\nprint(\"output message: \" + f.readline().decode(\"utf-8\"))\nfinally:\n# Shut down the agent\nmy_agent.stop()\nt.join()\nif __name__ == \"__main__\":\nrun()\n
"},{"location":"aea-framework-documentation/aggregation-demo/","title":"Aggregation Skill","text":"

This demo shows how AEAs can aggregate values over the peer-to-peer network.

"},{"location":"aea-framework-documentation/aggregation-demo/#discussion","title":"Discussion","text":"

This demonstration shows how to set up a simple aggregation network in which several AEAs take an average of values fetched from different sources for the same real-world quantity. For this particular example, we take an average of Bitcoin prices from four public APIs.

"},{"location":"aea-framework-documentation/aggregation-demo/#preparation-instructions","title":"Preparation Instructions","text":""},{"location":"aea-framework-documentation/aggregation-demo/#dependencies","title":"Dependencies","text":"

Follow the Preliminaries and Installation sections from the AEA quick start.

"},{"location":"aea-framework-documentation/aggregation-demo/#demo","title":"Demo","text":""},{"location":"aea-framework-documentation/aggregation-demo/#create-the-aeas","title":"Create the AEAs","text":"

Repeat the following process four times in four different terminals (for each {i=0, i=1, i=2, i=3}):

Fetch the aggregator AEA:

agent_name=\"agg$i\"\naea fetch fetchai/simple_aggregator:0.5.5 --alias $agent_name\ncd $agent_name\naea install\naea build\n
Alternatively, create from scratch:

Create the AEA:

agent_name=\"agg$i\"\naea create agent_name\ncd agent_name\naea add connection fetchai/http_client:0.24.6\naea add connection fetchai/http_server:0.23.6\naea add connection fetchai/p2p_libp2p:0.27.5\naea add connection fetchai/soef:0.27.6\naea add connection fetchai/prometheus:0.9.6\naea add skill fetchai/advanced_data_request:0.7.6\naea add skill fetchai/simple_aggregation:0.3.6\n\naea config set agent.default_connection fetchai/p2p_libp2p:0.27.5\naea install\naea build\n

Set the desired decimal precision for the quantity:

aea config set --type int vendor.fetchai.skills.advanced_data_request.models.advanced_data_request_model.args.decimals 0\n

Disable the http server since it is not used in this demo:

aea config set --type bool vendor.fetchai.skills.advanced_data_request.models.advanced_data_request_model.args.use_http_server false\n

Set the cert requests for the peer-to-peer connection:

aea config set --type list vendor.fetchai.connections.p2p_libp2p.cert_requests \\\n'[{\"identifier\": \"acn\", \"ledger_id\": \"fetchai\", \"not_after\": \"2023-01-01\", \"not_before\": \"2022-01-01\", \"public_key\": \"fetchai\", \"message_format\": \"{public_key}\", \"save_path\": \".certs/conn_cert.txt\"}]'\n

Match the agent index i to the COIN_URL and JSON_PATH below:

  • agg0: COIN_URL=\"https://api.coinbase.com/v2/prices/BTC-USD/buy\" && JSON_PATH=\"data.amount\"
  • agg1: COIN_URL=\"https://api.coinpaprika.com/v1/tickers/btc-bitcoin\" && JSON_PATH=\"quotes.USD.price\"
  • agg2: COIN_URL=\"https://api.cryptowat.ch/markets/kraken/btcusd/price\" && JSON_PATH=\"result.price\"
  • agg3: COIN_URL=\"https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies=usd\" && JSON_PATH=\"bitcoin.usd\"

Set the following configuration for the advanced_data_request skill:

aea config set vendor.fetchai.skills.advanced_data_request.models.advanced_data_request_model.args.url $COIN_URL\naea config set vendor.fetchai.skills.advanced_data_request.models.advanced_data_request_model.args.outputs '[{\"name\": \"price\", \"json_path\": '\"\\\"$JSON_PATH\\\"\"'}]'\n

Set the name of the quantity to aggregate and choose an aggregation function for the AEAs (the currently implemented options are mean, median, and mode):

aea config set vendor.fetchai.skills.simple_aggregation.models.strategy.args.quantity_name price\naea config set vendor.fetchai.skills.simple_aggregation.models.strategy.args.aggregation_function mean\n

Specify a name for your aggregation service:

SERVICE_ID=my_btc_aggregation_service\naea config set vendor.fetchai.skills.simple_aggregation.models.strategy.args.service_id $SERVICE_ID\naea config set vendor.fetchai.skills.simple_aggregation.models.strategy.args.search_query.search_value $SERVICE_ID\n

Additionally, create private keys for use with the ledger and the peer-to-peer connection:

aea generate-key fetchai\naea add-key fetchai\naea generate-key fetchai fetchai_connection_private_key.txt\naea add-key fetchai fetchai_connection_private_key.txt --connection\n

Finally, certify the keys for use by the connections that request them:

aea issue-certificates\n
"},{"location":"aea-framework-documentation/aggregation-demo/#configure-the-peer-to-peer-network","title":"Configure the Peer-to-Peer Network","text":"

Set the multiaddress of the first AEA as an initial peer to help the remaining AEAs find each other on the network. Also, if these AEAs are all running on the same machine, set different ports for their connections to ensure there are no conflicts (from the agg1, agg2, and agg3 directories):

MULTIADDR=$(cd ../agg0 && aea get-multiaddress fetchai --connection)\naea config set --type dict vendor.fetchai.connections.p2p_libp2p.config \\\n'{\n\"delegate_uri\": \"127.0.0.1:'$((11000+i))'\",\n\"entry_peers\": [\"/dns4/127.0.0.1/tcp/9000/p2p/'\"$MULTIADDR\\\"\"'],\n\"local_uri\": \"127.0.0.1:'$((9000+i))'\",\n\"log_file\": \"libp2p_node.log\",\n\"public_uri\": \"127.0.0.1:'$((9000+i))'\"\n}'\naea config set vendor.fetchai.connections.prometheus.config.port $((20000+i))\naea config set vendor.fetchai.connections.http_server.config.port $((8000+i))\n
"},{"location":"aea-framework-documentation/aggregation-demo/#oracle-integration-optional","title":"Oracle Integration (optional)","text":"

To publish the aggregated value to an oracle smart contract, add the ledger connection and simple oracle skill to one of the aggregators:

aea add connection fetchai/ledger:0.21.5\naea add skill fetchai/simple_oracle:0.16.5\n

Configure the simple oracle skill for the fetchai ledger:

aea config set vendor.fetchai.skills.simple_oracle.models.strategy.args.ledger_id fetchai\naea config set vendor.fetchai.skills.simple_oracle.models.strategy.args.update_function update_oracle_value\n

Generate some wealth to use for transactions on the testnet ledger:

aea generate-wealth fetchai\n

Set the name of the oracle value to match the value collected by the aggregators:

aea config set vendor.fetchai.skills.simple_oracle.models.strategy.args.oracle_value_name price_mean\n
"},{"location":"aea-framework-documentation/aggregation-demo/#run-the-aeas","title":"Run the AEAs","text":"

Run each of the aggregator AEAs in separate terminals:

aea run\n

After a few moments, you should see the AEAs finding peers, making observations, sending them to peers, and taking the average of their observations:

info: [agg_i] found agents...\n...\ninfo: [agg_i] Fetching data from...\n...\ninfo: [agg_i] Observation: {'price': {'value':...\n...\ninfo: [agg_i] sending observation to peer...\n...\ninfo: [agg_i] received observation from sender...\n...\ninfo: [agg_i] Observations:...\n...\ninfo: [agg_i] Aggregation (mean):...\n
"},{"location":"aea-framework-documentation/application/","title":"Application Areas","text":""},{"location":"aea-framework-documentation/application/#environments","title":"Environments","text":"

AEAs are most suited for environments which are:

  • Decentralized: there isn't a central authority that controls, manages, or makes decisions.
  • Multi-Stakeholder: the domain, problem, or solutions involve multiple distinct stakeholders.
  • Peer-to-Peer: interactions are (or could be made) direct and peer-to-peer.
  • Complex, Incomplete, and Uncertain: to the point that off-loading tasks to computational entities becomes valuable.
"},{"location":"aea-framework-documentation/application/#applications","title":"Applications","text":"

We identify a number of application areas for AEA-based solutions. This list is by no means comprehensive. In fact, we are most excited about applications which we have not thought of before.

Automation

AEAs can automate well-defined processes in different domains, such as supply chain, mobility, finance, ...

Micro-transactions

AEAs make it economically viable to execute trade involving small values. An example is use-cases with many small sellers (e.g. of data) on the supply side.

Wallet

AEAs can simplify interactions with blockchains. By acting as \"smart wallets\", they can hide away the majority of the complexities involved in using blockchains for end users.

IoT

Agents representing objects in the IoT (Internet of Things) space. For example, AEAs paired with hardware devices such as drones, laptops, heat sensors, etc., providing control and receiving data from the device. An example is a thermometer agent.

Web 2.0 <--> Web 3.0 interface

Agents that interface and bridge the gap between existing (Web 2.0) and new (Web 3.0) economic models. An example is an AEA that communicates with HTTP clients/servers.

Digital data sales

Agents with access to some data sources that sell the data, access to the data, or access to the usage of the data. An example is an AEA that continuously sells data to another AEA, who in turn uses it to improve their reinforcement learning model.

"},{"location":"aea-framework-documentation/application/#multi-agent-system-vs-agent-based-modelling","title":"Multi-Agent System VS Agent-Based Modelling","text":"

The AEA framework enables the creation of multi-agent systems as technological solutions to real world problems.

Although there are some overlap, the framework is not designed from the outset as an agent-based modelling software, where the goal is scientific behavioural observation rather than practical economic gain.

Moreover, there is no restriction to multi; single-agent applications are also supported.

"},{"location":"aea-framework-documentation/aries-cloud-agent-demo/","title":"Aries Cloud Agents Demo","text":"

Note

This demo is incomplete and will soon be updated.

Demonstrating an entire decentralized identity scenario involving AEAs and instances of Aries Cloud Agents (ACAs).

"},{"location":"aea-framework-documentation/aries-cloud-agent-demo/#discussion","title":"Discussion","text":"

This demo corresponds with the one here from Aries cloud agent repository .

The aim of this demo is to illustrate how AEAs can connect to ACAs, thus gaining all of their capabilities, such as issuing and requesting verifiable credentials, selective disclosure and zero knowledge proofs.

    sequenceDiagram\n        participant faea as Faber_AEA\n        participant faca as Faber_ACA\n        participant aaca as Alice_ACA\n        participant aaea as Alice_AEA\n\n        activate faea\n        activate faca\n        activate aaca\n        activate aaea\n\n        Note right of aaea: Shows P2P ID\n\n        faea->>faca: Request status?\n        faca->>faea: status\n        faea->>faca: Register schema\n        faca->>faea: schema_id\n        faea->>faca: Register credential definition\n        faca->>faea: credential_definition_id\n        faea->>faca: create-invitation\n        faca->>faea: connection inc. invitation\n        faea->>aaea: invitation detail\n        aaea->>aaca: receive-invitation\n\n        deactivate faea\n        deactivate faca\n        deactivate aaca\n        deactivate aaea

There are two AEAs:

  • Alice_AEA
  • Faber_AEA

and two ACAs:

  • Alice_ACA
  • Faber_ACA

Each AEA is connected to its corresponding ACA: Alice_AEA to Alice_ACA and Faber_AEA to Faber_ACA.

The following lists the sequence of interactions between the four agents:

  • Alice_AEA: starts
  • Alice_AEA: shows its P2P address in the terminal and waits for an invitation detail from Faber_AEA.
  • Alice_AEA: registers itself on the SOEF.
  • Faber_AEA: starts
  • Faber_AEA: searches the SOEF and finds Alice_AEA.
  • Faber_AEA: tests its connection to Faber_ACA.
  • Faber_ACA: responds to Faber_AEA.
  • Faber_AEA: registers a DID on the ledger.
  • Faber_AEA: request Faber_ACA to register a schema on the ledger.
  • Faber_ACA: responds by sending back the schema_id.
  • Faber_AEA: request Faber_ACA to register a credential definition on the ledger.
  • Faber_ACA: responds by sending back the credential_definition_id.
  • Faber_AEA: requests Faber_ACA to create an invitation.
  • Faber_ACA: responds by sending back the connection detail, which contains an invitation field.
  • Faber_AEA: sends the invitation detail to Alice_AEA.
  • Alice_AEA: receives invitation detail from Faber_AEA.
  • Alice_AEA: requests Alice_ACA to accept the invitation, by passing it the invitation detail it received in the last step.

All messages from an AEA to an ACA are http requests (using http_client connection).

All messages from an AEA to another AEA utilise the P2P communication network accessed via the p2p_libp2p connection.

All messages initiated from an ACA to an AEA are webhooks (using webhook connection).

This is the extent of the demo at this point. The rest of the interactions require an instance of the Indy ledger to run. This is what will be implemented next.

The rest of the interactions are broadly as follows:

  • Alice_ACA: accepts the invitation.
  • Alice_ACA: sends a matching invitation request to Faber_ACA.
  • Faber_ACA: accepts

At this point, the two ACAs are connected to each other.

  • Faber_AEA: requests Faber_ACA to issue a credential (e.g. university degree) to Alice_AEA, which Faber_ACA does via Alice_ACA.
  • Faber_AEA: requests proof that Alice_AEA's age is above 18.
  • Alice_AEA: presents proof that it's age is above 18, without presenting its credential.
"},{"location":"aea-framework-documentation/aries-cloud-agent-demo/#preparation-instructions","title":"Preparation Instructions","text":""},{"location":"aea-framework-documentation/aries-cloud-agent-demo/#dependencies","title":"Dependencies","text":"

Follow the Preliminaries and Installation sections from the AEA quick start.

Install Aries cloud-agents (for more info see here) if you do not have it on your machine:

pip install aries-cloudagent\n

This demo has been successfully tested with aca-py version 0.4.5.

This demo requires an instance of von network running in docker locally (for more info see here)

This demo has been successfully tested with the von-network git repository pulled on 07 Aug 2020 (commit number ad1f84f64d4f4c106a81462f5fbff496c5fbf10e).

"},{"location":"aea-framework-documentation/aries-cloud-agent-demo/#terminals","title":"Terminals","text":"

Open five terminals. The first terminal is used to run an instance of von-network locally in docker. The other four terminals will be used to run each of the four agents in this demo.

"},{"location":"aea-framework-documentation/aries-cloud-agent-demo/#von-network","title":"VON Network","text":"

In the first terminal move to the von-network directory and run an instance of von-network locally in docker.

This tutorial has information on starting (and stopping) the network locally.

./manage build\n./manage start --logs\n

Once the ledger is running, you can see the ledger by going to the web server running on port 9000. On localhost, that means going to http://localhost:9000.

"},{"location":"aea-framework-documentation/aries-cloud-agent-demo/#alice-and-faber-acas","title":"Alice and Faber ACAs","text":"

To learn about the command for starting an ACA and its various options:

aca-py start --help\n
"},{"location":"aea-framework-documentation/aries-cloud-agent-demo/#faber_aca","title":"Faber_ACA","text":"

In the first terminal:

aca-py start --admin 127.0.0.1 8021 --admin-insecure-mode --inbound-transport http 0.0.0.0 8020 --outbound-transport http --webhook-url http://127.0.0.1:8022/webhooks\n

Make sure the ports above are unused.

Take note of the specific IP addresses and ports you used in the above command. We will refer to them by the following names:

  • Faber admin IP: 127.0.0.1
  • Faber admin port: 8021
  • Faber webhook port: 8022

The admin IP and port will be used to send administrative commands to this ACA from an AEA.

The webhook port is where the ACA will send notifications to. We will expose this from the AEA so it receives this ACA's notifications.

"},{"location":"aea-framework-documentation/aries-cloud-agent-demo/#alice_aca","title":"Alice_ACA","text":"

In the second terminal:

aca-py start --admin 127.0.0.1 8031 --admin-insecure-mode --inbound-transport http 0.0.0.0 8030 --outbound-transp http --webhook-url http://127.0.0.1:8032/webhooks\n

Again, make sure the above ports are unused and take note of the specific IP addresses and ports. In this case:

  • Alice admin IP: 127.0.0.1
  • Alice admin port: 8031
  • Alice webhook port: 8032
"},{"location":"aea-framework-documentation/aries-cloud-agent-demo/#alice-and-faber-aeas","title":"Alice and Faber AEAs","text":"

Now you can create Alice_AEA and Faber_AEA in terminals 3 and 4 respectively.

"},{"location":"aea-framework-documentation/aries-cloud-agent-demo/#alice_aea","title":"Alice_AEA","text":"

In the third terminal, fetch Alice_AEA and move into its project folder:

aea fetch fetchai/aries_alice:0.32.5\ncd aries_alice\n
Alternatively, create from scratch:

The following steps create Alice_AEA from scratch:

aea create aries_alice\ncd aries_alice\naea add connection fetchai/p2p_libp2p:0.27.5\naea add connection fetchai/soef:0.27.6\naea add connection fetchai/http_client:0.24.6\naea add connection fetchai/webhook:0.20.6\naea add skill fetchai/aries_alice:0.26.6\n
"},{"location":"aea-framework-documentation/aries-cloud-agent-demo/#configure-the-aries_alice-skill","title":"Configure the aries_alice Skill","text":"

(configuration file: alice/vendor/fetchai/skills/aries_alice/skill.yaml)

Ensure admin_host and admin_port values match with the values you noted above for Alice_ACA. You can use the framework's handy config CLI command to set these values:

aea config set vendor.fetchai.skills.aries_alice.models.strategy.args.admin_host 127.0.0.1\n
aea config set --type int vendor.fetchai.skills.aries_alice.models.strategy.args.admin_port 8031\n
"},{"location":"aea-framework-documentation/aries-cloud-agent-demo/#configure-the-webhook-connection","title":"Configure the webhook Connection","text":"

(configuration file: alice/vendor/fetchai/connections/webhook/connection.yaml).

First ensure the value of webhook_port matches with what you used above for Alice_ACA.

aea config set --type int vendor.fetchai.connections.webhook.config.webhook_port 8032\n

Next, make sure the value of webhook_url_path is /webhooks/topic/{topic}/.

aea config set vendor.fetchai.connections.webhook.config.webhook_url_path /webhooks/topic/{topic}/\n
"},{"location":"aea-framework-documentation/aries-cloud-agent-demo/#configure-the-p2p_libp2p-connection","title":"Configure the p2p_libp2p Connection","text":"
aea config set --type dict vendor.fetchai.connections.p2p_libp2p.config \\\n'{\n  \"delegate_uri\": \"127.0.0.1:11000\",\n  \"entry_peers\": [],\n  \"local_uri\": \"127.0.0.1:7000\",\n  \"log_file\": \"libp2p_node.log\",\n  \"public_uri\": \"127.0.0.1:7000\"\n}'\n
"},{"location":"aea-framework-documentation/aries-cloud-agent-demo/#install-the-dependencies-and-run-alice_aea","title":"Install the Dependencies and Run Alice_AEA","text":"

Now install all the dependencies:

aea install\naea build\n

Finally, run Alice_AEA:

aea run\n

Once you see a message of the form To join its network use multiaddr 'SOME_ADDRESS' take note of the address. (Alternatively, use aea get-multiaddress fetchai -c -i fetchai/p2p_libp2p:0.27.5 -u public_uri to retrieve the address.) We will refer to this as Alice_AEA's P2P address.

"},{"location":"aea-framework-documentation/aries-cloud-agent-demo/#faber_aea","title":"Faber_AEA","text":"

In the fourth terminal, fetch Faber_AEA and move into its project folder:

aea fetch fetchai/aries_faber:0.32.5\ncd aries_faber\n
Alternatively, create from scratch:

The following steps create Faber_AEA from scratch:

aea create aries_faber\ncd aries_faber\naea add connection fetchai/p2p_libp2p:0.27.5\naea add connection fetchai/soef:0.27.6\naea add connection fetchai/http_client:0.24.6\naea add connection fetchai/webhook:0.20.6\naea add skill fetchai/aries_faber:0.24.5\n
"},{"location":"aea-framework-documentation/aries-cloud-agent-demo/#configure-the-aries_faber-skill","title":"Configure the aries_faber Skill","text":"

(configuration file: faber/vendor/fetchai/skills/aries_alice/skill.yaml)

Ensure admin_host and admin_port values match with those you noted above for Faber_ACA.

aea config set vendor.fetchai.skills.aries_faber.models.strategy.args.admin_host 127.0.0.1\n
aea config set --type int vendor.fetchai.skills.aries_faber.models.strategy.args.admin_port 8021\n
"},{"location":"aea-framework-documentation/aries-cloud-agent-demo/#configure-the-webhook-connection_1","title":"Configure the webhook Connection","text":"

(configuration file: faber/vendor/fetchai/connections/webhook/connection.yaml).

First, ensure the value of webhook_port matches with what you used above for Faber_ACA.

aea config set --type int vendor.fetchai.connections.webhook.config.webhook_port 8022\n

Next, make sure the value of webhook_url_path is /webhooks/topic/{topic}/.

aea config set vendor.fetchai.connections.webhook.config.webhook_url_path /webhooks/topic/{topic}/\n
"},{"location":"aea-framework-documentation/aries-cloud-agent-demo/#configure-the-p2p_libp2p-connection_1","title":"Configure the p2p_libp2p Connection","text":"
aea config set --type dict vendor.fetchai.connections.p2p_libp2p.config \\\n'{\n  \"delegate_uri\": \"127.0.0.1:11001\",\n  \"entry_peers\": [\"SOME_ADDRESS\"],\n  \"local_uri\": \"127.0.0.1:7001\",\n  \"log_file\": \"libp2p_node.log\",\n  \"public_uri\": \"127.0.0.1:7001\"\n}'\n

where SOME_ADDRESS is Alice_AEA's P2P address as displayed in the third terminal.

"},{"location":"aea-framework-documentation/aries-cloud-agent-demo/#install-the-dependencies-and-run-faber_aea","title":"Install the Dependencies and Run Faber_AEA","text":"

Now install all the dependencies:

aea install\naea build\n

Finally run Faber_AEA:

aea run\n

You should see Faber_AEA running and showing logs of its activities. For example:

Looking now at Alice_AEA terminal, you should also see more activity by Alice_AEA after Faber_AEA was started. For example:

The last error line in Alice_AEA's terminal is caused due to the absence of an Indy ledger instance. In the next update to this demo, this will be resolved.

"},{"location":"aea-framework-documentation/aries-cloud-agent-demo/#terminate-and-delete-the-agents","title":"Terminate and Delete the Agents","text":"

You can terminate each agent by pressing Ctrl+C.

To delete the AEAs, go to the projects' parent directory and delete the AEAs:

aea delete aries_faber\naea delete aries_alice\n
"},{"location":"aea-framework-documentation/aries-cloud-agent-demo/#further-developments","title":"Further Developments","text":"

In the next update to this demo, the remaining interactions between AEAs and ACAs must be implemented. This means:

  • An instance of Indy ledger must be installed and running. See here for more detail.
  • The commands for running the ACAs need to be adjusted. Additional options relating to a wallet (wallet-name, type, key, storage-type, configuration, credentials) need to be fed to the ACAs as well as the ledger's genesis file so the ACAs can connect to the ledger.
  • The remaining interactions between the AEAs and ACAs as described here need to be implemented.
"},{"location":"aea-framework-documentation/build-aea-programmatically/","title":"Build an AEA Programmatically","text":"

These instructions detail the Python code you need for running an AEA outside the cli tool, using the code interface.

"},{"location":"aea-framework-documentation/build-aea-programmatically/#preparation","title":"Preparation","text":"

Get the packages directory from the AEA repository:

svn export https://github.com/fetchai/agents-aea.git/trunk/packages\n

Also, install aea-ledger-fetchai plug-in:

pip install aea-ledger-fetchai\n
"},{"location":"aea-framework-documentation/build-aea-programmatically/#imports","title":"Imports","text":"

First, import the necessary common Python libraries and classes.

import os\nimport time\nfrom threading import Thread\n

Then, import the application specific libraries.

from aea_ledger_fetchai import FetchAICrypto\nfrom aea.aea_builder import AEABuilder\nfrom aea.configurations.base import SkillConfig\nfrom aea.crypto.helpers import PRIVATE_KEY_PATH_SCHEMA, create_private_key\nfrom aea.helpers.file_io import write_with_lock\nfrom aea.skills.base import Skill\n

Set up a variable pointing to where the packages directory is located - this should be our current directory - and where the input and output files are located.

ROOT_DIR = \"./\"\nINPUT_FILE = \"input_file\"\nOUTPUT_FILE = \"output_file\"\nFETCHAI_PRIVATE_KEY_FILE = PRIVATE_KEY_PATH_SCHEMA.format(FetchAICrypto.identifier)\n
"},{"location":"aea-framework-documentation/build-aea-programmatically/#create-a-private-key","title":"Create a Private Key","text":"

We need a private key to populate the AEA's wallet.

    # Create a private key\ncreate_private_key(FetchAICrypto.identifier, FETCHAI_PRIVATE_KEY_FILE)\n
"},{"location":"aea-framework-documentation/build-aea-programmatically/#clearing-the-input-and-output-files","title":"Clearing the Input and Output Files","text":"

We will use the stub connection to pass envelopes in and out of the AEA. Ensure that any input and output text files are removed before we start.

    # Ensure the input and output files do not exist initially\nif os.path.isfile(INPUT_FILE):\nos.remove(INPUT_FILE)\nif os.path.isfile(OUTPUT_FILE):\nos.remove(OUTPUT_FILE)\n
"},{"location":"aea-framework-documentation/build-aea-programmatically/#initialise-the-aea","title":"Initialise the AEA","text":"

We use the AEABuilder to readily build an AEA. By default, the AEABuilder adds the fetchai/default:1.1.7, fetchai/state_update:1.1.7 and fetchai/signing:1.1.7 protocols.

    # Instantiate the builder and build the AEA\n# By default, the default protocol, error skill and stub connection are added\nbuilder = AEABuilder()\n

We set the name, add the private key for the AEA to use and set the ledger configurations for the AEA to use.

    builder.set_name(\"my_aea\")\nbuilder.add_private_key(FetchAICrypto.identifier, FETCHAI_PRIVATE_KEY_FILE)\n

Next, we add the fetchai/stub:0.15.0 connection which will read/write messages from file:

    # Add the stub connection (assuming it is present in the local directory 'packages')\nbuilder.add_connection(\"./packages/fetchai/connections/stub\")\n

Next, we add the echo skill which will bounce our messages back to us. We first need to place the echo skill into a relevant directory (see path), either by downloading the packages directory from the AEA repo or by getting the package from the registry.

    # Add the echo skill (assuming it is present in the local directory 'packages')\nbuilder.add_skill(\"./packages/fetchai/skills/echo\")\n

Also, we can add a component that was instantiated programmatically. :

    # create skill and handler manually\nfrom aea.protocols.base import Message\nfrom aea.skills.base import Handler\nfrom packages.fetchai.protocols.default.message import DefaultMessage\nclass DummyHandler(Handler):\n\"\"\"Dummy handler to handle messages.\"\"\"\nSUPPORTED_PROTOCOL = DefaultMessage.protocol_id\ndef setup(self) -> None:\n\"\"\"Noop setup.\"\"\"\ndef teardown(self) -> None:\n\"\"\"Noop teardown.\"\"\"\ndef handle(self, message: Message) -> None:\n\"\"\"Handle incoming message.\"\"\"\nself.context.logger.info(\"You got a message: {}\".format(str(message)))\nconfig = SkillConfig(name=\"test_skill\", author=\"fetchai\")\nskill = Skill(configuration=config)\ndummy_handler = DummyHandler(\nname=\"dummy_handler\", skill_context=skill.skill_context\n)\nskill.handlers.update({dummy_handler.name: dummy_handler})\nbuilder.add_component_instance(skill)\n

Finally, we can build our AEA:

    # Create our AEA\nmy_aea = builder.build()\n
"},{"location":"aea-framework-documentation/build-aea-programmatically/#start-the-aea","title":"Start the AEA","text":"

We run the AEA from a different thread so that we can still use the main thread to pass it messages.

    # Set the AEA running in a different thread\ntry:\nt = Thread(target=my_aea.start)\nt.start()\n# Wait for everything to start up\ntime.sleep(4)\n
"},{"location":"aea-framework-documentation/build-aea-programmatically/#send-and-receive-an-envelope","title":"Send and Receive an Envelope","text":"

We use the input and output text files to send an envelope to our AEA and receive a response (from the echo skill)

        # Create a message inside an envelope and get the stub connection to pass it on to the echo skill\nmessage_text = b\"my_aea,other_agent,fetchai/default:1.0.0,\\x12\\x10\\x08\\x01\\x12\\x011*\\t*\\x07\\n\\x05hello,\"\nwith open(INPUT_FILE, \"wb\") as f:\nwrite_with_lock(f, message_text)\nprint(b\"input message: \" + message_text)\n# Wait for the envelope to get processed\ntime.sleep(4)\n# Read the output envelope generated by the echo skill\nwith open(OUTPUT_FILE, \"rb\") as f:\nprint(b\"output message: \" + f.readline())\n
"},{"location":"aea-framework-documentation/build-aea-programmatically/#shutdown","title":"Shutdown","text":"

Finally, stop our AEA and wait for it to finish

    finally:\n# Shut down the AEA\nmy_aea.stop()\nt.join()\nt = None\n
"},{"location":"aea-framework-documentation/build-aea-programmatically/#running-the-aea","title":"Running the AEA","text":"

If you now run this python script file, you should see this output:

input message: my_aea,other_agent,fetchai/default:1.0.0,\\x12\\x10\\x08\\x01\\x12\\x011*\\t*\\x07\\n\\x05hello,\noutput message: other_agent,my_aea,fetchai/default:1.0.0,...\\x05hello\n
"},{"location":"aea-framework-documentation/build-aea-programmatically/#entire-code-listing","title":"Entire Code Listing","text":"

If you just want to copy and past the entire script in you can find it here:

Click here to see full listing:
import os\nimport time\nfrom threading import Thread\nfrom aea_ledger_fetchai import FetchAICrypto\nfrom aea.aea_builder import AEABuilder\nfrom aea.configurations.base import SkillConfig\nfrom aea.crypto.helpers import PRIVATE_KEY_PATH_SCHEMA, create_private_key\nfrom aea.helpers.file_io import write_with_lock\nfrom aea.skills.base import Skill\nROOT_DIR = \"./\"\nINPUT_FILE = \"input_file\"\nOUTPUT_FILE = \"output_file\"\nFETCHAI_PRIVATE_KEY_FILE = PRIVATE_KEY_PATH_SCHEMA.format(FetchAICrypto.identifier)\ndef run():\n\"\"\"Run demo.\"\"\"\n# Create a private key\ncreate_private_key(FetchAICrypto.identifier, FETCHAI_PRIVATE_KEY_FILE)\n# Ensure the input and output files do not exist initially\nif os.path.isfile(INPUT_FILE):\nos.remove(INPUT_FILE)\nif os.path.isfile(OUTPUT_FILE):\nos.remove(OUTPUT_FILE)\n# Instantiate the builder and build the AEA\n# By default, the default protocol, error skill and stub connection are added\nbuilder = AEABuilder()\nbuilder.set_name(\"my_aea\")\nbuilder.add_private_key(FetchAICrypto.identifier, FETCHAI_PRIVATE_KEY_FILE)\n# Add the stub connection (assuming it is present in the local directory 'packages')\nbuilder.add_connection(\"./packages/fetchai/connections/stub\")\n# Add the echo skill (assuming it is present in the local directory 'packages')\nbuilder.add_skill(\"./packages/fetchai/skills/echo\")\n# create skill and handler manually\nfrom aea.protocols.base import Message\nfrom aea.skills.base import Handler\nfrom packages.fetchai.protocols.default.message import DefaultMessage\nclass DummyHandler(Handler):\n\"\"\"Dummy handler to handle messages.\"\"\"\nSUPPORTED_PROTOCOL = DefaultMessage.protocol_id\ndef setup(self) -> None:\n\"\"\"Noop setup.\"\"\"\ndef teardown(self) -> None:\n\"\"\"Noop teardown.\"\"\"\ndef handle(self, message: Message) -> None:\n\"\"\"Handle incoming message.\"\"\"\nself.context.logger.info(\"You got a message: {}\".format(str(message)))\nconfig = SkillConfig(name=\"test_skill\", author=\"fetchai\")\nskill = Skill(configuration=config)\ndummy_handler = DummyHandler(\nname=\"dummy_handler\", skill_context=skill.skill_context\n)\nskill.handlers.update({dummy_handler.name: dummy_handler})\nbuilder.add_component_instance(skill)\n# Create our AEA\nmy_aea = builder.build()\n# Set the AEA running in a different thread\ntry:\nt = Thread(target=my_aea.start)\nt.start()\n# Wait for everything to start up\ntime.sleep(4)\n# Create a message inside an envelope and get the stub connection to pass it on to the echo skill\nmessage_text = b\"my_aea,other_agent,fetchai/default:1.0.0,\\x12\\x10\\x08\\x01\\x12\\x011*\\t*\\x07\\n\\x05hello,\"\nwith open(INPUT_FILE, \"wb\") as f:\nwrite_with_lock(f, message_text)\nprint(b\"input message: \" + message_text)\n# Wait for the envelope to get processed\ntime.sleep(4)\n# Read the output envelope generated by the echo skill\nwith open(OUTPUT_FILE, \"rb\") as f:\nprint(b\"output message: \" + f.readline())\nfinally:\n# Shut down the AEA\nmy_aea.stop()\nt.join()\nt = None\nif __name__ == \"__main__\":\nrun()\n
"},{"location":"aea-framework-documentation/build-aea-step-by-step/","title":"Build an AEA with the CLI","text":"

Building an AEA step by step (ensure you have followed the Preliminaries and Installation sections from the AEA quick start first):

  1. Set up your AEA project with the CLI: aea create my_aea && cd my_aea
  2. Look at, then add the right connections for your use case: aea search connections, then aea add connection [public_id]
  3. Look for, then add or generate the protocols you require: aea search protocols, then aea add protocol [public_id] or aea generate protocol [path_to_specification]
  4. Look for, then add or code the skills you need: aea search skills, then aea add skill [public_id]. This guide shows you step by step how to develop a skill.
  5. Where required, scaffold any of the above resources with the scaffolding tool or generate a protocol with the protocol generator.
  6. Now, run your AEA: aea run --connections [public_id]

See information on the CLI tool here for all the available commands.

"},{"location":"aea-framework-documentation/car-park-skills/","title":"Car park skills","text":"

The AEA car-park skills demonstrate an interaction between two AEAs.

  • The carpark_detection AEA provides information on the number of car parking spaces available in a given vicinity.
  • The carpark_client AEA is interested in purchasing information on available car parking spaces in the same vicinity.
"},{"location":"aea-framework-documentation/car-park-skills/#discussion","title":"Discussion","text":"

The full Fetch.ai car park AEA demo is documented in its own repo here. This demo allows you to test the AEA functionality of the car park AEA demo without the detection logic.

It demonstrates how the AEAs trade car park information.

"},{"location":"aea-framework-documentation/car-park-skills/#communication","title":"Communication","text":"

This diagram shows the communication between the various entities as data is successfully sold by the car park AEA to the client.

    sequenceDiagram\n        participant Search\n        participant Car_Data_Buyer_AEA\n        participant Car_Park_AEA\n        participant Blockchain\n\n        activate Search\n        activate Car_Data_Buyer_AEA\n        activate Car_Park_AEA\n        activate Blockchain\n\n        Car_Park_AEA->>Search: register_service\n        Car_Data_Buyer_AEA->>Search: search\n        Search-->>Car_Data_Buyer_AEA: list_of_agents\n        Car_Data_Buyer_AEA->>Car_Park_AEA: call_for_proposal\n        Car_Park_AEA->>Car_Data_Buyer_AEA: propose\n        Car_Data_Buyer_AEA->>Car_Park_AEA: accept\n        Car_Park_AEA->>Car_Data_Buyer_AEA: match_accept\n        Car_Data_Buyer_AEA->>Blockchain: transfer_funds\n        Car_Data_Buyer_AEA->>Car_Park_AEA: send_transaction_hash\n        Car_Park_AEA->>Blockchain: check_transaction_status\n        Car_Park_AEA->>Car_Data_Buyer_AEA: send_data\n\n        deactivate Search\n        deactivate Car_Data_Buyer_AEA\n        deactivate Car_Park_AEA\n        deactivate Blockchain
"},{"location":"aea-framework-documentation/car-park-skills/#option-1-aea-manager-approach","title":"Option 1: AEA Manager Approach","text":"

Follow this approach when using the AEA Manager Desktop app. Otherwise, skip and follow the CLI approach below.

"},{"location":"aea-framework-documentation/car-park-skills/#preparation-instructions","title":"Preparation Instructions","text":"

Install the AEA Manager.

"},{"location":"aea-framework-documentation/car-park-skills/#demo-instructions","title":"Demo Instructions","text":"

The following steps assume you have launched the AEA Manager Desktop app.

  1. Add a new AEA called car_detector with public id fetchai/car_detector:0.32.5.

  2. Add another new AEA called car_data_buyer with public id fetchai/car_data_buyer:0.33.5.

  3. Copy the address from the car_data_buyer into your clip board. Then go to the Dorado block explorer and request some test tokens via Get Funds.

  4. Run the car_detector AEA. Navigate to its logs and copy the multiaddress displayed.

  5. Navigate to the settings of the car_data_buyer and under components > connection > fetchai/p2p_libp2p:0.22.0 update as follows (make sure to replace the placeholder with the multiaddress):

    {\n\"delegate_uri\": \"127.0.0.1:11001\",\n  \"entry_peers\": [\"REPLACE_WITH_MULTI_ADDRESS_HERE\"],\n  \"local_uri\": \"127.0.0.1:9001\",\n  \"log_file\": \"libp2p_node.log\",\n  \"public_uri\": \"127.0.0.1:9001\"\n}\n
  6. Run the car_data_buyer.

In the AEA's logs, you should see the agent trading successfully.

"},{"location":"aea-framework-documentation/car-park-skills/#option-2-cli-approach","title":"Option 2: CLI Approach","text":"

Follow this approach when using the aea CLI.

"},{"location":"aea-framework-documentation/car-park-skills/#preparation-instructions_1","title":"Preparation Instructions","text":""},{"location":"aea-framework-documentation/car-park-skills/#dependencies","title":"Dependencies","text":"

Follow the Preliminaries and Installation sections from the AEA quick start.

"},{"location":"aea-framework-documentation/car-park-skills/#demo-instructions_1","title":"Demo Instructions","text":""},{"location":"aea-framework-documentation/car-park-skills/#create-car-detector-aea","title":"Create Car Detector AEA","text":"

First, fetch the car detector AEA:

aea fetch fetchai/car_detector:0.32.5\ncd car_detector\naea install\naea build\n
Alternatively, create from scratch:

The following steps create the car detector from scratch:

aea create car_detector\ncd car_detector\naea add connection fetchai/p2p_libp2p:0.27.5\naea add connection fetchai/soef:0.27.6\naea add connection fetchai/ledger:0.21.5\naea add skill fetchai/carpark_detection:0.27.6\naea config set --type dict agent.dependencies \\\n'{\n  \"aea-ledger-fetchai\": {\"version\": \"<2.0.0,>=1.0.0\"}\n}'\naea config set agent.default_connection fetchai/p2p_libp2p:0.27.5\naea config set --type dict agent.default_routing \\\n'{\n  \"fetchai/ledger_api:1.1.7\": \"fetchai/ledger:0.21.5\",\n  \"fetchai/oef_search:1.1.7\": \"fetchai/soef:0.27.6\"\n}'\naea install\naea build\n
"},{"location":"aea-framework-documentation/car-park-skills/#create-car-data-buyer-aea","title":"Create Car Data Buyer AEA","text":"

Then, fetch the car data client AEA:

aea fetch fetchai/car_data_buyer:0.33.5\ncd car_data_buyer\naea install\naea build\n
Alternatively, create from scratch:

The following steps create the car data client from scratch:

aea create car_data_buyer\ncd car_data_buyer\naea add connection fetchai/p2p_libp2p:0.27.5\naea add connection fetchai/soef:0.27.6\naea add connection fetchai/ledger:0.21.5\naea add skill fetchai/carpark_client:0.27.6\naea config set --type dict agent.dependencies \\\n'{\n  \"aea-ledger-fetchai\": {\"version\": \"<2.0.0,>=1.0.0\"}\n}'\naea config set agent.default_connection fetchai/p2p_libp2p:0.27.5\naea config set --type dict agent.default_routing \\\n'{\n  \"fetchai/ledger_api:1.1.7\": \"fetchai/ledger:0.21.5\",\n  \"fetchai/oef_search:1.1.7\": \"fetchai/soef:0.27.6\"\n}'\naea install\naea build\n
"},{"location":"aea-framework-documentation/car-park-skills/#add-keys-for-the-car-data-seller-aea","title":"Add Keys for the Car Data Seller AEA","text":"

First, create the private key for the car data seller AEA based on the network you want to transact. To generate and add a private-public key pair for Fetch.ai Dorado use:

aea generate-key fetchai\naea add-key fetchai fetchai_private_key.txt\n

Next, create a private key used to secure the AEA's communications:

aea generate-key fetchai fetchai_connection_private_key.txt\naea add-key fetchai fetchai_connection_private_key.txt --connection\n

Finally, certify the key for use by the connections that request that:

aea issue-certificates\n
"},{"location":"aea-framework-documentation/car-park-skills/#add-keys-and-generate-wealth-for-the-car-data-buyer-aea","title":"Add Keys and Generate Wealth for the Car Data Buyer AEA","text":"

The buyer needs to have some wealth to purchase the service from the seller.

First, create the private key for the car data buyer AEA based on the network you want to transact. To generate and add a private-public key pair for Fetch.ai Dorado use:

aea generate-key fetchai\naea add-key fetchai fetchai_private_key.txt\n

Then, create some wealth for your car data buyer based on the network you want to transact with. On the Fetch.ai Dorado network:

aea generate-wealth fetchai\n

Next, create a private key used to secure the AEA's communications:

aea generate-key fetchai fetchai_connection_private_key.txt\naea add-key fetchai fetchai_connection_private_key.txt --connection\n

Finally, certify the key for use by the connections that request that:

aea issue-certificates\n
"},{"location":"aea-framework-documentation/car-park-skills/#run-the-aeas","title":"Run the AEAs","text":"

Run both AEAs from their respective terminals.

First, run the car data seller AEA:

aea run\n

Once you see a message of the form To join its network use multiaddr 'SOME_ADDRESS' take note of the address. (Alternatively, use aea get-multiaddress fetchai -c -i fetchai/p2p_libp2p:0.27.5 -u public_uri to retrieve the address.) This is the entry peer address for the local agent communication network created by the car data seller.

Then, in the car data buyer, run this command (replace SOME_ADDRESS with the correct value as described above):

aea config set --type dict vendor.fetchai.connections.p2p_libp2p.config \\\n'{\n  \"delegate_uri\": \"127.0.0.1:11001\",\n  \"entry_peers\": [\"SOME_ADDRESS\"],\n  \"local_uri\": \"127.0.0.1:9001\",\n  \"log_file\": \"libp2p_node.log\",\n  \"public_uri\": \"127.0.0.1:9001\"\n}'\n

This allows the car data buyer to connect to the same local agent communication network as the car data seller.

Then run the buyer AEA:

aea run\n

You will see that the AEAs negotiate and then transact using the Fetch.ai testnet.

"},{"location":"aea-framework-documentation/car-park-skills/#cleaning-up","title":"Cleaning up","text":"

When you're finished, delete your AEAs:

cd ..\naea delete car_detector\naea delete car_data_buyer\n
"},{"location":"aea-framework-documentation/cli-commands/","title":"CLI Commands","text":"Command Description add [package_type] [public_id] Add a package_type connection, contract, protocol, or skill, with [public_id], to the AEA. add --local to add from local packages directory. add-key [ledger_id] file [--connection] Add a private key from a file for ledger_id. build Build the agent and its components. config get [path] Reads the configuration specified in path and prints its target. config set [path] [--type TYPE] Sets a new value for the target of the path. Optionally cast to type. create [name] Create a new AEA project called name. delete [name] Delete an AEA project. See below for disabling a resource. eject [package_type] [public_id] Move a package of package_type and package_id from vendor to project working directory. fetch [public_id] Fetch an AEA project with public_id. fetch --local to fetch from local packages directory. fingerprint [package_type] [public_id] Fingerprint connection, contract, protocol, or skill, with public_id. freeze Get all the dependencies needed for the AEA project and its components. generate protocol [protocol_spec_path] Generate a protocol from the specification. generate-key [ledger_id] Generate private keys. The AEA uses a private key to derive the associated public key and address. generate-wealth [ledger_id] Generate wealth for address on test network. get-address [ledger_id] Get the address associated with the private key. get-multiaddress [ledger_id]... Get the multiaddress associated with a private key or connection. get-public-key [ledger_id]... Get the public key associated with a private key of the agent. get-wealth [ledger_id] Get the wealth associated with the private key. init Initialize your AEA configurations. (With --author to define author.) install [-r <requirements_file>] Install the dependencies. (With --install-deps to install dependencies.) interact Interact with a running AEA via the stub connection. ipfs IPFS Commands issue-certificates Issue the connection certificates. launch [path_to_agent_project]... Launch many agents at the same time. list [package_type] List the installed resources. local-registry-sync Upgrade the local package registry. login USERNAME [--password password] Login to a registry account with credentials. logout Logout from registry account. publish Publish the AEA to registry. Needs to be executed from an AEA project.publish --local to publish to local packages directory. push [package_type] [public_id] Push connection, protocol, or skill with public_id to registry. push --local to push to local packages directory. register Create a new registry account. remove [package_type] [name] Remove connection, protocol, or skill, called name, from AEA. remove-key [ledger_id] [name] Remove a private key registered with id ledger_id. reset_password EMAIL Reset the password of the registry account. run {using [connections, ...]} Run the AEA on the Fetch.ai network with default or specified connections. scaffold [package_type] [name] Scaffold a new connection, protocol, or skill called name. search [package_type] Search for components in the registry. search --local [package_type] [--query searching_query] to search in local packages directory. transfer [type] [address] [amount] Transfer wealth associated with a private key of the agent to another account. upgrade [package_type] [public_id] Upgrade the packages of the agent. -v DEBUG run Run with debugging.

Tip

You can also disable a resource without deleting it by removing the entry from the configuration but leaving the package in the skills namespace.

Tip

You can skip the consistency checks on the AEA project by using the flag --skip-consistency-check. E.g. aea --skip-consistency-check run will bypass the fingerprint checks.

"},{"location":"aea-framework-documentation/cli-vs-programmatic-aeas/","title":"CLI vs Programmatic AEAs","text":"

The AEA framework enables us to create agents either from the CLI tool or programmatically.

The following demo demonstrates an interaction between two AEAs.

The provider of weather data (managed with the CLI). The buyer of weather data (managed programmatically).

"},{"location":"aea-framework-documentation/cli-vs-programmatic-aeas/#discussion","title":"Discussion","text":"

The scope of the specific demo is to demonstrate how a CLI based AEA can interact with a programmatically managed AEA. In order to achieve this we are going to use the weather station skills. This demo does not utilize a smart contract or a ledger interaction.

"},{"location":"aea-framework-documentation/cli-vs-programmatic-aeas/#get-required-packages","title":"Get Required Packages","text":"

Copy the packages directory into your local working directory:

svn export https://github.com/fetchai/agents-aea.git/trunk/packages\n

Also, install aea-ledger-fetchai plug-in:

pip install aea-ledger-fetchai\n
"},{"location":"aea-framework-documentation/cli-vs-programmatic-aeas/#demo-instructions","title":"Demo Instructions","text":"

If you want to create the weather station AEA step by step you can follow this guide here

"},{"location":"aea-framework-documentation/cli-vs-programmatic-aeas/#create-the-weather-station-aea","title":"Create the Weather Station AEA","text":"

Fetch the weather station AEA with the following command :

aea fetch fetchai/weather_station:0.32.5\ncd weather_station\naea install\naea build\n
"},{"location":"aea-framework-documentation/cli-vs-programmatic-aeas/#update-the-aea-configurations","title":"Update the AEA Configurations","text":"

In the terminal change the configuration:

aea config set vendor.fetchai.skills.weather_station.models.strategy.args.is_ledger_tx False --type bool\n

The is_ledger_tx will prevent the AEA to communicate with a ledger.

"},{"location":"aea-framework-documentation/cli-vs-programmatic-aeas/#add-keys","title":"Add Keys","text":"

Add a private key for the weather station.

aea generate-key fetchai\naea add-key fetchai fetchai_private_key.txt\n

Next, create a private key used to secure the AEA's communications:

aea generate-key fetchai fetchai_connection_private_key.txt\naea add-key fetchai fetchai_connection_private_key.txt --connection\n

Finally, certify the key for use by the connections that request that:

aea issue-certificates\n
"},{"location":"aea-framework-documentation/cli-vs-programmatic-aeas/#run-the-weather-station-aea","title":"Run the Weather Station AEA","text":"
aea run\n

Once you see a message of the form To join its network use multiaddr: ['SOME_ADDRESS'] take note of the address.

"},{"location":"aea-framework-documentation/cli-vs-programmatic-aeas/#create-the-weather-client-aea","title":"Create the Weather Client AEA","text":"

Since we want to show the interaction between a programmatically created AEA with a CLI based AEA we are going to write some code for the client.

Create a new python file and name it weather_client.py and add the following code

Weather client full code:
import logging\nimport os\nimport sys\nfrom typing import cast\nfrom aea_ledger_fetchai import FetchAICrypto\nfrom aea.aea import AEA\nfrom aea.aea_builder import AEABuilder\nfrom aea.configurations.base import ConnectionConfig\nfrom aea.crypto.helpers import (\nPRIVATE_KEY_PATH_SCHEMA,\ncreate_private_key,\nmake_certificate,\n)\nfrom aea.crypto.wallet import Wallet\nfrom aea.helpers.base import CertRequest\nfrom aea.identity.base import Identity\nfrom aea.protocols.base import Protocol\nfrom aea.registries.resources import Resources\nfrom aea.skills.base import Skill\nimport packages.fetchai.connections.p2p_libp2p.connection\nfrom packages.fetchai.connections.ledger.connection import LedgerConnection\nfrom packages.fetchai.connections.p2p_libp2p.connection import P2PLibp2pConnection\nfrom packages.fetchai.connections.soef.connection import SOEFConnection\nfrom packages.fetchai.protocols.ledger_api.message import LedgerApiMessage\nfrom packages.fetchai.protocols.oef_search.message import OefSearchMessage\nfrom packages.fetchai.skills.weather_client.strategy import Strategy\nAPI_KEY = \"TwiCIriSl0mLahw17pyqoA\"\nSOEF_ADDR = \"s-oef.fetch.ai\"\nSOEF_PORT = 443\nENTRY_PEER_ADDRESS = (\n\"/dns4/127.0.0.1/tcp/9000/p2p/16Uiu2HAmLBCAqHL8SuFosyDhAKYsLKXBZBWXBsB9oFw2qU4Kckun\"\n)\nFETCHAI_PRIVATE_KEY_FILE = PRIVATE_KEY_PATH_SCHEMA.format(FetchAICrypto.identifier)\nFETCHAI_PRIVATE_KEY_FILE_CONNECTION = PRIVATE_KEY_PATH_SCHEMA.format(\n\"fetchai_connection\"\n)\nROOT_DIR = os.getcwd()\nlogger = logging.getLogger(\"aea\")\nlogging.basicConfig(stream=sys.stdout, level=logging.INFO)\ndef run():\n\"\"\"Run demo.\"\"\"\n# Create a private key\ncreate_private_key(FetchAICrypto.identifier, FETCHAI_PRIVATE_KEY_FILE)\ncreate_private_key(FetchAICrypto.identifier, FETCHAI_PRIVATE_KEY_FILE_CONNECTION)\n# Set up the wallet, identity and (empty) resources\nwallet = Wallet(\nprivate_key_paths={FetchAICrypto.identifier: FETCHAI_PRIVATE_KEY_FILE},\nconnection_private_key_paths={\nFetchAICrypto.identifier: FETCHAI_PRIVATE_KEY_FILE_CONNECTION\n},\n)\nidentity = Identity(\n\"my_aea\",\naddress=wallet.addresses.get(FetchAICrypto.identifier),\npublic_key=wallet.public_keys.get(FetchAICrypto.identifier),\n)\nresources = Resources()\ndata_dir = os.getcwd()\n# specify the default routing for some protocols\ndefault_routing = {\nLedgerApiMessage.protocol_id: LedgerConnection.connection_id,\nOefSearchMessage.protocol_id: SOEFConnection.connection_id,\n}\ndefault_connection = P2PLibp2pConnection.connection_id\nstate_update_protocol = Protocol.from_dir(\nos.path.join(os.getcwd(), \"packages\", \"fetchai\", \"protocols\", \"state_update\")\n)\nresources.add_protocol(state_update_protocol)\n# Add the default protocol (which is part of the AEA distribution)\ndefault_protocol = Protocol.from_dir(\nos.path.join(os.getcwd(), \"packages\", \"fetchai\", \"protocols\", \"default\")\n)\nresources.add_protocol(default_protocol)\n# Add the signing protocol (which is part of the AEA distribution)\nsigning_protocol = Protocol.from_dir(\nos.path.join(os.getcwd(), \"packages\", \"fetchai\", \"protocols\", \"signing\")\n)\nresources.add_protocol(signing_protocol)\n# Add the ledger_api protocol\nledger_api_protocol = Protocol.from_dir(\nos.path.join(\nos.getcwd(),\n\"packages\",\n\"fetchai\",\n\"protocols\",\n\"ledger_api\",\n)\n)\nresources.add_protocol(ledger_api_protocol)\n# Add the oef_search protocol\noef_protocol = Protocol.from_dir(\nos.path.join(\nos.getcwd(),\n\"packages\",\n\"fetchai\",\n\"protocols\",\n\"oef_search\",\n)\n)\nresources.add_protocol(oef_protocol)\n# Add the fipa protocol\nfipa_protocol = Protocol.from_dir(\nos.path.join(\nos.getcwd(),\n\"packages\",\n\"fetchai\",\n\"protocols\",\n\"fipa\",\n)\n)\nresources.add_protocol(fipa_protocol)\n# Add the LedgerAPI connection\nconfiguration = ConnectionConfig(connection_id=LedgerConnection.connection_id)\nledger_api_connection = LedgerConnection(\nconfiguration=configuration, data_dir=data_dir, identity=identity\n)\nresources.add_connection(ledger_api_connection)\n# Add the P2P connection\ncert_path = \".certs/conn_cert.txt\"\ncert_request = CertRequest(\nidentifier=\"acn\",\nledger_id=FetchAICrypto.identifier,\nnot_after=\"2022-01-01\",\nnot_before=\"2021-01-01\",\npublic_key=\"fetchai\",\nmessage_format=\"{public_key}\",\nsave_path=cert_path,\n)\npublic_key = wallet.connection_cryptos.public_keys.get(FetchAICrypto.identifier)\nmessage = cert_request.get_message(public_key)\nmake_certificate(\nFetchAICrypto.identifier, FETCHAI_PRIVATE_KEY_FILE, message, cert_path\n)\nconfiguration = ConnectionConfig(\nconnection_id=P2PLibp2pConnection.connection_id,\ndelegate_uri=\"127.0.0.1:11001\",\nentry_peers=[ENTRY_PEER_ADDRESS],\nlocal_uri=\"127.0.0.1:9001\",\nlog_file=\"libp2p_node.log\",\npublic_uri=\"127.0.0.1:9001\",\nbuild_directory=os.getcwd(),\nbuild_entrypoint=\"check_dependencies.py\",\ncert_requests=[cert_request],\n)\nconfiguration.directory = os.path.dirname(\npackages.fetchai.connections.p2p_libp2p.connection.__file__\n)\nAEABuilder.run_build_for_component_configuration(configuration)\np2p_connection = P2PLibp2pConnection(\nconfiguration=configuration,\ndata_dir=data_dir,\nidentity=identity,\ncrypto_store=wallet.connection_cryptos,\n)\nresources.add_connection(p2p_connection)\n# Add the SOEF connection\nconfiguration = ConnectionConfig(\napi_key=API_KEY,\nsoef_addr=SOEF_ADDR,\nsoef_port=SOEF_PORT,\nrestricted_to_protocols={OefSearchMessage.protocol_id},\nconnection_id=SOEFConnection.connection_id,\n)\nsoef_connection = SOEFConnection(\nconfiguration=configuration, data_dir=data_dir, identity=identity\n)\nresources.add_connection(soef_connection)\n# create the AEA\nmy_aea = AEA(\nidentity,\nwallet,\nresources,\ndata_dir,\ndefault_connection=default_connection,\ndefault_routing=default_routing,\n)\n# Add the error and weather_client skills\nerror_skill = Skill.from_dir(\nos.path.join(ROOT_DIR, \"packages\", \"fetchai\", \"skills\", \"error\"),\nagent_context=my_aea.context,\n)\nweather_skill = Skill.from_dir(\nos.path.join(ROOT_DIR, \"packages\", \"fetchai\", \"skills\", \"weather_client\"),\nagent_context=my_aea.context,\n)\nstrategy = cast(Strategy, weather_skill.models.get(\"strategy\"))\nstrategy._is_ledger_tx = False\nfor skill in [error_skill, weather_skill]:\nresources.add_skill(skill)\n# Run the AEA\ntry:\nlogger.info(\"STARTING AEA NOW!\")\nmy_aea.start()\nexcept KeyboardInterrupt:\nlogger.info(\"STOPPING AEA NOW!\")\nmy_aea.stop()\nif __name__ == \"__main__\":\nrun()\n

Now replace ENTRY_PEER_ADDRESS with the peer address (SOME_ADDRESS) noted above.

For more details on how to create an agent programmatically follow this guide here.

"},{"location":"aea-framework-documentation/cli-vs-programmatic-aeas/#run-the-weather-client-aea","title":"Run the Weather Client AEA","text":"

In a new terminal window, navigate to the folder that you created the script and run:

python weather_client.py\n

You should see both AEAs interacting now.

"},{"location":"aea-framework-documentation/config/","title":"Configurations","text":"

This document describes the configuration files of the different packages.

"},{"location":"aea-framework-documentation/config/#aea-configuration-yaml","title":"AEA Configuration YAML","text":"

The following provides a list of the relevant regex used:

PACKAGE_REGEX: \"[a-zA-Z_][a-zA-Z0-9_]*\"\nAUTHOR_REGEX: \"[a-zA-Z_][a-zA-Z0-9_]*\"\nPUBLIC_ID_REGEX: \"^[a-zA-Z0-9_]*/[a-zA-Z_][a-zA-Z0-9_]*:(0|[1-9]\\\\d*)\\\\.(0|[1-9]\\\\d*)\\\\.(0|[1-9]\\\\d*)(?:-((?:0|[1-9]\\\\d*|\\\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\\\.(?:0|[1-9]\\\\d*|\\\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\\\+([0-9a-zA-Z-]+(?:\\\\.[0-9a-zA-Z-]+)*))?$\"\nLEDGER_ID_REGEX: \"^[^\\\\d\\\\W]\\\\w*\\\\Z\"\n

The aea-config.yaml defines the AEA project. The compulsory components are listed below:

agent_name: my_agent                            # Name of the AEA project (must satisfy PACKAGE_REGEX)\nauthor: fetchai                                 # Author handle of the project's author (must satisfy AUTHOR_REGEX)\nversion: 0.1.0                                  # Version of the AEA project (a semantic version number, see https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string\")\ndescription: A demo project                     # Description of the AEA project\nlicense: Apache-2.0                             # License of the AEA project\naea_version: '>=1.0.0, <2.0.0'               # AEA framework version(s) compatible with the AEA project (a version number that matches PEP 440 version schemes, or a comma-separated list of PEP 440 version specifiers, see https://www.python.org/dev/peps/pep-0440/#version-specifiers)\nfingerprint: {}                                 # Fingerprint of AEA project components.\nfingerprint_ignore_patterns: []                 # Ignore pattern for the fingerprinting tool.\nconnections:                                    # The list of connection public ids the AEA project depends on (each public id must satisfy PUBLIC_ID_REGEX)\n- fetchai/stub:0.21.3\ncontracts: []                                   # The list of contract public ids the AEA project depends on (each public id must satisfy PUBLIC_ID_REGEX).\nprotocols:                                      # The list of protocol public ids the AEA project depends on (each public id must satisfy PUBLIC_ID_REGEX).\n- fetchai/default:1.1.7\nskills:                                         # The list of skill public ids the AEA project depends on (each public id must satisfy PUBLIC_ID_REGEX).\n- fetchai/error:0.18.6\ndefault_connection: fetchai/p2p_libp2p:0.27.5   # The default connection used for envelopes sent by the AEA (must satisfy PUBLIC_ID_REGEX).\ndefault_ledger: fetchai                         # The default ledger identifier the AEA project uses (must satisfy LEDGER_ID_REGEX)\nrequired_ledgers: [fetchai]                            # the list of identifiers of ledgers that the AEA project requires key pairs for (each item must satisfy LEDGER_ID_REGEX)\ndefault_routing: {}                             # The default routing scheme applied to envelopes sent by the AEA, it maps from protocol public ids to connection public ids (both keys and values must satisfy PUBLIC_ID_REGEX)\nconnection_private_key_paths:                   # The private key paths the AEA project uses for its connections (keys must satisfy LEDGER_ID_REGEX, values must be file paths)\nfetchai: fetchai_private_key.txt\nprivate_key_paths:                              # The private key paths the AEA project uses (keys must satisfy LEDGER_ID_REGEX, values must be file paths)\nfetchai: fetchai_private_key.txt\nlogging_config:                                 # The logging configurations the AEA project uses\ndisable_existing_loggers: false\nversion: 1\ndependencies: {}                                # The python dependencies the AEA relies on (e.g. plugins). They will be installed when `aea install` is run.\n

The aea-config.yaml can be extended with a number of optional fields:

period: 0.05                                    # The period to call agent's act\nexecution_timeout: 0                            # The execution time limit on each call to `react` and `act` (0 disables the feature)\ntimeout: 0.05                                   # The sleep time on each AEA loop spin (only relevant for the `sync` mode)\nmax_reactions: 20                               # The maximum number of envelopes processed per call to `react` (only relevant for the `sync` mode)\nskill_exception_policy: propagate               # The exception policy applied to skills (must be one of \"propagate\", \"just_log\", or \"stop_and_exit\")\nconnection_exception_policy: propagate          # The exception policy applied to connections (must be one of \"propagate\", \"just_log\", or \"stop_and_exit\")\nloop_mode: async                                # The agent loop mode (must be one of \"sync\" or \"async\")\nruntime_mode: threaded                          # The runtime mode (must be one of \"threaded\" or \"async\") and determines how agent loop and multiplexer are run\nerror_handler: None                             # The error handler to be used.\ndecision_maker_handler: None                    # The decision maker handler to be used.\nstorage_uri: None                               # The URI to the storage.\ndata_dir: None                                  # The path to the directory for local files. Defaults to current working directory.\n

The aea-config.yaml can further be extended with component configuration overrides.

For custom connection configurations:

public_id: some_author/some_package:0.1.0       # The public id of the connection (must satisfy PUBLIC_ID_REGEX).\ntype: connection                                # for connections, this must be \"connection\".\nconfig: ...                                     # a dictionary to overwrite the `config` field (see below)\n

For custom skill configurations:

public_id: some_author/some_package:0.1.0       # The public id of the connection (must satisfy PUBLIC_ID_REGEX).\ntype: skill                                     # for skills, this must be \"skill\".\nbehaviours:                                     # override configurations for behaviours\nbehaviour_1:                                  # override configurations for \"behaviour_1\"\nargs:                                       # arguments for a specific behaviour (see below)\nfoo: bar\nhandlers:                                       # override configurations for handlers\nhandler_1:                                    # override configurations for \"handler_1\"\nargs:                                       # arguments for a specific handler (see below)\nfoo: bar\nmodels:                                         # override configurations for models\nmodel_1:                                      # override configurations for \"model_1\"\nargs:                                       # arguments for a specific model (see below)\nfoo: bar\n
"},{"location":"aea-framework-documentation/config/#connection-configuration-yaml","title":"Connection Configuration YAML","text":"

The connection.yaml, which is present in each connection package, has the following required fields:

name: scaffold                                  # Name of the package (must satisfy PACKAGE_REGEX)\nauthor: fetchai                                 # Author handle of the package's author (must satisfy AUTHOR_REGEX)\nversion: 0.1.0                                  # Version of the package (a semantic version number, see https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string\")\ntype: connection                                # The type of the package; for connections, it must be \"connection\"\ndescription: A scaffold connection              # Description of the package\nlicense: Apache-2.0                             # License of the package\naea_version: '>=1.0.0, <2.0.0'               # AEA framework version(s) compatible with the AEA project (a version number that matches PEP 440 version schemes, or a comma-separated list of PEP 440 version specifiers, see https://www.python.org/dev/peps/pep-0440/#version-specifiers)\nfingerprint:                                    # Fingerprint of package components.\n__init__.py: QmZvYZ5ECcWwqiNGh8qNTg735wu51HqaLxTSifUxkQ4KGj\nconnection.py: QmagwVgaPgfeXqVTgcpFESA4DYsteSbojz94SLtmnHNAze\nfingerprint_ignore_patterns: []                 # Ignore pattern for the fingerprinting tool.\nconnections: []                                 # The list of connection public ids the package depends on (each public id must satisfy PUBLIC_ID_REGEX).\nprotocols: []                                   # The list of protocol public ids the package depends on (each public id must satisfy PUBLIC_ID_REGEX).\nclass_name: MyScaffoldConnection                # The class name of the class implementing the connection interface.\nconfig:                                         # A dictionary containing the kwargs for the connection instantiation.\nfoo: bar\nexcluded_protocols: []                          # The list of protocol public ids the package does not permit (each public id must satisfy PUBLIC_ID_REGEX).\nrestricted_to_protocols: []                     # The list of protocol public ids the package is limited to (each public id must satisfy PUBLIC_ID_REGEX).\ndependencies: {}                                # The python dependencies the package relies on. They will be installed when `aea install` is run.\nis_abstract: false                              # An optional boolean that if `true` makes the connection\n
"},{"location":"aea-framework-documentation/config/#contract-configuration-yaml","title":"Contract Configuration YAML","text":"

The contract.yaml, which is present in each contract package, has the following required fields:

name: scaffold                                  # Name of the package (must satisfy PACKAGE_REGEX)\nauthor: fetchai                                 # Author handle of the package's author (must satisfy AUTHOR_REGEX)\nversion: 0.1.0                                  # Version of the package (a semantic version number, see https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string\")\ntype: contract                                  # The type of the package; for contracts, it must be \"contract\"\ndescription: A scaffold contract                # Description of the package\nlicense: Apache-2.0                             # License of the package\naea_version: '>=1.0.0, <2.0.0'               # AEA framework version(s) compatible with the AEA project (a version number that matches PEP 440 version schemes, or a comma-separated list of PEP 440 version specifiers, see https://www.python.org/dev/peps/pep-0440/#version-specifiers)\nfingerprint:                                    # Fingerprint of package components.\n__init__.py: QmPBwWhEg3wcH1q9612srZYAYdANVdWLDFWKs7TviZmVj6\ncontract.py: QmXvjkD7ZVEJDJspEz5YApe5bRUxvZHNi8vfyeVHPyQD5G\nfingerprint_ignore_patterns: []                 # Ignore pattern for the fingerprinting tool.\nclass_name: MyScaffoldContract                  # The class name of the class implementing the contract interface.\ncontract_interface_paths: {}                    # The paths to the contract interfaces (one for each ledger identifier).\nconfig:                                         # A dictionary containing the kwargs for the contract instantiation.\nfoo: bar\ndependencies: {}                                # The python dependencies the package relies on. They will be installed when `aea install` is run.\n
"},{"location":"aea-framework-documentation/config/#protocol-configuration-yaml","title":"Protocol Configuration YAML","text":"

The protocol.yaml, which is present in each protocol package, has the following required fields:

name: scaffold                                  # Name of the package (must satisfy PACKAGE_REGEX)\nauthor: fetchai                                 # Author handle of the package's author (must satisfy AUTHOR_REGEX)\nversion: 0.1.0                                  # Version of the package (a semantic version number, see https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string\")\ntype: protocol                                  # The type of the package; for protocols, it must be \"protocol\" \ndescription: A scaffold protocol                # Description of the package\nlicense: Apache-2.0                             # License of the package\naea_version: '>=1.0.0, <2.0.0'               # AEA framework version(s) compatible with the AEA project (a version number that matches PEP 440 version schemes, or a comma-separated list of PEP 440 version specifiers, see https://www.python.org/dev/peps/pep-0440/#version-specifiers)\nfingerprint:                                    # Fingerprint of package components.\n__init__.py: Qmay9PmfeHqqVa3rdgiJYJnzZzTStboQEfpwXDpcgJMHTJ\nmessage.py: QmdvAdYSHNdZyUMrK3ue7quHAuSNwgZZSHqxYXyvh8Nie4\nserialization.py: QmVUzwaSMErJgNFYQZkzsDhuuT2Ht4EdbGJ443usHmPxVv\nfingerprint_ignore_patterns: []                 # Ignore pattern for the fingerprinting tool.\ndependencies: {}                                # The python dependencies the package relies on. They will be installed when `aea install` is run.\n
"},{"location":"aea-framework-documentation/config/#skill-configuration-yaml","title":"Skill Configuration YAML","text":"

The skill.yaml, which is present in each protocol package, has the following required fields:

name: scaffold                                  # Name of the package (must satisfy PACKAGE_REGEX)\nauthor: fetchai                                 # Author handle of the package's author (must satisfy AUTHOR_REGEX)\nversion: 0.1.0                                  # Version of the package (a semantic version number, see https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string\")\ntype: skill                                     # The type of the package; for skills, it must be \"skill\"\ndescription: A scaffold skill                   # Description of the package\nlicense: Apache-2.0                             # License of the package\naea_version: '>=1.0.0, <2.0.0'               # AEA framework version(s) compatible with the AEA project (a version number that matches PEP 440 version schemes, or a comma-separated list of PEP 440 version specifiers, see https://www.python.org/dev/peps/pep-0440/#version-specifiers)\nfingerprint:                                    # Fingerprint of package components.\n__init__.py: QmNkZAetyctaZCUf6ACxP5onGWsSxu2hjSNoFmJ3ta6Lta\nbehaviours.py: QmYa1rczhGTtMJBgCd1QR9uZhhkf45orm7TnGTE5Eizjpy\nhandlers.py: QmZYyTENRr6ecnxx1FeBdgjLiBhFLVn9mqarzUtFQmNUFn\nmy_model.py: QmPaZ6G37Juk63mJj88nParaEp71XyURts8AmmX1axs24V\nfingerprint_ignore_patterns: []                 # Ignore pattern for the fingerprinting tool.\ncontracts: []                                   # The list of contract public ids the package depends on (each public id must satisfy PUBLIC_ID_REGEX).\nprotocols: []                                   # The list of protocol public ids the package depends on (each public id must satisfy PUBLIC_ID_REGEX).\nskills: []                                      # The list of skill public ids the package depends on (each public id must satisfy PUBLIC_ID_REGEX).\nis_abstract: false                              # An optional boolean that if `true` makes the skill abstract, i.e. not instantiated by the framework but importable from other skills. Defaults to `false`. \nbehaviours:                                     # The dictionary describing the behaviours immplemented in the package (including their configuration)\nscaffold:                                     # Name of the behaviour under which it is made available on the skill context.\nargs:                                       # Keyword arguments provided to the skill component on instantiation.\nfoo: bar\nclass_name: MyScaffoldBehaviour             # The class name of the class implementing the behaviour interface.\nhandlers:                                       # The dictionary describing the handlers immplemented in the package (including their configuration)\nscaffold:                                     # Name of the handler under which it is made available on the skill\nargs:                                       # Keyword arguments provided to the skill component on instantiation.\nfoo: bar\nclass_name: MyScaffoldHandler               # The class name of the class implementing the handler interface.\nmodels:                                         # The dictionary describing the models immplemented in the package (including their configuration)\nscaffold:                                     # Name of the model under which it is made available on the skill\nargs:                                       # Keyword arguments provided to the skill component on instantiation.\nfoo: bar\nclass_name: MyModel                         # The class name of the class implementing the model interface.\ndependencies: {}                                # The python dependencies the package relies on. They will be installed when `aea install` is run.\n
"},{"location":"aea-framework-documentation/connect-a-frontend/","title":"Front-End Integration","text":"

This page lays out two options for connecting a front-end to an AEA. The following diagram illustrates these two options.

"},{"location":"aea-framework-documentation/connect-a-frontend/#case-1","title":"Case 1","text":"

The first option is to create a HTTP Server connection that handles incoming requests from a REST API. In this scenario, the REST API communicates with the AEA and requests are handled by the HTTP Server connection package. The REST API should send CRUD requests to the HTTP Server connection (fetchai/http_server:0.23.6) which translates these into Envelopes to be consumed by the correct skill.

"},{"location":"aea-framework-documentation/connect-a-frontend/#case-2","title":"Case 2","text":"

The second option is to create a front-end comprising a stand-alone Multiplexer with a P2P connection (fetchai/p2p_libp2p:0.27.5). In this scenario the Agent Communication Network can be used to send Envelopes from the AEA to the front-end.

"},{"location":"aea-framework-documentation/connection/","title":"Connections","text":"

A Connection provides an interface for the agent to connect with entities in the outside world. Connections wrap SDKs or APIs and provide interfaces to networks, ledgers and other services. As such, a connection is concerned with I/O bound and continuously connected operations. Where necessary, a connection is responsible for translating between the framework specific protocol (an Envelope with its contained Message) and the external service or third-party protocol (e.g. HTTP). Hence, there are two roles for connections: wrapper and transport connection. The transport connection is responsible to delivering AEA envelopes.

The messages constructed or received by a connection are eventually processed by one or several skills which deal with handling and generating messages related to a specific business objective.

An AEA can interact with multiple connections at the same time via the Multiplexer. Connections are passive in terms of multiplexer interactions (its methods are called by the Multiplexer), but they can run their own asynchronous or threaded tasks.

The Multiplexer maintains an InBox and OutBox, which are, respectively, queues for incoming and outgoing envelopes and their contained messages.

"},{"location":"aea-framework-documentation/connection/#developing-your-connection","title":"Developing your Connection","text":"

The easiest way to get started developing your own connection is by using the scaffold command:

aea scaffold connection my_new_connection\n

This will scaffold a connection package called my_new_connection with three files:

  • __init__.py
  • connection.py containing the scaffolded connection class
  • connection.yaml containing the scaffolded configuration file

As a developer you have the choice between implementing a sync or asynchronous interface. The scaffolded connection.py file contains two classes: the MyScaffoldAsyncConnection inherited from the Connection base class and the MyScaffoldSyncConnection inherited from the BaseSyncConnection. Remove the unused class.

"},{"location":"aea-framework-documentation/connection/#primary-methods-to-develop-asynchronous-connection-interface","title":"Primary Methods to Develop - Asynchronous Connection Interface","text":"

The developer needs to implement four public coroutines:

  • The connect coroutine implements the setup logic required to be performed for the connection when it is initially launched. The connect coroutine is called by the AEA framework once when the agent is being started.

  • The disconnect coroutine implements the teardown logic required to be performed for the connection when it is eventually stopped. The disconnect coroutine is called by the AEA framework once when the agent is being stopped.

  • The send coroutine is called by the AEA framework each time the Multiplexer handles an outgoing envelope specified to be handled by this connection. The send coroutine must implement the processing of the envelope leaving the agent.

  • The receive coroutine is continuously called by the AEA framework. It either returns None or an envelope. The receive coroutine must implement the logic of data being received by the agent, and if necessary, its translation into a relevant protocol.

The framework provides a demo stub connection which implements an I/O reader and writer to send and receive messages between the agent and a local file. To gain inspiration and become familiar with the structure of connection packages, you may find it useful to check out fetchai/stub:0.21.3, fetchai/http_server:0.23.6 or fetchai/http_client:0.24.6 connections. The latter two connections are for external clients to connect with an agent, and for the agent to connect with external servers, respectively.

"},{"location":"aea-framework-documentation/connection/#primary-methods-to-develop-sync-connection-interface","title":"Primary Methods to Develop - Sync Connection Interface","text":"

The BaseSyncConnection uses executors to execute synchronous code from the asynchronous context of the Multiplexer in executors/threads, which are limited by the amount of configured workers.

The asynchronous methods connect, disconnect and send are converted to callbacks which the developer implements:

  • on_connect
  • on_disconnect
  • on_send

All of these methods will be executed in the executor pool.

Every method can create a message by putting it into the thread/asynchronous friendly queue that is consumed by the Multiplexer.

The receive coroutine has no direct equivalent. Instead, the developer implements a main method which runs synchronously in the background.

"},{"location":"aea-framework-documentation/connection/#configuration","title":"Configuration","text":"

Every connection must have a configuration file in connection.yaml, containing meta-information about the connection as well as all the required configuration details. For more details, have a look here.

"},{"location":"aea-framework-documentation/connection/#configuration-options","title":"Configuration Options","text":"

The connection.yaml file contains a number of fields that must be edited by the developer of the connection:

connections: []\nprotocols: []\nclass_name: MyScaffoldConnection\nconfig:\nfoo: bar\nexcluded_protocols: []\nrestricted_to_protocols: []\ndependencies: {}\nis_abstract: false\ncert_requests: []\n
  • connections specifies the list of other connection this connection depends on
  • protocols specifies the list of protocols this connection depends on
  • class_name needs to match the name of the connection class in connection.py
  • config can contain arbitrary configuration information which is made available in the constructor of the connection as keyword arguments (**kwargs)
  • excluded_protocols lists the protocols which cannot be used in this connection
  • restricted_to_protocols lists the protocols which this connection is restricted to be used by
  • dependencies lists any Python dependencies of the connection package
  • is_abstract specifies whether this connection is only used as an abstract base class
  • cert_requests lists certification requests of the connection (see proof of representation for details)
"},{"location":"aea-framework-documentation/contract/","title":"Contracts","text":"

Contracts wrap smart contracts for Fetch.ai and third-party decentralized ledgers. In particular, they provide wrappers around the API or ABI of a smart contract and its byte code. They implement a translation between framework messages (in the fetchai/contract_api:1.0.0 protocol) and the implementation specifics of the ABI.

Contracts usually implement four types of methods:

  • a method to create a smart contract deployment transaction,
  • methods to create transactions to modify state in the deployed smart contract,
  • methods to create contract calls to execute static methods on the deployed smart contract, and
  • methods to query the state of the deployed smart contract.

Contracts can be added as packages which means they become reusable across AEA projects.

The smart contract wrapped in an AEA contract package might be a third-party smart contract or your own smart contract potentially interacting with a third-party contract on-chain.

"},{"location":"aea-framework-documentation/contract/#interacting-with-contracts-from-skills","title":"Interacting with Contracts from Skills","text":"

Interacting with contracts in almost all cases requires network access. Therefore, the framework executes contract related logic in a Connection.

In particular, the fetchai/ledger:0.21.5 connection can be used to execute contract related logic. The skills communicate with the fetchai/ledger:0.21.5 connection via the fetchai/contract_api:1.0.0 protocol. This protocol implements a request-response pattern to serve the four types of methods listed above:

  • the get_deploy_transaction message is used to request a deploy transaction for a specific contract. For instance, to request a deploy transaction for the deployment of the smart contract wrapped in the fetchai/erc1155:0.23.3 package, we send the following message to the fetchai/ledger:0.21.5:
contract_api_msg = ContractApiMessage(\nperformative=ContractApiMessage.Performative.GET_DEPLOY_TRANSACTION,\ndialogue_reference=contract_api_dialogues.new_self_initiated_dialogue_reference(),\nledger_id=strategy.ledger_id,\ncontract_id=\"fetchai/erc1155:0.23.3\",\ncallable=\"get_deploy_transaction\",\nkwargs=ContractApiMessage.Kwargs(\n{\"deployer_address\": self.context.agent_address}\n),\n)\n

Any additional arguments needed by the contract's constructor method should be added to kwargs.

This message will be handled by the fetchai/ledger:0.21.5 connection and then a raw_transaction message will be returned with the matching raw transaction. To send this transaction to the ledger for processing, we first sign the message with the decision maker and then send the signed transaction to the fetchai/ledger:0.21.5 connection using the fetchai/ledger_api:1.0.0 protocol. For details on how to implement the message handling, see the handlers in the erc1155_deploy skill.

CosmWasm based smart contract deployments

When using CosmWasm based smart contracts two types of deployment transactions exist. The first transaction stores the code on the chain. The second transaction initialises the code. This way, the same contract code can be initialised many times.

Both the store and init messages use the ContractApiMessage.Performative.GET_DEPLOY_TRANSACTION performative. The ledger API automatically detects the type of transactions based on the provided keyword arguments. In particular, an init transaction requires the keyword arguments code_id (integer), label (string), amount (integer) and init_msg (JSON).

For an example look at the fetchai/erc1155:0.23.3 package.

  • the get_raw_transaction message is used to request any transaction for a specific contract which changes state in the contract. For instance, to request a transaction for the creation of token in the deployed erc1155 smart contract wrapped in the fetchai/erc1155:0.23.3 package, we send the following message to the fetchai/ledger:0.21.5:
contract_api_msg = ContractApiMessage(\nperformative=ContractApiMessage.Performative.GET_RAW_TRANSACTION,\ndialogue_reference=contract_api_dialogues.new_self_initiated_dialogue_reference(),\nledger_id=strategy.ledger_id,\ncontract_id=\"fetchai/erc1155:0.23.3\",\ncontract_address=strategy.contract_address,\ncallable=\"get_create_batch_transaction\",\nkwargs=ContractApiMessage.Kwargs(\n{\n\"deployer_address\": self.context.agent_address,\n\"token_ids\": strategy.token_ids,\n}\n),\n)\n

This message will be handled by the fetchai/ledger:0.21.5 connection and then a raw_transaction message will be returned with the matching raw transaction. For this to be executed correctly, the fetchai/erc1155:0.23.3 contract package needs to implement the get_create_batch_transaction method with the specified key word arguments (see example in Deploy your own, below). Similar to the above, to send this transaction to the ledger for processing, we first sign the message with the decision maker and then send the signed transaction to the fetchai/ledger:0.21.5 connection using the fetchai/ledger_api:1.0.0 protocol.

  • the get_raw_message message is used to request any contract method call for a specific contract which does not change state in the contract. For instance, to request a call to get a hash from some input data in the deployed erc1155 smart contract wrapped in the fetchai/erc1155:0.23.3 package, we send the following message to the fetchai/ledger:0.21.5:
contract_api_msg = ContractApiMessage(\nperformative=ContractApiMessage.Performative.GET_RAW_MESSAGE,\ndialogue_reference=contract_api_dialogues.new_self_initiated_dialogue_reference(),\nledger_id=strategy.ledger_id,\ncontract_id=\"fetchai/erc1155:0.23.3\",\ncontract_address=strategy.contract_address,\ncallable=\"get_hash_single\",\nkwargs=ContractApiMessage.Kwargs(\n{\n\"from_address\": from_address,\n\"to_address\": to_address,\n\"token_id\": token_id,\n\"from_supply\": from_supply,\n\"to_supply\": to_supply,\n\"value\": value,\n\"trade_nonce\": trade_nonce,\n}\n),\n)\n

This message will be handled by the fetchai/ledger:0.21.5 connection and then a raw_message message will be returned with the matching raw message. For this to be executed correctly, the fetchai/erc1155:0.23.3 contract package needs to implement the get_hash_single method with the specified key word arguments. We can then send the raw message to the fetchai/ledger:0.21.5 connection using the fetchai/ledger_api:1.0.0 protocol. In this case, signing is not required.

  • the get_state message is used to request any contract method call to query state in the deployed contract. For instance, to request a call to get the balances in the deployed erc1155 smart contract wrapped in the fetchai/erc1155:0.23.3 package, we send the following message to the fetchai/ledger:0.21.5:
contract_api_msg = ContractApiMessage(\nperformative=ContractApiMessage.Performative.GET_STATE,\ndialogue_reference=contract_api_dialogues.new_self_initiated_dialogue_reference(),\nledger_id=strategy.ledger_id,\ncontract_id=\"fetchai/erc1155:0.23.3\",\ncontract_address=strategy.contract_address,\ncallable=\"get_balance\",\nkwargs=ContractApiMessage.Kwargs(\n{\"agent_address\": address, \"token_id\": token_id}\n),\n)\n

This message will be handled by the fetchai/ledger:0.21.5 connection and then a state message will be returned with the matching state. For this to be executed correctly, the fetchai/erc1155:0.23.3 contract package needs to implement the get_balance method with the specified key word arguments. We can then send the raw message to the fetchai/ledger:0.21.5 connection using the fetchai/ledger_api:1.0.0 protocol. In this case, signing is not required.

"},{"location":"aea-framework-documentation/contract/#developing-your-own","title":"Developing your own","text":"

The easiest way to get started developing your own contract is by using the scaffold command:

aea scaffold contract my_new_contract\n

This will scaffold a contract package called my_new_contract with three files:

  • __init__.py
  • contract.py, containing the scaffolded contract class
  • contract.yaml containing the scaffolded configuration file

Once your scaffold is in place, you can create a build folder in the package and copy the smart contract interface (e.g. bytes code and ABI) to it. Then, specify the path to the interfaces in the contract.yaml. For instance, if you use Ethereum, then you might specify the following:

contract_interface_paths:\nethereum: build/my_contract.json\n

where ethereum is the ledger id and my_contract.json is the file containing the byte code and ABI.

Finally, you will want to implement the part of the contract interface you need in contract.py:

from aea.contracts.base import Contract\nfrom aea.crypto.base import LedgerApi\nclass MyContract(Contract):\n\"\"\"The MyContract contract class which acts as a bridge between AEA framework and ERC1155 ABI.\"\"\"\n@classmethod\ndef get_create_batch_transaction(\ncls,\nledger_api: LedgerApi,\ncontract_address: str,\ndeployer_address: str,\ntoken_ids: List[int],\ndata: Optional[bytes] = b\"\",\ngas: int = 300000,\n) -> Dict[str, Any]:\n\"\"\"\n        Get the transaction to create a batch of tokens.\n        :param ledger_api: the ledger API\n        :param contract_address: the address of the contract\n        :param deployer_address: the address of the deployer\n        :param token_ids: the list of token ids for creation\n        :param data: the data to include in the transaction\n        :param gas: the gas to be used\n        :return: the transaction object\n        \"\"\"\n# create the transaction dict\nnonce = ledger_api.api.eth.getTransactionCount(deployer_address)\ninstance = cls.get_instance(ledger_api, contract_address)\ntx = instance.functions.createBatch(\ndeployer_address, token_ids\n).buildTransaction(\n{\n\"gas\": gas,\n\"gasPrice\": ledger_api.api.toWei(\"50\", \"gwei\"),\n\"nonce\": nonce,\n}\n)\ntx = cls._try_estimate_gas(ledger_api, tx)\nreturn tx\n

Above, we implement a method to create a transaction, in this case a transaction to create a batch of tokens. The method will be called by the framework, specifically the fetchai/ledger:0.21.5 connection once it receives a message (see bullet point 2 above). The method first gets the latest transaction nonce of the deployer_address, then constructs the contract instance, then uses the instance to build the transaction and finally updates the gas on the transaction.

It helps to look at existing contract packages, like fetchai/erc1155:0.23.3, and skills using them, like fetchai/erc1155_client:0.11.0 and fetchai/erc1155_deploy:0.31.6, for inspiration and guidance.

"},{"location":"aea-framework-documentation/core-components-1/","title":"Core Components - Part 1","text":"

The AEA framework consists of several core components, some required to run an AEA and others optional.

The following sections discuss the inner workings of the AEA framework and how it calls the code in custom packages (see inversion of control and a helpful comparison here). Whilst it is in principle possible to use parts of the framework as a library, we do not recommend it.

"},{"location":"aea-framework-documentation/core-components-1/#the-elements-each-aea-uses","title":"The Elements Each AEA Uses","text":""},{"location":"aea-framework-documentation/core-components-1/#envelope","title":"Envelope","text":"

AEA objects communicate asynchronously via Envelopes.

An Envelope is the core object with which agents communicate. It is a vehicle for Messages with five attributes:

  • to: defines the destination address.
  • sender: defines the sender address.
  • protocol_id: defines the id of the Protocol.
  • message: is a bytes field which holds the Message in serialized form.
  • Optional[context]: an optional field to specify routing information in a URI.

Messages must adhere to a Protocol.

"},{"location":"aea-framework-documentation/core-components-1/#protocol","title":"Protocol","text":"

Protocols define agent-to-agent as well as component-to-component interactions within AEAs. As such, they include:

  • Messages defining the syntax of messages;
  • Serialization defining how a Message is encoded for transport; and, optionally
  • Dialogues, which define rules over Message sequences.

The framework provides one default Protocol, called default (current version fetchai/default:1.1.7). This Protocol provides a bare-bones implementation for an AEA Protocol which includes a DefaultMessage class and associated DefaultSerializer and DefaultDialogue classes.

Additional Protocols, for new types of interactions, can be added as packages. For more details on Protocols you can read the protocol guide. To learn how you can easily automate protocol definition, head to the guide for the protocol generator.

Protocol specific Messages, wrapped in Envelopes, are sent and received to other agents, agent components and services via Connections.

"},{"location":"aea-framework-documentation/core-components-1/#connection","title":"Connection","text":"

A Connection wraps an SDK or API and provides an interface to networks, ledgers or other services. Where necessary, a Connection is responsible for translating between the framework specific Envelope with its contained Message and the external service or third-party protocol (e.g. HTTP).

The framework provides one default Connection, called stub (current version fetchai/stub:0.21.3). It implements an I/O reader and writer to send Messages to the agent from a local file.

Additional Connections can be added as packages. For more details on Connections read the Connection guide .

An AEA runs and manages Connections via a Multiplexer.

"},{"location":"aea-framework-documentation/core-components-1/#multiplexer","title":"Multiplexer","text":"

The Multiplexer is responsible for maintaining (potentially multiple) Connections.

It maintains an InBox and OutBox, which are, respectively, queues for incoming and outgoing Envelopes from the perspective of Skills.

"},{"location":"aea-framework-documentation/core-components-1/#skill","title":"Skill","text":"

Skills are the core focus of the framework's extensibility as they implement business logic to deliver economic value for the AEA. They are self-contained capabilities that AEAs can dynamically take on board, in order to expand their effectiveness in different situations.

A Skill encapsulates implementations of the three abstract base classes Handler, Behaviour, Model, and is closely related with the abstract base class Task:

  • Handler: each Skill has zero, one or more Handler objects. There is a one-to-one correspondence between Handlers and the protocols in an AEA (also known as the registered protocols). Handlers implement AEAs' reactive behaviour. If an AEA understands a Protocol referenced in a received Envelope (i.e. the protocol is registered in this AEA), this envelope is sent to the corresponding Handler which executes the AEA's reaction to this Message.
  • Behaviour: a skill can have zero, one or more Behaviours, each encapsulating actions which further the AEAs goal and are initiated by internals of the AEA rather than external events. Behaviours implement AEAs' pro-activeness. The framework provides a number of abstract base classes implementing different types of simple and composite behaviours (e.g. cyclic, one-shot, finite-state-machine, etc), and these define how often and in what order a behaviour and its sub-behaviours must be executed.
  • Model: zero, one or more Models that inherit from the Model abstract base class and are accessible via the SkillContext.
  • Task: zero, one or more Tasks encapsulate background work internal to the AEA. Task differs from the other three in that it is not a part of Skills, but Tasks are declared in or from Skills if a packaging approach for AEA creation is used.

A Skill can read (parts of) an AEA's state (as summarised in the AgentContext), and propose actions to the AEA according to its specific logic. As such, more than one Skill could exist per Protocol, competing with each other in suggesting to the AEA the best course of actions to take. In technical terms, this means Skills are horizontally arranged.

For instance, an AEA which is trading goods, could subscribe to more than one Skill, where each corresponds to a different trading strategy.

The framework places no limits on the complexity of Skills. They can implement simple (e.g. if-this-then-that) logic or be complex (e.g. a deep learning model or reinforcement learning agent).

The framework provides one default Skill, called error. Additional Skills can be added as packages. For more details on Skills head over to the Skill guide .

"},{"location":"aea-framework-documentation/core-components-1/#agent-loop","title":"Agent Loop","text":"

The AgentLoop performs a series of activities while the AEA state is not stopped.

  • it calls the act() function of all active registered Behaviours at their respective tick rate.
  • it grabs all Envelopes waiting in the InBox queue and calls the handle() function for the Handlers currently registered against the Protocol of the Envelope.
  • it dispatches the internal Messages from the decision maker (described below) to the handler in the relevant Skill.

The AgentLoop and Multiplexer are decoupled via the InBox and OutBox, and both are maintained by the Runtime.

"},{"location":"aea-framework-documentation/core-components-1/#next-steps","title":"Next Steps","text":""},{"location":"aea-framework-documentation/core-components-1/#recommended","title":"Recommended","text":"

We recommend you continue with the next step in the 'Getting Started' series:

  • AEA and web frameworks
"},{"location":"aea-framework-documentation/core-components-1/#relevant-deep-dives","title":"Relevant Deep-Dives","text":"

Most AEA development focuses on developing the Skills and Protocols necessary for an AEA to deliver against its economic objectives.

Understanding Protocols is core to developing your own agent. You can learn more about the Protocols agents use to communicate with each other and how they are created in the following section:

  • Protocols

Most of an AEA developer's time is spent on Skill development. Skills are the core business logic components of an AEA. Check out the following guide to learn more:

  • Skills

In most cases, one of the available Connection packages can be used. Occasionally, you might develop your own Connection:

  • Connections
"},{"location":"aea-framework-documentation/core-components-2/","title":"Core components - Part 2","text":"

The AEA framework consists of several core components, some required to run an AEA and others optional.

In Core Components - Part 1 we described the common components each AEA uses. In this page, we will look at more advanced components.

"},{"location":"aea-framework-documentation/core-components-2/#required-components-used-by-aeas","title":"Required Components Used by AEAs","text":""},{"location":"aea-framework-documentation/core-components-2/#decision-maker","title":"Decision Maker","text":"

The DecisionMaker can be thought of as a Wallet manager plus \"economic brain\" of the AEA. It is responsible for the AEA's crypto-economic security and goal management, and it contains the preference and ownership representation of the AEA. The decision maker is the only component with access to the Wallet's private keys.

You can learn more about the decision maker here. In its simplest form, the decision maker acts like a Wallet with Handler that reacts to the messages it receives from the skills.

"},{"location":"aea-framework-documentation/core-components-2/#wallet","title":"Wallet","text":"

The Wallet contains the private-public key pairs used by the AEA. Skills do not have access to the wallet, only the decision maker does.

The agent has two sets of private keys, as configured in the aea-config.yaml:

  • private_key_paths: This is a dictionary mapping identifiers to the file paths of private keys used in the AEA. For each identifier, e.g. fetchai, the AEA can have one private key. The private keys listed here are available in the Decision Maker and the associated public keys and addresses are available in all skills. The AEA uses these keys to sign transactions and messages. These keys usually hold the AEAs funds.
  • connection_private_key_paths: This is a dictionary mapping identifiers to the file paths of private keys used in connections. For each identifier, e.g. fetchai, the Multiplexer can have one private key. The private keys listed here are available in the connections. The connections use these keys to secure message transport, for instance.

It is the responsibility of the AEA's user to safeguard the keys used and ensure that keys are only used in a single AEA. Using the same key across different AEAs will lead to various failure modes.

Private keys can be encrypted at rest. The CLI commands used for interacting with the wallet allow specifying a password for encryption/decryption.

"},{"location":"aea-framework-documentation/core-components-2/#identity","title":"Identity","text":"

The Identity is an abstraction that represents the identity of an AEA in the Open Economic Framework, backed by public-key cryptography. It contains the AEA's addresses as well as its name.

The identity can be accessed in a Skill via the AgentContext.

"},{"location":"aea-framework-documentation/core-components-2/#optional-components-used-by-aeas","title":"Optional Components Used by AEAs","text":""},{"location":"aea-framework-documentation/core-components-2/#contracts","title":"Contracts","text":"

Contracts wrap smart contracts for third-party decentralized ledgers. In particular, they provide wrappers around the API or ABI of a smart contract. They expose an API to abstract implementation specifics of the ABI from the Skills.

Contracts usually contain the logic to create contract transactions and make contract calls.

Contracts can be added as packages. For more details on Contracts also read the Contract guide here.

"},{"location":"aea-framework-documentation/core-components-2/#putting-it-together","title":"Putting it Together","text":"

Taken together, the core components from this section and the first part provide the following simplified illustration of an AEA:

"},{"location":"aea-framework-documentation/core-components-2/#next-steps","title":"Next Steps","text":""},{"location":"aea-framework-documentation/core-components-2/#recommended","title":"Recommended","text":"

We recommend you continue with the next step in the 'Getting Started' series:

  • How AEAs talk to each other - Interaction protocols
"},{"location":"aea-framework-documentation/core-components-2/#relevant-deep-dives","title":"Relevant Deep-Dives","text":"

Understanding the decision maker is vital to developing a goal oriented and crypto-economically safe AEA. You can learn more about the DecisionMaker in the following section:

  • Decision Maker

Understanding Contracts is important when developing AEAs that make commitments or use smart contracts for other purposes. You can learn more about the Contracts agents use in the following section:

  • Contracts
"},{"location":"aea-framework-documentation/core-components/","title":"Core Components","text":"

AEAs can be made from various components, much like legos, and these components can be of differing types. Below are some of the more important types of components an agent can have.

"},{"location":"aea-framework-documentation/core-components/#skill","title":"Skill","text":"

A Skill is an isolated, self-contained, (and preferably atomic) functionality that AEAs can take on board to expand their capability. Skills contain the proactive and reactive behaviour that ultimately makes it possible for an AEA to deliver economic value to its owner.

A Skill encapsulates implementations of three base classes Handler, Behaviour, Model, and is closely related with Task:

  • Handler: Handlers implement AEAs' reactive behaviour. If an AEA understands a protocol referenced in a received Envelope, this envelope is sent to the corresponding handler which executes the AEA's reaction to this message.
  • Behaviour: Behaviours implement AEAs' proactiveness, encapsulating actions which further an AEA's goals, and are initiated by internals of the AEA rather than external events.
  • Model: Encapsulate arbitrary objects and is made available to all components of the skill.
  • Task: Tasks encapsulate background work internal to the AEA.

A skill can read (parts of) an AEA's state and propose actions to the AEA according to its specific logic. As such, more than one skill could exist per protocol, competing with each other in suggesting to the AEA the best course of actions to take.

For instance, an AEA which is trading goods, could subscribe to more than one skill, where each corresponds to a different trading strategy.

The framework places no limits on the complexity of Skills. They can implement simple (e.g. if-this-then-that) logic or be complex (e.g. a deep learning model or reinforcement learning agent).

The framework provides one default error skill. Additional Skills can be added as packages. For more details on skills, head over to the Skill guide .

"},{"location":"aea-framework-documentation/core-components/#protocol","title":"Protocol","text":"

A Protocol defines the structure and nature of an interaction that can happen between agents, or between components of an agent. You can think of a protocol as the language that two agents speak and a skill for this protocol as a particular way of speaking this language. From a game-theoretic viewpoint, a protocol defines the rules of a game and a skill for this protocol defines a particular strategy for playing this game.

Protocols define agent-to-agent as well as component-to-component interactions within AEAs. As such, they include:

  • Messages: defining the syntax of messages.
  • Serialization: defining how a message is encoded for transport.
  • Dialogues: defines rules over sequences of messages.

The framework provides one default protocol. This protocol provides a bare-bones implementation which includes a DefaultMessage class and associated DefaultSerializer and DefaultDialogue classes.

Additional protocols for new types of interactions, can be added as packages. For more details on protocols, you can read the protocol guide. To learn how you can easily automate protocol definition, head to the guide for the protocol generator.

Protocol specific messages, wrapped in Envelopes, are sent and received to other agents, agent components and services via Connections.

"},{"location":"aea-framework-documentation/core-components/#connection","title":"Connection","text":"

Connections act as interfaces between an agent and the outside world. As such, a connection allows the agent to communicate with some entity outside of it, for example, another agent, a traditional HTTP server, a database, a reinforcement learning training environment, a blockchain, etc.

Where necessary, a Connection is responsible for translating between the framework specific Envelope with its contained message and the external service or third-party protocol (e.g. HTTP).

The framework provides one default stub connection. It implements an I/O reader and writer to send messages to the agent from a local file.

Additional connections can be added as packages. For more details on Connections read the Connection guide.

"},{"location":"aea-framework-documentation/debug/","title":"Debugging","text":"

There are multiple ways in which to configure your AEA for debugging during development. We focus on the standard Python approach here.

"},{"location":"aea-framework-documentation/debug/#using-pdb-stdlib","title":"Using pdb stdlib","text":"

You can add a debugger anywhere in your code:

import pdb; pdb.set_trace()\n

Then simply run you AEA with the --skip-consistency-check mode:

aea -s run\n

For more guidance on how to use pdb check out the documentation.

"},{"location":"aea-framework-documentation/debug/#using-an-ide","title":"Using an IDE","text":"
  • For VSCode modify the launch.json to include the following information:
{\n\"version\": \"0.2.0\",\n\"configurations\": [\n{\n\"name\": \"aea run\",\n\"type\": \"python\",\n\"request\": \"launch\",\n\"program\": \"PATH_TO_VIRTUAL_ENV/bin/aea\",\n\"args\": [\"-v\",\"DEBUG\",\"--skip-consistency-check\",\"run\"],\n\"cwd\": \"CWD\",\n\"console\": \"integratedTerminal\"\n}\n]\n}\n

where PATH_TO_VIRTUAL_ENV should be replaced with the path to the virtual environment and CWD with the working directory for the agent to debug (where the aea-config.yaml file is).

"},{"location":"aea-framework-documentation/decision-maker-transaction/","title":"Create Decision-Maker Transaction","text":"

This guide can be considered as a part 2 of the the stand-alone transaction demo. The main difference is that now we are going to use the decision-maker to sign the transaction.

First, import the libraries and the set the constant values. (Get the packages directory from the AEA repository svn export https://github.com/fetchai/agents-aea.git/trunk/packages.)

import logging\nimport time\nfrom threading import Thread\nfrom typing import Optional, cast\nfrom aea_ledger_fetchai import FetchAICrypto\nfrom aea.aea_builder import AEABuilder\nfrom aea.configurations.base import PublicId, SkillConfig\nfrom aea.crypto.helpers import create_private_key\nfrom aea.crypto.ledger_apis import LedgerApis\nfrom aea.crypto.wallet import Wallet\nfrom aea.helpers.transaction.base import RawTransaction, Terms\nfrom aea.identity.base import Identity\nfrom aea.protocols.base import Address, Message\nfrom aea.protocols.dialogue.base import Dialogue\nfrom aea.skills.base import Handler, Model, Skill, SkillContext\nfrom packages.fetchai.protocols.signing.dialogues import SigningDialogue\nfrom packages.fetchai.protocols.signing.dialogues import (\nSigningDialogues as BaseSigningDialogues,\n)\nfrom packages.fetchai.protocols.signing.message import SigningMessage\nfrom tests.conftest import get_wealth_if_needed\nlogger = logging.getLogger(\"aea\")\nlogging.basicConfig(level=logging.INFO)\nFETCHAI_PRIVATE_KEY_FILE_1 = \"fetchai_private_key_1.txt\"\nFETCHAI_PRIVATE_KEY_FILE_2 = \"fetchai_private_key_2.txt\"\n
"},{"location":"aea-framework-documentation/decision-maker-transaction/#create-a-private-key-and-an-aea","title":"Create a Private Key and an AEA","text":"

To have access to the decision-maker, which is responsible for signing transactions, we need to create an AEA. We can create an AEA with the builder, providing it with a private key we generate first.

    # Create a private key\ncreate_private_key(\nFetchAICrypto.identifier, private_key_file=FETCHAI_PRIVATE_KEY_FILE_1\n)\n# Instantiate the builder and build the AEA\n# By default, the default protocol, error skill and stub connection are added\nbuilder = AEABuilder()\nbuilder.set_name(\"my_aea\")\nbuilder.add_private_key(FetchAICrypto.identifier, FETCHAI_PRIVATE_KEY_FILE_1)\n# Create our AEA\nmy_aea = builder.build()\n
"},{"location":"aea-framework-documentation/decision-maker-transaction/#add-a-simple-skill","title":"Add a Simple Skill","text":"

Add a simple skill with a signing handler and the signing dialogues.

    # add a simple skill with handler\nskill_context = SkillContext(my_aea.context)\nskill_config = SkillConfig(name=\"simple_skill\", author=\"fetchai\", version=\"0.1.0\")\nsigning_handler = SigningHandler(\nskill_context=skill_context, name=\"signing_handler\"\n)\nsigning_dialogues_model = SigningDialogues(\nskill_context=skill_context,\nname=\"signing_dialogues\",\nself_address=str(skill_config.public_id),\n)\nsimple_skill = Skill(\nskill_config,\nskill_context,\nhandlers={signing_handler.name: signing_handler},\nmodels={signing_dialogues_model.name: signing_dialogues_model},\n)\nmy_aea.resources.add_skill(simple_skill)\n
"},{"location":"aea-framework-documentation/decision-maker-transaction/#create-a-second-identity","title":"Create a Second Identity","text":"
    # create a second identity\ncreate_private_key(\nFetchAICrypto.identifier, private_key_file=FETCHAI_PRIVATE_KEY_FILE_2\n)\ncounterparty_wallet = Wallet({FetchAICrypto.identifier: FETCHAI_PRIVATE_KEY_FILE_2})\nget_wealth_if_needed(counterparty_wallet.addresses[\"fetchai\"])\ncounterparty_identity = Identity(\nname=\"counterparty_aea\",\naddresses=counterparty_wallet.addresses,\npublic_keys=counterparty_wallet.public_keys,\ndefault_address_key=FetchAICrypto.identifier,\n)\n
"},{"location":"aea-framework-documentation/decision-maker-transaction/#create-the-signing-message","title":"Create the Signing Message","text":"

Next, we are creating the signing message and sending it to the decision-maker.

    # create signing message for decision maker to sign\nterms = Terms(\nledger_id=FetchAICrypto.identifier,\nsender_address=my_aea.identity.address,\ncounterparty_address=counterparty_identity.address,\namount_by_currency_id={\"FET\": -1},\nquantities_by_good_id={\"some_service\": 1},\nnonce=\"some_nonce\",\nfee_by_currency_id={\"FET\": 0},\n)\nget_wealth_if_needed(terms.sender_address)\nsigning_dialogues = cast(SigningDialogues, skill_context.signing_dialogues)\nstub_transaction = LedgerApis.get_transfer_transaction(\nterms.ledger_id,\nterms.sender_address,\nterms.counterparty_address,\nterms.sender_payable_amount,\nterms.sender_fee,\nterms.nonce,\n)\nsigning_msg = SigningMessage(\nperformative=SigningMessage.Performative.SIGN_TRANSACTION,\ndialogue_reference=signing_dialogues.new_self_initiated_dialogue_reference(),\nraw_transaction=RawTransaction(FetchAICrypto.identifier, stub_transaction),\nterms=terms,\n)\nsigning_dialogue = cast(\nOptional[SigningDialogue],\nsigning_dialogues.create_with_message(\"decision_maker\", signing_msg),\n)\nassert signing_dialogue is not None\nmy_aea.context.decision_maker_message_queue.put_nowait(signing_msg)\n
"},{"location":"aea-framework-documentation/decision-maker-transaction/#run-the-agent","title":"Run the Agent","text":"

Finally, we are running the agent and expect the signed transaction to be printed in the terminal.

    # Set the AEA running in a different thread\ntry:\nlogger.info(\"STARTING AEA NOW!\")\nt = Thread(target=my_aea.start)\nt.start()\n# Let it run long enough to interact with the decision maker\ntime.sleep(1)\nfinally:\n# Shut down the AEA\nlogger.info(\"STOPPING AEA NOW!\")\nmy_aea.stop()\nt.join()\n

After the completion of the signing, we get the signed transaction.

"},{"location":"aea-framework-documentation/decision-maker-transaction/#more-details","title":"More Details","text":"

To be able to register a handler that reads the internal messages, we have to create a class at the end of the file which processes the signing messages.

class SigningDialogues(Model, BaseSigningDialogues):\n\"\"\"Signing dialogues model.\"\"\"\ndef __init__(self, self_address: Address, **kwargs) -> None:\n\"\"\"\n        Initialize dialogues.\n        :return: None\n        \"\"\"\nModel.__init__(self, **kwargs)\ndef role_from_first_message(  # pylint: disable=unused-argument\nmessage: Message, receiver_address: Address\n) -> Dialogue.Role:\n\"\"\"Infer the role of the agent from an incoming/outgoing first message\n            :param message: an incoming/outgoing first message\n            :param receiver_address: the address of the receiving agent\n            :return: The role of the agent\n            \"\"\"\nreturn SigningDialogue.Role.SKILL\nBaseSigningDialogues.__init__(\nself,\nself_address=self_address,\nrole_from_first_message=role_from_first_message,\n)\nclass SigningHandler(Handler):\n\"\"\"Implement the signing handler.\"\"\"\nSUPPORTED_PROTOCOL = SigningMessage.protocol_id  # type: Optional[PublicId]\ndef setup(self) -> None:\n\"\"\"Implement the setup for the handler.\"\"\"\ndef handle(self, message: Message) -> None:\n\"\"\"\n        Implement the reaction to a message.\n        :param message: the message\n        :return: None\n        \"\"\"\nsigning_msg = cast(SigningMessage, message)\n# recover dialogue\nsigning_dialogues = cast(SigningDialogues, self.context.signing_dialogues)\nsigning_dialogue = cast(\nOptional[SigningDialogue], signing_dialogues.update(signing_msg)\n)\nif signing_dialogue is None:\nself._handle_unidentified_dialogue(signing_msg)\nreturn\n# handle message\nif signing_msg.performative is SigningMessage.Performative.SIGNED_TRANSACTION:\nself._handle_signed_transaction(signing_msg, signing_dialogue)\nelif signing_msg.performative is SigningMessage.Performative.ERROR:\nself._handle_error(signing_msg, signing_dialogue)\nelse:\nself._handle_invalid(signing_msg, signing_dialogue)\ndef teardown(self) -> None:\n\"\"\"\n        Implement the handler teardown.\n        :return: None\n        \"\"\"\ndef _handle_unidentified_dialogue(self, signing_msg: SigningMessage) -> None:\n\"\"\"\n        Handle an unidentified dialogue.\n        :param msg: the message\n        \"\"\"\nself.context.logger.info(\n\"received invalid signing message={}, unidentified dialogue.\".format(\nsigning_msg\n)\n)\ndef _handle_signed_transaction(\nself, signing_msg: SigningMessage, signing_dialogue: SigningDialogue\n) -> None:\n\"\"\"\n        Handle a signing message.\n        :param signing_msg: the signing message\n        :param signing_dialogue: the dialogue\n        :return: None\n        \"\"\"\nself.context.logger.info(\"transaction signing was successful.\")\nlogger.info(signing_msg.signed_transaction)\ndef _handle_error(\nself, signing_msg: SigningMessage, signing_dialogue: SigningDialogue\n) -> None:\n\"\"\"\n        Handle an oef search message.\n        :param signing_msg: the signing message\n        :param signing_dialogue: the dialogue\n        :return: None\n        \"\"\"\nself.context.logger.info(\n\"transaction signing was not successful. Error_code={} in dialogue={}\".format(\nsigning_msg.error_code, signing_dialogue\n)\n)\ndef _handle_invalid(\nself, signing_msg: SigningMessage, signing_dialogue: SigningDialogue\n) -> None:\n\"\"\"\n        Handle an oef search message.\n        :param signing_msg: the signing message\n        :param signing_dialogue: the dialogue\n        :return: None\n        \"\"\"\nself.context.logger.warning(\n\"cannot handle signing message of performative={} in dialogue={}.\".format(\nsigning_msg.performative, signing_dialogue\n)\n)\n

You can find the full code for this example below:

Transaction via decision-maker full code:
import logging\nimport time\nfrom threading import Thread\nfrom typing import Optional, cast\nfrom aea_ledger_fetchai import FetchAICrypto\nfrom aea.aea_builder import AEABuilder\nfrom aea.configurations.base import PublicId, SkillConfig\nfrom aea.crypto.helpers import create_private_key\nfrom aea.crypto.ledger_apis import LedgerApis\nfrom aea.crypto.wallet import Wallet\nfrom aea.helpers.transaction.base import RawTransaction, Terms\nfrom aea.identity.base import Identity\nfrom aea.protocols.base import Address, Message\nfrom aea.protocols.dialogue.base import Dialogue\nfrom aea.skills.base import Handler, Model, Skill, SkillContext\nfrom packages.fetchai.protocols.signing.dialogues import SigningDialogue\nfrom packages.fetchai.protocols.signing.dialogues import (\nSigningDialogues as BaseSigningDialogues,\n)\nfrom packages.fetchai.protocols.signing.message import SigningMessage\nfrom tests.conftest import get_wealth_if_needed\nlogger = logging.getLogger(\"aea\")\nlogging.basicConfig(level=logging.INFO)\nFETCHAI_PRIVATE_KEY_FILE_1 = \"fetchai_private_key_1.txt\"\nFETCHAI_PRIVATE_KEY_FILE_2 = \"fetchai_private_key_2.txt\"\ndef run():\n\"\"\"Run demo.\"\"\"\n# Create a private key\ncreate_private_key(\nFetchAICrypto.identifier, private_key_file=FETCHAI_PRIVATE_KEY_FILE_1\n)\n# Instantiate the builder and build the AEA\n# By default, the default protocol, error skill and stub connection are added\nbuilder = AEABuilder()\nbuilder.set_name(\"my_aea\")\nbuilder.add_private_key(FetchAICrypto.identifier, FETCHAI_PRIVATE_KEY_FILE_1)\n# Create our AEA\nmy_aea = builder.build()\n# add a simple skill with handler\nskill_context = SkillContext(my_aea.context)\nskill_config = SkillConfig(name=\"simple_skill\", author=\"fetchai\", version=\"0.1.0\")\nsigning_handler = SigningHandler(\nskill_context=skill_context, name=\"signing_handler\"\n)\nsigning_dialogues_model = SigningDialogues(\nskill_context=skill_context,\nname=\"signing_dialogues\",\nself_address=str(skill_config.public_id),\n)\nsimple_skill = Skill(\nskill_config,\nskill_context,\nhandlers={signing_handler.name: signing_handler},\nmodels={signing_dialogues_model.name: signing_dialogues_model},\n)\nmy_aea.resources.add_skill(simple_skill)\n# create a second identity\ncreate_private_key(\nFetchAICrypto.identifier, private_key_file=FETCHAI_PRIVATE_KEY_FILE_2\n)\ncounterparty_wallet = Wallet({FetchAICrypto.identifier: FETCHAI_PRIVATE_KEY_FILE_2})\nget_wealth_if_needed(counterparty_wallet.addresses[\"fetchai\"])\ncounterparty_identity = Identity(\nname=\"counterparty_aea\",\naddresses=counterparty_wallet.addresses,\npublic_keys=counterparty_wallet.public_keys,\ndefault_address_key=FetchAICrypto.identifier,\n)\n# create signing message for decision maker to sign\nterms = Terms(\nledger_id=FetchAICrypto.identifier,\nsender_address=my_aea.identity.address,\ncounterparty_address=counterparty_identity.address,\namount_by_currency_id={\"FET\": -1},\nquantities_by_good_id={\"some_service\": 1},\nnonce=\"some_nonce\",\nfee_by_currency_id={\"FET\": 0},\n)\nget_wealth_if_needed(terms.sender_address)\nsigning_dialogues = cast(SigningDialogues, skill_context.signing_dialogues)\nstub_transaction = LedgerApis.get_transfer_transaction(\nterms.ledger_id,\nterms.sender_address,\nterms.counterparty_address,\nterms.sender_payable_amount,\nterms.sender_fee,\nterms.nonce,\n)\nsigning_msg = SigningMessage(\nperformative=SigningMessage.Performative.SIGN_TRANSACTION,\ndialogue_reference=signing_dialogues.new_self_initiated_dialogue_reference(),\nraw_transaction=RawTransaction(FetchAICrypto.identifier, stub_transaction),\nterms=terms,\n)\nsigning_dialogue = cast(\nOptional[SigningDialogue],\nsigning_dialogues.create_with_message(\"decision_maker\", signing_msg),\n)\nassert signing_dialogue is not None\nmy_aea.context.decision_maker_message_queue.put_nowait(signing_msg)\n# Set the AEA running in a different thread\ntry:\nlogger.info(\"STARTING AEA NOW!\")\nt = Thread(target=my_aea.start)\nt.start()\n# Let it run long enough to interact with the decision maker\ntime.sleep(1)\nfinally:\n# Shut down the AEA\nlogger.info(\"STOPPING AEA NOW!\")\nmy_aea.stop()\nt.join()\nclass SigningDialogues(Model, BaseSigningDialogues):\n\"\"\"Signing dialogues model.\"\"\"\ndef __init__(self, self_address: Address, **kwargs) -> None:\n\"\"\"\n        Initialize dialogues.\n        :return: None\n        \"\"\"\nModel.__init__(self, **kwargs)\ndef role_from_first_message(  # pylint: disable=unused-argument\nmessage: Message, receiver_address: Address\n) -> Dialogue.Role:\n\"\"\"Infer the role of the agent from an incoming/outgoing first message\n            :param message: an incoming/outgoing first message\n            :param receiver_address: the address of the receiving agent\n            :return: The role of the agent\n            \"\"\"\nreturn SigningDialogue.Role.SKILL\nBaseSigningDialogues.__init__(\nself,\nself_address=self_address,\nrole_from_first_message=role_from_first_message,\n)\nclass SigningHandler(Handler):\n\"\"\"Implement the signing handler.\"\"\"\nSUPPORTED_PROTOCOL = SigningMessage.protocol_id  # type: Optional[PublicId]\ndef setup(self) -> None:\n\"\"\"Implement the setup for the handler.\"\"\"\ndef handle(self, message: Message) -> None:\n\"\"\"\n        Implement the reaction to a message.\n        :param message: the message\n        :return: None\n        \"\"\"\nsigning_msg = cast(SigningMessage, message)\n# recover dialogue\nsigning_dialogues = cast(SigningDialogues, self.context.signing_dialogues)\nsigning_dialogue = cast(\nOptional[SigningDialogue], signing_dialogues.update(signing_msg)\n)\nif signing_dialogue is None:\nself._handle_unidentified_dialogue(signing_msg)\nreturn\n# handle message\nif signing_msg.performative is SigningMessage.Performative.SIGNED_TRANSACTION:\nself._handle_signed_transaction(signing_msg, signing_dialogue)\nelif signing_msg.performative is SigningMessage.Performative.ERROR:\nself._handle_error(signing_msg, signing_dialogue)\nelse:\nself._handle_invalid(signing_msg, signing_dialogue)\ndef teardown(self) -> None:\n\"\"\"\n        Implement the handler teardown.\n        :return: None\n        \"\"\"\ndef _handle_unidentified_dialogue(self, signing_msg: SigningMessage) -> None:\n\"\"\"\n        Handle an unidentified dialogue.\n        :param msg: the message\n        \"\"\"\nself.context.logger.info(\n\"received invalid signing message={}, unidentified dialogue.\".format(\nsigning_msg\n)\n)\ndef _handle_signed_transaction(\nself, signing_msg: SigningMessage, signing_dialogue: SigningDialogue\n) -> None:\n\"\"\"\n        Handle a signing message.\n        :param signing_msg: the signing message\n        :param signing_dialogue: the dialogue\n        :return: None\n        \"\"\"\nself.context.logger.info(\"transaction signing was successful.\")\nlogger.info(signing_msg.signed_transaction)\ndef _handle_error(\nself, signing_msg: SigningMessage, signing_dialogue: SigningDialogue\n) -> None:\n\"\"\"\n        Handle an oef search message.\n        :param signing_msg: the signing message\n        :param signing_dialogue: the dialogue\n        :return: None\n        \"\"\"\nself.context.logger.info(\n\"transaction signing was not successful. Error_code={} in dialogue={}\".format(\nsigning_msg.error_code, signing_dialogue\n)\n)\ndef _handle_invalid(\nself, signing_msg: SigningMessage, signing_dialogue: SigningDialogue\n) -> None:\n\"\"\"\n        Handle an oef search message.\n        :param signing_msg: the signing message\n        :param signing_dialogue: the dialogue\n        :return: None\n        \"\"\"\nself.context.logger.warning(\n\"cannot handle signing message of performative={} in dialogue={}.\".format(\nsigning_msg.performative, signing_dialogue\n)\n)\nif __name__ == \"__main__\":\nrun()\n
"},{"location":"aea-framework-documentation/decision-maker/","title":"Decision Maker","text":"

The DecisionMaker can be thought of like a wallet manager plus \"economic brain\" of the AEA. It is responsible for the AEA's crypto-economic security and goal management, and it contains the preference and ownership representation of the AEA. The decision maker is the only component which has access to the wallet's private keys.

"},{"location":"aea-framework-documentation/decision-maker/#interaction-with-skills","title":"Interaction with Skills","text":"

Skills communicate with the decision maker via Messages. At present, the decision maker processes messages of two protocols:

  • SigningMessage: it is used by skills to propose a transaction to the decision-maker for signing.

  • StateUpdateMessage: it is used to initialize the decision maker with preferences and ownership states. It can also be used to update the ownership states in the decision maker if the settlement of transaction takes place.

A message, say msg, is sent to the decision maker like so from any skill:

self.context.decision_maker_message_queue.put_nowait(msg)\n

The decision maker processes messages and can accept or reject them.

To process Messages from the decision maker in a given skill you need to create a Handler, in particular a SigningHandler like so:

class SigningHandler(Handler):\nprotocol_id = SigningMessage.protocol_id\ndef handle(self, message: Message):\n\"\"\"\n        Handle a signing message.\n        :param message: the signing message from the decision maker.\n        \"\"\"\n# code to handle the message\n
"},{"location":"aea-framework-documentation/decision-maker/#custom-decisionmaker","title":"Custom DecisionMaker","text":"

The framework implements a default DecisionMakerHandler and an advanced DecisionMakerHandler. You can also implement your own and mount it.

No further configuration is needed to use the default. To use the advanced decision maker handler, add the following configuration to the aea-config.yaml of your AEA (on page 1):

decision_maker_handler:\nconfig: {}\ndotted_path: \"aea.decision_maker.gop:DecisionMakerHandler\"\nfile_path: null\n

The easiest way to add a custom decision maker handler is to run the following command to scaffold a custom DecisionMakerHandler:

aea scaffold decision-maker-handler\n

You can then implement your own custom logic to process messages and interact with the Wallet.

Note

For examples how to use these concepts have a look at the tac_ skills. These functionalities are experimental and subject to change.

"},{"location":"aea-framework-documentation/defining-data-models/","title":"Defining Data Models","text":"

In this section, we explain how to define data models, an important component of the OEF Search & Discovery. It allows agents to describe themselves and to discover the services/resources they are interested in.

In a sentence, a DataModel is a set of attributes, and a Description of a service/resource is an assignment of those attributes.

All you need to specify data models and descriptions (that is, instances of the data model) can be found in the aea.helpers.search module.

"},{"location":"aea-framework-documentation/defining-data-models/#attributes","title":"Attributes","text":"

At the lowest level of our data model language, we have the Attribute. An attribute is an abstract definition of a property.

It is identified by a name, that must be unique in a given data model (that is, we can't have two attributes that share the same name).

Every attribute has a type, that specifies the domain of the property, that is, the possible values that the attribute can assume. At the moment, we support five types of attributes:

  • strings
  • integers
  • booleans
  • floats
  • locations, i.e. instances of Location (pairs of (latitude, longitude))

An attribute can be optional, in the sense that instantiation of the attribute is not mandatory by the instances of the data model.

Finally, every attribute might have a description that explains the purpose of the attribute.

Example: suppose we have a bookshop, and we want to describe the books we sell. Presumably, we would like to include: the following properties of our books:

  • The title
  • The author
  • The genre (e.g. science fiction, horror)
  • The year of publication
  • The average rating (average of the ratings between 0 and 5)
  • The ISBN code
  • If it can be sold as an e-book.

For each of these fields, we can define an attribute by using Attribute:

from aea.helpers.search.models import Attribute, Location\nattr_title   = Attribute(\"title\", str, True, \"The title of the book.\")\nattr_author  = Attribute(\"author\", str, True, \"The author of the book.\")\nattr_genre   = Attribute(\"genre\", str, True, \"The genre of the book.\")\nattr_year    = Attribute(\"year\", int, True, \"The year of publication of the book.\")\nattr_avg_rat = Attribute(\"average_rating\",  float,    False, \"The average rating of the book.\")\nattr_isbn    = Attribute(\"ISBN\", str, True, \"The ISBN.\")\nattr_ebook   = Attribute(\"ebook_available\", bool, False, \"If the book can be sold as an e-book.\")\nattr_bookshop = Attribute(\"bookshop_pos\", Location, False, \"The location of the bookshop where you can find the book\")\n

Let's focus on the parameters of the Attribute constructor:

  1. the first one is the name of the attribute. It is needed to instantiate a data model and to define queries over it.
  2. the second one is the type of the attribute. It specifies the domain of the possible values the attribute can assume. E.g. the attribute year can only be an integer, whereas the average_rating can only be a floating-point number. The supported types are: str, int, bool, float and Location.
  3. the third one is a boolean that specifies whether the attribute is always required or it can be omitted. For example, we might not be able to specify the ebook_available attribute, maybe because it's not applicable to some kind of books.
  4. the fourth parameter is the description, that is a short description of the purpose of the attribute.
"},{"location":"aea-framework-documentation/defining-data-models/#data-models","title":"Data Models","text":"

A data model is just a set of attributes. The class that implements the data model is DataModel.

Example: let's continue with the example of the bookshop. Once we've defined the attributes, we'd like to group them in the same structure. We can do it in the following way:

from aea.helpers.search.models import DataModel\nbook_model = DataModel(\"book\", [\nattr_title,\nattr_author,\nattr_genre,\nattr_year,\nattr_avg_rat,\nattr_isbn,\nattr_ebook,\nattr_bookshop\n], \"A data model to describe books.\")\n

A DataModel requires:

  1. a name (in the example the name is \"book\") used to refer to the data model.
  2. a list of attributes, that constitutes the abstract data model.
  3. an (optional) description about the purpose of the data model.
"},{"location":"aea-framework-documentation/defining-data-models/#description","title":"Description","text":"

A Description is just an instantiation of a data model. That is, we specify a value to every attribute belonging to the data model we are interested in.

The class that implements the description is Description.

Example: now we have all we need to create a little catalogue about our books:

from aea.helpers.search.models import Description\nIt = Description({\n\"title\" :           \"It\",\n\"author\":           \"Stephen King\",\n\"genre\":            \"horror\",\n\"year\":             1986,\n\"average_rating\":   4.5,\n\"ISBN\":             \"0-670-81302-8\",\n\"ebook_available\":  True,\n\"bookshop_pos\":     Location(52.2057092, 0.1183431)\n}, book_model)\n_1984 = Description({\n\"title\" :           \"1984\",\n\"author\":           \"George Orwell\",\n\"genre\":            \"novel\",\n\"year\":             1949,\n\"ISBN\":             \"978-0451524935\",\n\"ebook_available\":  False\n}, book_model)\n

We defined the descriptions for two books, namely It and _1984, that refers to a data model.

The attributes are instantiated with a dictionary that has:

  • as keys, the name of the attributes.
  • as values, the values associated with the attributes.

Notice that in the latter book we omitted the average_rating field. We are allowed to do that because of the average_rating attribute is not mandatory.

"},{"location":"aea-framework-documentation/demos/","title":"Demos","text":"

We provide demo guides for multiple use-cases, each one involving several AEAs interacting in a different scenario.

These demos serve to highlight the concept of AEAs as well as provide inspiration for developers. Demos should not be taken as production ready software, although every care is taken to fix bugs when reported.

Demos are alphabetically sorted, we recommend you start with the weather skills demo.

"},{"location":"aea-framework-documentation/deployment/","title":"Deployment","text":"

The easiest way to run an AEA is using your development environment.

If you would like to run an AEA from a browser you can use Google Colab. This gist can be opened in Colab and implements the quick start.

For deployment, we recommend you use Docker.

"},{"location":"aea-framework-documentation/deployment/#deployment-using-a-docker-image","title":"Deployment using a Docker Image","text":"

First, we fetch a directory containing a Dockerfile and some dependencies:

svn export https://github.com/fetchai/agents-aea/branches/main/deploy-image\ncd deploy-image\n

Then follow the README.md contained in the folder.

"},{"location":"aea-framework-documentation/deployment/#deployment-using-kubernetes","title":"Deployment using Kubernetes","text":"

For an example of how to use Kubernetes navigate to our TAC deployment example.

"},{"location":"aea-framework-documentation/design-principles/","title":"Design Principles","text":"

The AEA framework development is guided by the following 8 principles:

  • Accessibility: ease of use.
  • Modularity: encourages module creation, sharing and reuse.
  • Openness: easily extensible with third-party libraries.
  • Conciseness: conceptually simple.
  • Value-driven: drives immediate value.
  • Low entry barriers: leverages existing programming languages and web protocols.
  • Safety: safe for the user (economically speaking).
  • Goal-alignment: seamless facilitation of users' preferences and goals.
"},{"location":"aea-framework-documentation/development-setup/","title":"Development Setup","text":"

An AEA consists of packages . When developing, it helps to be able to save packages in a local package registry, rather than pushing them to remote registry. This guide helps you set up a local package registry and configure the working directory for development.

There are two ways to write code for an AEA:

  1. independent of a concrete AEA project, write individual packages

  2. from within an AEA project, write packages for that AEA

"},{"location":"aea-framework-documentation/development-setup/#approach-1","title":"Approach 1","text":"

To prepare a directory (henceforth working directory) for development with the AEA framework you can take a few steps:

  • Either, manually:
    • Ensure you start with an empty working directory to avoid any unnecessary side effects.
    • In your working directory, create an empty folder called packages. This folder will act as the local registry for packages.
    • In your working directory, create a .env file with the constant PYTHONPATH=$PYTHONPATH:path_to_packages_dir where path_to_packages_dir is the path to the packages folder in your working directory.
  • Or, automated:
    • Fork our template repo for AEA development. Then clone it to your machine.
  • Depending on your editor, you might take further steps:
    • VS Code: The Python Extension in VS Code can be configured to include additional paths in the Python path. The extension has a setting for python.envFile which specifies the path to a file containing environment variable definitions. The default is set to \"python.envFile\": \"${workspaceFolder}/.env\". Provide the path to the .env file in the above settings. In the .env file, add the PYTHONPATH constant defined above. Then close VS Code and re-open it for the settings to take effect.

After developing a package, you can add it to an AEA project in the working directory (e.g. aea create AGENT_NAME && cd AGENT_NAME && aea add --local PACKAGE_TYPE PUBLIC_ID will create a new AEA project AGENT_NAME and add the package of type PACKAGE_TYPE with public id PUBLIC_ID to it.)

"},{"location":"aea-framework-documentation/development-setup/#approach-2","title":"Approach 2","text":"

It is also possible to develop directly in an AEA project:

  • Prepare a directory (henceforth working directory) for development.
  • Create a new project aea create AGENT_NAME && cd AGENT_NAME
  • Scaffold a new package aea scaffold --with-symlinks PACKAGE_TYPE PACKAGE_NAME. This will create the package scaffold under the directory {PACKAGE_TYPE}s and create symlinks to ensure package import paths line up with the folder structure. The symlinks are not needed to run the AEA. They are purely for your IDE.
  • In your working directory, create a .env file with the constant PYTHONPATH=$PYTHONPATH:path_to_project_dir where path_to_project_dir is the path to the AEA project contained in your working directory.
  • Depending on your editor, you might take further steps:
    • VS Code: The Python Extension in VS Code can be configured to include additional paths in the Python path. The extension has a setting for python.envFile which specifies the path to a file containing environment variable definitions. The default is set to \"python.envFile\": \"${workspaceFolder}/.env\". Provide the path to the .env file in the above settings. In the .env file, add the PYTHONPATH constant defined above. Then close VS Code and re-open it for the settings to take effect.
"},{"location":"aea-framework-documentation/development-setup/#general-advice","title":"General Advice","text":"

This advice partially overlaps with the previous two sections:

  • When developing a specific AEA, it might be helpful to publish/push or fetch/add from local registry. From your working directory/AEA project, simply execute the usual AEA CLI commands. The CLI will first search in the packages directory, then in the remote AEA registry. You can explicitly point to local registry by providing flag --local or --remote to only point to remote registry (see here for more details on CLI commands).
  • When working on an AEA, it may help to provide a symbolic link to the packages directory, so that the import paths are detected by your editor. Simply create an empty file with touch packages in your AEA project, then create a symbolic link to the packages directory with ln -s ../packages packages.
  • Alternatively, it can help to provide symbolic links within an AEA to align import paths with folder structure. Simply create an empty file with touch packages in your AEA project, then create a symbolic link to ln -s vendor packages.
"},{"location":"aea-framework-documentation/diagram/","title":"Architectural Diagram","text":"

The framework has two distinctive parts.

  • A core that is developed by the Fetch.ai team as well as external contributors.
  • Extensions (also known as packages) developed by any developer.

Currently, the framework supports four types of packages which can be added to the core as modules:

  • Skills encapsulate logic that deliver economic value to the AEA. Skills are the main focus of the framework's extensibility.
  • Protocols define the structure of agent-to-agent and component-to-component interactions (messages and dialogues) for agents.
  • Connections provide interfaces for the agent to connect with the outside world. They wrap SDKs or APIs and provide interfaces to networks, ledgers and other services.
  • Contracts wrap smart contracts for Fetch.ai and third-party decentralized ledgers.

The following figure illustrates the framework's architecture:

The execution is broken down in more detail below:

The agent operation breaks down into three parts:

  • Setup: calls the setup() method of all registered resources
  • Operation:
    • Agent loop (Thread 1 - Asynchronous agent loop):
      • react(): this function grabs all Envelopes waiting in the InBox queue and calls the handle() method on the Handler(s) responsible for them.
      • act(): this function calls the act() method of all registered Behaviours.
      • update(): this function enqueues scheduled tasks for execution with the TaskManager and executes the decision maker.
    • Task loop (Thread 2- Synchronous): executes available tasks
    • Decision maker loop (Thread 3- Synchronous): processes internal messages
    • Multiplexer (Thread 4 - Asynchronous event loop): processes incoming and outgoing messages across several connections asynchronously.
  • Teardown: calls the teardown() method of all registered resources

To prevent a developer from blocking the main loop with custom skill code, an execution time limit is applied to every Behaviour.act and Handler.handle call.

By default, the execution limit is set to 0 seconds, which disables the feature. You can set the limit to a strictly positive value (e.g. 0.1 seconds) to test your AEA for production readiness. If the act or handle time exceed this limit, the call will be terminated.

An appropriate message is added to the logs in the case of some code execution being terminated.

"},{"location":"aea-framework-documentation/ecosystem/","title":"Agent Ecosystem","text":"

AEAs are situated within a larger ecosystem comprised of various other systems and technology layers.

"},{"location":"aea-framework-documentation/ecosystem/#agent-communication-network-acn","title":"Agent Communication Network (ACN)","text":"

ACN is a peer-to-peer communication network for agents. It allows AEAs to send and receive envelopes between each other.

The implementation builds on the open-source libp2p library. A distributed hash table is used by all participating peers to maintain a mapping between agents' cryptographic addresses and their network addresses.

Agents can receive messages from other agents if they are both connected to the ACN (see here for an example).

"},{"location":"aea-framework-documentation/ecosystem/#search-and-discovery","title":"Search and Discovery","text":"

An sOEF node allows agents to discover each other. In particular, agents can register themselves and the services they offer, and can search for agents who offer specific services.

For two agents to be able to find each other, at least one must register itself on the sOEF and the other must query the sOEF node for it. Detailed documentation is provided here.

"},{"location":"aea-framework-documentation/ecosystem/#ledgers","title":"Ledgers","text":"

Ledgers enable AEAs to store transactions, for example involving the transfer of funds to each other, or the execution of smart contracts. They optionally ensure the truth and integrity of agent to agent interactions.

Although a ledger can, in principle, be used to store structured data (e.g. training data in a machine learning model), in most cases the resulting costs and privacy implications do not make this sustainable. Instead, usually only references to structured data - often in the form of hashes - are stored on a ledger, and the actual data is stored off-chain.

The Python implementation of the AEA Framework currently integrates with three ledgers:

  • Fetch.ai ledger
  • Ethereum ledger
  • Cosmos ledger

Furthermore, the framework makes it straightforward for any developer to create a ledger plugin, adding support for another ledger.

"},{"location":"aea-framework-documentation/ecosystem/#aeas-as-second-layer-technology","title":"AEAs as Second Layer Technology","text":"

The following presentation discusses how AEAs can be seen as second layer technology to ledgers.

"},{"location":"aea-framework-documentation/erc1155-skills/","title":"Contract Deploy and Interact","text":"

The AEA erc1155_deploy and erc1155_client skills demonstrate an interaction between two AEAs which use a smart contract.

  • The erc1155_deploy skill deploys the smart contract, creates and mints items.
  • The erc1155_client skill signs a transaction to complete a trustless trade with its counterparty.
"},{"location":"aea-framework-documentation/erc1155-skills/#preparation-instructions","title":"Preparation Instructions","text":""},{"location":"aea-framework-documentation/erc1155-skills/#dependencies","title":"Dependencies","text":"

Follow the Preliminaries and Installation sections from the AEA quick start.

"},{"location":"aea-framework-documentation/erc1155-skills/#discussion","title":"Discussion","text":"

The scope of this guide is demonstrating how you can deploy a smart contract and interact with it using AEAs. In this specific demo, you create two AEAs. One deploys and creates tokens inside a smart contract. The other signs a transaction to complete an atomic swap. The smart contract used is ERC1155 with a one-step atomic swap functionality. This means the trade between the two AEAs can be trustless.

Note

This is only for demonstrative purposes since the AEA deploying the contract also has the ability to mint tokens. In reality, the transfer of tokens from the AEA signing the transaction is worthless.

"},{"location":"aea-framework-documentation/erc1155-skills/#demo","title":"Demo","text":""},{"location":"aea-framework-documentation/erc1155-skills/#create-the-deployer-aea","title":"Create the Deployer AEA","text":"

Fetch the AEA that will deploy the contract:

aea fetch fetchai/erc1155_deployer:0.34.5\ncd erc1155_deployer\naea install\naea build\n
Alternatively, create from scratch:

Create the AEA that will deploy the contract.

aea create erc1155_deployer\ncd erc1155_deployer\naea add connection fetchai/p2p_libp2p:0.27.5\naea add connection fetchai/soef:0.27.6\naea add connection fetchai/ledger:0.21.5\naea add skill fetchai/erc1155_deploy:0.31.6\naea config set --type dict agent.dependencies \\\n'{\n  \"aea-ledger-fetchai\": {\"version\": \"<2.0.0,>=1.0.0\"},\n  \"aea-ledger-ethereum\": {\"version\": \"<2.0.0,>=1.0.0\"},\n  \"aea-ledger-cosmos\": {\"version\": \"<2.0.0,>=1.0.0\"}\n}'\naea config set agent.default_connection fetchai/p2p_libp2p:0.27.5\naea config set --type dict agent.default_routing \\\n'{\n  \"fetchai/contract_api:1.1.7\": \"fetchai/ledger:0.21.5\",\n  \"fetchai/ledger_api:1.1.7\": \"fetchai/ledger:0.21.5\",\n  \"fetchai/oef_search:1.1.7\": \"fetchai/soef:0.27.6\"\n}'\naea config set --type list vendor.fetchai.connections.p2p_libp2p.cert_requests \\\n'[{\"identifier\": \"acn\", \"ledger_id\": \"ethereum\", \"not_after\": \"2023-01-01\", \"not_before\": \"2022-01-01\", \"public_key\": \"fetchai\", \"save_path\": \".certs/conn_cert.txt\"}]'\naea install\naea build\n

And change the default ledger:

aea config set agent.default_ledger ethereum\n

Create a private key for the deployer AEA and add it for Ethereum use:

aea generate-key ethereum\naea add-key ethereum ethereum_private_key.txt\n

Create a private key for the P2P connection:

aea generate-key fetchai fetchai_connection_private_key.txt\naea add-key fetchai fetchai_connection_private_key.txt --connection\n

Finally, certify the key for use by the connections that request that:

aea issue-certificates\n
"},{"location":"aea-framework-documentation/erc1155-skills/#create-the-client-aea","title":"Create the Client AEA","text":"

In another terminal, fetch the client AEA which will receive some tokens from the deployer.

aea fetch fetchai/erc1155_client:0.34.5\ncd erc1155_client\naea install\naea build\n
Alternatively, create from scratch:

Create the AEA that will get some tokens from the deployer.

aea create erc1155_client\ncd erc1155_client\naea add connection fetchai/p2p_libp2p:0.27.5\naea add connection fetchai/soef:0.27.6\naea add connection fetchai/ledger:0.21.5\naea add skill fetchai/erc1155_client:0.29.6\naea config set --type dict agent.dependencies \\\n'{\n  \"aea-ledger-fetchai\": {\"version\": \"<2.0.0,>=1.0.0\"},\n  \"aea-ledger-ethereum\": {\"version\": \"<2.0.0,>=1.0.0\"},\n  \"aea-ledger-cosmos\": {\"version\": \"<2.0.0,>=1.0.0\"}\n}'\naea config set agent.default_connection fetchai/p2p_libp2p:0.27.5\naea config set --type dict agent.default_routing \\\n'{\n  \"fetchai/contract_api:1.1.7\": \"fetchai/ledger:0.21.5\",\n  \"fetchai/ledger_api:1.1.7\": \"fetchai/ledger:0.21.5\",\n  \"fetchai/oef_search:1.1.7\": \"fetchai/soef:0.27.6\"\n}'\naea config set --type list vendor.fetchai.connections.p2p_libp2p.cert_requests \\\n'[{\"identifier\": \"acn\", \"ledger_id\": \"ethereum\", \"not_after\": \"2023-01-01\", \"not_before\": \"2022-01-01\", \"public_key\": \"fetchai\", \"save_path\": \".certs/conn_cert.txt\"}]'\naea install\naea build\n

And change the default ledger:

aea config set agent.default_ledger ethereum\n

Create a private key for the client AEA and add it for Ethereum use:

aea generate-key ethereum\naea add-key ethereum ethereum_private_key.txt\n

Create a private key for the P2P connection:

aea generate-key fetchai fetchai_connection_private_key.txt\naea add-key fetchai fetchai_connection_private_key.txt --connection\n

Finally, certify the key for use by the connections that request that:

aea issue-certificates\n
"},{"location":"aea-framework-documentation/erc1155-skills/#run-ganache","title":"Run Ganache","text":"

Execute the following command to run Ganache:

docker run -p 8545:8545 trufflesuite/ganache-cli:latest --verbose --gasPrice=0 --gasLimit=0x1fffffffffffff --account=\"$(cat erc1155_deployer/ethereum_private_key.txt),1000000000000000000000\" --account=\"$(cat erc1155_client/ethereum_private_key.txt),1000000000000000000000\"\n

Wait some time for the wealth creation to be mined on Ropsten.

Check your wealth:

aea get-wealth ethereum\n

You should get 1000000000000000000000.

Note

If no wealth appears after a while, then try funding the private key directly using a web faucet.

"},{"location":"aea-framework-documentation/erc1155-skills/#update-soef-configurations-for-both-aeas","title":"Update SOEF Configurations for both AEAs","text":"

Update the SOEF configuration in both AEA projects:

aea config set vendor.fetchai.connections.soef.config.chain_identifier ethereum\n
"},{"location":"aea-framework-documentation/erc1155-skills/#run-the-aeas","title":"Run the AEAs","text":"

First, run the deployer AEA:

aea run\n

Once you see a message of the form To join its network use multiaddr 'SOME_ADDRESS' take note of this address.

Alternatively, use aea get-multiaddress fetchai -c -i fetchai/p2p_libp2p:0.27.5 -u public_uri to retrieve the address. The output will be something like /dns4/127.0.0.1/tcp/9000/p2p/16Uiu2HAm2JPsUX1Su59YVDXJQizYkNSe8JCusqRpLeeTbvY76fE5.

This is the entry peer address for the local agent communication network created by the deployer.

This AEA then performs the following steps:

  • deploys the smart contract
  • creates a batch of items in the smart contract
  • mints a batch of items in the smart contract

At some point you should see the log output:

registering service on SOEF.\n

At this point, configure the client AEA to connect to the same local ACN created by the deployer by running the following command in the client's terminal, replacing SOME_ADDRESS with the value you noted above:

aea config set --type dict vendor.fetchai.connections.p2p_libp2p.config \\\n'{\n  \"delegate_uri\": \"127.0.0.1:11001\",\n  \"entry_peers\": [\"SOME_ADDRESS\"],\n  \"local_uri\": \"127.0.0.1:9001\",\n  \"log_file\": \"libp2p_node.log\",\n  \"public_uri\": \"127.0.0.1:9001\"\n}'\n

Then, run the client AEA:

aea run\n

You will see that after discovery, the two AEAs exchange information about the transaction and the client at the end signs and sends the signature to the deployer AEA to send it to the network.

Note

Transactions on Ropsten can take a significant amount of time! If you run the example a second time, and the previous transaction is still pending, it can lead to a failure. The warning message Cannot verify whether transaction improves utility. Assuming it does! can be ignored.

"},{"location":"aea-framework-documentation/erc1155-skills/#delete-the-aeas","title":"Delete the AEAs","text":"

When you're done, stop the agents (CTRL+C), go up a level and delete the AEAs.

cd ..\naea delete erc1155_deployer\naea delete erc1155_client\n
"},{"location":"aea-framework-documentation/erc1155-skills/#communication","title":"Communication","text":"

This diagram shows the communication between the various entities in this interaction:

    sequenceDiagram\n        participant Search\n        participant Erc1155_contract\n        participant Client_AEA\n        participant Deployer_AEA\n        participant Blockchain\n\n        activate Search\n        activate Erc1155_contract\n        activate Client_AEA\n        activate Deployer_AEA\n        activate Blockchain\n\n        Deployer_AEA->>Blockchain: deployes smart contract\n        Deployer_AEA->>ERC1155_contract: creates tokens\n        Deployer_AEA->>ERC1155_contract: mint tokens       \n        Deployer_AEA->>Search: register_service\n        Client_AEA->>Search: search\n        Search-->>Client_AEA: list_of_agents\n        Client_AEA->>Deployer_AEA: call_for_proposal\n        Deployer_AEA->>Client_AEA: inform_message\n        Client_AEA->>Deployer_AEA: signature\n        Deployer_AEA->>Blockchain: send_transaction\n        Client_AEA->>ERC1155_contract: asks_balance\n\n        deactivate Search\n        deactivate Erc1155_contract\n        deactivate Client_AEA\n        deactivate Deployer_AEA\n        deactivate Blockchain
"},{"location":"aea-framework-documentation/generic-skills-step-by-step/","title":"Trade between Two AEAs","text":"

This guide is a step-by-step introduction to building AEAs that advertise their static and dynamic data, find other AEAs with required data, negotiate terms of trade, and carry out trades via ledger transactions.

If you simply want to run the resulting AEAs go here.

"},{"location":"aea-framework-documentation/generic-skills-step-by-step/#dependencies-required","title":"Dependencies (Required)","text":"

Follow the Preliminaries and Installation sections from the AEA quick start.

"},{"location":"aea-framework-documentation/generic-skills-step-by-step/#reference-code-optional","title":"Reference Code (Optional)","text":"

This step-by-step guide goes through the creation of two AEAs which are already developed by Fetch.ai. You can get the finished AEAs, and compare your code against them, by following the next steps:

aea fetch fetchai/generic_seller:0.29.5\ncd generic_seller\naea eject skill fetchai/generic_seller:0.28.6\ncd ..\n
aea fetch fetchai/generic_buyer:0.30.5\ncd generic_buyer\naea eject skill fetchai/generic_buyer:0.27.6\ncd ..\n
"},{"location":"aea-framework-documentation/generic-skills-step-by-step/#simplification-step","title":"Simplification Step","text":"

To keep file paths consistent with the reference code, we suggest you initialize your local author as fetchai for the purpose of this demo only:

aea init --reset --author fetchai\n
"},{"location":"aea-framework-documentation/generic-skills-step-by-step/#generic-seller-aea","title":"Generic Seller AEA","text":""},{"location":"aea-framework-documentation/generic-skills-step-by-step/#step-1-create-the-aea","title":"Step 1: Create the AEA","text":"

Create a new AEA by typing the following command in the terminal:

aea create my_generic_seller\ncd my_generic_seller\naea install\n

Our newly created AEA is inside the current working directory. Let\u2019s create our new skill that will handle the sale of data. Type the following command:

aea scaffold skill generic_seller\n

The scaffold skill command creates the correct structure for a new skill inside our AEA project. You can locate the newly created skill under the \"skills\" folder (my_generic_seller/skills/generic_seller/), and it must contain the following files:

  • __init__.py
  • behaviours.py
  • handlers.py
  • my_model.py
  • skills.yaml
"},{"location":"aea-framework-documentation/generic-skills-step-by-step/#step-2-create-the-behaviour","title":"Step 2: Create the Behaviour","text":"

A Behaviour class contains the business logic specific to actions initiated by the AEA, rather than reactions to other events.

Open the behaviours.py file (my_generic_seller/skills/generic_seller/behaviours.py) and replace the stub code with the following:

from typing import Any, Optional, cast\nfrom aea.helpers.search.models import Description\nfrom aea.skills.behaviours import TickerBehaviour\nfrom packages.fetchai.connections.ledger.base import (\nCONNECTION_ID as LEDGER_CONNECTION_PUBLIC_ID,\n)\nfrom packages.fetchai.protocols.ledger_api.message import LedgerApiMessage\nfrom packages.fetchai.protocols.oef_search.message import OefSearchMessage\nfrom packages.fetchai.skills.generic_seller.dialogues import (\nLedgerApiDialogues,\nOefSearchDialogues,\n)\nfrom packages.fetchai.skills.generic_seller.strategy import GenericStrategy\nDEFAULT_SERVICES_INTERVAL = 60.0\nDEFAULT_MAX_SOEF_REGISTRATION_RETRIES = 5\nLEDGER_API_ADDRESS = str(LEDGER_CONNECTION_PUBLIC_ID)\nclass GenericServiceRegistrationBehaviour(TickerBehaviour):\n\"\"\"This class implements a behaviour.\"\"\"\ndef __init__(self, **kwargs: Any):\n\"\"\"Initialise the behaviour.\"\"\"\nservices_interval = kwargs.pop(\n\"services_interval\", DEFAULT_SERVICES_INTERVAL\n)  # type: int\nself._max_soef_registration_retries = kwargs.pop(\n\"max_soef_registration_retries\", DEFAULT_MAX_SOEF_REGISTRATION_RETRIES\n)  # type: int\nsuper().__init__(tick_interval=services_interval, **kwargs)\nself.failed_registration_msg = None  # type: Optional[OefSearchMessage]\nself._nb_retries = 0\ndef setup(self) -> None:\n\"\"\"Implement the setup.\"\"\"\nstrategy = cast(GenericStrategy, self.context.strategy)\nif strategy.is_ledger_tx:\nledger_api_dialogues = cast(\nLedgerApiDialogues, self.context.ledger_api_dialogues\n)\nledger_api_msg, _ = ledger_api_dialogues.create(\ncounterparty=LEDGER_API_ADDRESS,\nperformative=LedgerApiMessage.Performative.GET_BALANCE,\nledger_id=strategy.ledger_id,\naddress=cast(str, self.context.agent_addresses.get(strategy.ledger_id)),\n)\nself.context.outbox.put_message(message=ledger_api_msg)\nself._register_agent()\ndef act(self) -> None:\n\"\"\"Implement the act.\"\"\"\nself._retry_failed_registration()\ndef teardown(self) -> None:\n\"\"\"Implement the task teardown.\"\"\"\nself._unregister_service()\nself._unregister_agent()\ndef _retry_failed_registration(self) -> None:\n\"\"\"Retry a failed registration.\"\"\"\nif self.failed_registration_msg is not None:\nself._nb_retries += 1\nif self._nb_retries > self._max_soef_registration_retries:\nself.context.is_active = False\nreturn\noef_search_dialogues = cast(\nOefSearchDialogues, self.context.oef_search_dialogues\n)\noef_search_msg, _ = oef_search_dialogues.create(\ncounterparty=self.failed_registration_msg.to,\nperformative=self.failed_registration_msg.performative,\nservice_description=self.failed_registration_msg.service_description,\n)\nself.context.outbox.put_message(message=oef_search_msg)\nself.context.logger.info(\nf\"Retrying registration on SOEF. Retry {self._nb_retries} out of {self._max_soef_registration_retries}.\"\n)\nself.failed_registration_msg = None\ndef _register(self, description: Description, logger_msg: str) -> None:\n\"\"\"\n        Register something on the SOEF.\n        :param description: the description of what is being registered\n        :param logger_msg: the logger message to print after the registration\n        \"\"\"\noef_search_dialogues = cast(\nOefSearchDialogues, self.context.oef_search_dialogues\n)\noef_search_msg, _ = oef_search_dialogues.create(\ncounterparty=self.context.search_service_address,\nperformative=OefSearchMessage.Performative.REGISTER_SERVICE,\nservice_description=description,\n)\nself.context.outbox.put_message(message=oef_search_msg)\nself.context.logger.info(logger_msg)\ndef _register_agent(self) -> None:\n\"\"\"Register the agent's location.\"\"\"\nstrategy = cast(GenericStrategy, self.context.strategy)\ndescription = strategy.get_location_description()\nself._register(description, \"registering agent on SOEF.\")\ndef register_service(self) -> None:\n\"\"\"Register the agent's service.\"\"\"\nstrategy = cast(GenericStrategy, self.context.strategy)\ndescription = strategy.get_register_service_description()\nself._register(description, \"registering agent's service on the SOEF.\")\ndef register_genus(self) -> None:\n\"\"\"Register the agent's personality genus.\"\"\"\nstrategy = cast(GenericStrategy, self.context.strategy)\ndescription = strategy.get_register_personality_description()\nself._register(\ndescription, \"registering agent's personality genus on the SOEF.\"\n)\ndef register_classification(self) -> None:\n\"\"\"Register the agent's personality classification.\"\"\"\nstrategy = cast(GenericStrategy, self.context.strategy)\ndescription = strategy.get_register_classification_description()\nself._register(\ndescription, \"registering agent's personality classification on the SOEF.\"\n)\ndef _unregister_service(self) -> None:\n\"\"\"Unregister service from the SOEF.\"\"\"\nstrategy = cast(GenericStrategy, self.context.strategy)\ndescription = strategy.get_unregister_service_description()\noef_search_dialogues = cast(\nOefSearchDialogues, self.context.oef_search_dialogues\n)\noef_search_msg, _ = oef_search_dialogues.create(\ncounterparty=self.context.search_service_address,\nperformative=OefSearchMessage.Performative.UNREGISTER_SERVICE,\nservice_description=description,\n)\nself.context.outbox.put_message(message=oef_search_msg)\nself.context.logger.info(\"unregistering service from SOEF.\")\ndef _unregister_agent(self) -> None:\n\"\"\"Unregister agent from the SOEF.\"\"\"\nstrategy = cast(GenericStrategy, self.context.strategy)\ndescription = strategy.get_location_description()\noef_search_dialogues = cast(\nOefSearchDialogues, self.context.oef_search_dialogues\n)\noef_search_msg, _ = oef_search_dialogues.create(\ncounterparty=self.context.search_service_address,\nperformative=OefSearchMessage.Performative.UNREGISTER_SERVICE,\nservice_description=description,\n)\nself.context.outbox.put_message(message=oef_search_msg)\nself.context.logger.info(\"unregistering agent from SOEF.\")\n

This TickerBehaviour registers (see setup method) and de-registers (see teardown method) our AEA\u2019s service on the SOEF search node at regular tick intervals (here 60 seconds). By registering, the AEA becomes discoverable to other AEAs.

In setup, prior to registrations, we send a message to the ledger connection to check the account balance for the AEA's address on the configured ledger.

"},{"location":"aea-framework-documentation/generic-skills-step-by-step/#step-3-create-the-handler","title":"Step 3: Create the Handler","text":"

So far, we have tasked the AEA with sending register/unregister requests to the SOEF search node. However at present, the AEA has no way of handling the responses it receives from the search node, or in fact messages sent by any other AEA.

We have to specify the logic to negotiate with another AEA based on the strategy we want our AEA to follow. The following diagram illustrates the negotiation flow that we want this AEA to use, as well as interactions with a search node and the blockchain between a seller_AEA and a buyer_AEA.

    sequenceDiagram\n        participant Search\n        participant Buyer_AEA\n        participant Seller_AEA\n        participant Blockchain\n\n        activate Buyer_AEA\n        activate Search\n        activate Seller_AEA\n        activate Blockchain\n\n        Seller_AEA->>Search: register_service\n        Buyer_AEA->>Search: search\n        Search-->>Buyer_AEA: list_of_agents\n        Buyer_AEA->>Seller_AEA: call_for_proposal\n        Seller_AEA->>Buyer_AEA: propose\n        Buyer_AEA->>Seller_AEA: accept\n        Seller_AEA->>Buyer_AEA: match_accept\n        loop Once with LedgerConnection\n            Buyer_AEA->>Buyer_AEA: Get raw transaction from ledger api\n        end\n        loop Once with DecisionMaker\n            Buyer_AEA->>Buyer_AEA: Get signed transaction from decision maker\n        end\n        loop Once with LedgerConnection\n            Buyer_AEA->>Buyer_AEA: Send transaction and get digest from ledger api\n            Buyer_AEA->>Blockchain: transfer_funds\n        end\n        Buyer_AEA->>Seller_AEA: send_transaction_digest\n        Seller_AEA->>Blockchain: check_transaction_status\n        Seller_AEA->>Buyer_AEA: send_data\n\n        deactivate Buyer_AEA\n        deactivate Search\n        deactivate Seller_AEA\n        deactivate Blockchain

In our case, my_generic_seller is the Seller_AEA in the above figure.

Let us now implement a Handler to deal with incoming messages. Open the handlers.py file (my_generic_seller/skills/generic_seller/handlers.py) and replace the stub code with the following:

from typing import Optional, cast\nfrom aea.configurations.base import PublicId\nfrom aea.crypto.ledger_apis import LedgerApis\nfrom aea.helpers.transaction.base import TransactionDigest\nfrom aea.protocols.base import Message\nfrom aea.skills.base import Handler\nfrom packages.fetchai.connections.ledger.base import (\nCONNECTION_ID as LEDGER_CONNECTION_PUBLIC_ID,\n)\nfrom packages.fetchai.protocols.default.message import DefaultMessage\nfrom packages.fetchai.protocols.fipa.message import FipaMessage\nfrom packages.fetchai.protocols.ledger_api.message import LedgerApiMessage\nfrom packages.fetchai.protocols.oef_search.message import OefSearchMessage\nfrom packages.fetchai.skills.generic_seller.behaviours import (\nGenericServiceRegistrationBehaviour,\n)\nfrom packages.fetchai.skills.generic_seller.dialogues import (\nDefaultDialogues,\nFipaDialogue,\nFipaDialogues,\nLedgerApiDialogue,\nLedgerApiDialogues,\nOefSearchDialogue,\nOefSearchDialogues,\n)\nfrom packages.fetchai.skills.generic_seller.strategy import GenericStrategy\nLEDGER_API_ADDRESS = str(LEDGER_CONNECTION_PUBLIC_ID)\nclass GenericFipaHandler(Handler):\n\"\"\"This class implements a FIPA handler.\"\"\"\nSUPPORTED_PROTOCOL = FipaMessage.protocol_id  # type: Optional[PublicId]\ndef setup(self) -> None:\n\"\"\"Implement the setup for the handler.\"\"\"\ndef handle(self, message: Message) -> None:\n\"\"\"\n        Implement the reaction to a message.\n        :param message: the message\n        \"\"\"\nfipa_msg = cast(FipaMessage, message)\n# recover dialogue\nfipa_dialogues = cast(FipaDialogues, self.context.fipa_dialogues)\nfipa_dialogue = cast(FipaDialogue, fipa_dialogues.update(fipa_msg))\nif fipa_dialogue is None:\nself._handle_unidentified_dialogue(fipa_msg)\nreturn\n# handle message\nif fipa_msg.performative == FipaMessage.Performative.CFP:\nself._handle_cfp(fipa_msg, fipa_dialogue)\nelif fipa_msg.performative == FipaMessage.Performative.DECLINE:\nself._handle_decline(fipa_msg, fipa_dialogue, fipa_dialogues)\nelif fipa_msg.performative == FipaMessage.Performative.ACCEPT:\nself._handle_accept(fipa_msg, fipa_dialogue)\nelif fipa_msg.performative == FipaMessage.Performative.INFORM:\nself._handle_inform(fipa_msg, fipa_dialogue)\nelse:\nself._handle_invalid(fipa_msg, fipa_dialogue)\ndef teardown(self) -> None:\n\"\"\"Implement the handler teardown.\"\"\"\n

The code above contains the logic for handling FipaMessages received by the my_generic_seller AEA. We use FipaDialogues (more on this below) to keep track of the progress of the negotiation dialogue between the my_generic_seller AEA and the my_generic_buyer AEA.

In the above handle method, we first check if a received message belongs to an existing dialogue or if we have to create a new dialogue (the recover dialogue part). Once this is done, we break down the AEA's response to each type of negotiation message, as indicated by the message's performative (the handle message part). Therefore, we implement the AEA's response to each negotiation message type in a different handler function.

Below the unused teardown function, we continue by adding the following function:

    def _handle_unidentified_dialogue(self, fipa_msg: FipaMessage) -> None:\n\"\"\"\n        Handle an unidentified dialogue.\n        :param fipa_msg: the message\n        \"\"\"\nself.context.logger.info(\n\"received invalid fipa message={}, unidentified dialogue.\".format(fipa_msg)\n)\ndefault_dialogues = cast(DefaultDialogues, self.context.default_dialogues)\ndefault_msg, _ = default_dialogues.create(\ncounterparty=fipa_msg.sender,\nperformative=DefaultMessage.Performative.ERROR,\nerror_code=DefaultMessage.ErrorCode.INVALID_DIALOGUE,\nerror_msg=\"Invalid dialogue.\",\nerror_data={\"fipa_message\": fipa_msg.encode()},\n)\nself.context.outbox.put_message(message=default_msg)\n

The above code handles an unidentified dialogue by responding to the sender with a DefaultMessage containing the appropriate error information.

The next code block handles CFP (call-for-proposal) negotiation messages. Paste the following code below the _handle_unidentified_dialogue function:

    def _handle_cfp(self, fipa_msg: FipaMessage, fipa_dialogue: FipaDialogue) -> None:\n\"\"\"\n        Handle the CFP.\n        If the CFP matches the supplied services then send a PROPOSE, otherwise send a DECLINE.\n        :param fipa_msg: the message\n        :param fipa_dialogue: the dialogue object\n        \"\"\"\nself.context.logger.info(\n\"received CFP from sender={}\".format(fipa_msg.sender[-5:])\n)\nstrategy = cast(GenericStrategy, self.context.strategy)\nif strategy.is_matching_supply(fipa_msg.query):\nproposal, terms, data_for_sale = strategy.generate_proposal_terms_and_data(\nfipa_msg.query, fipa_msg.sender\n)\nfipa_dialogue.data_for_sale = data_for_sale\nfipa_dialogue.terms = terms\nself.context.logger.info(\n\"sending a PROPOSE with proposal={} to sender={}\".format(\nproposal.values, fipa_msg.sender[-5:]\n)\n)\nproposal_msg = fipa_dialogue.reply(\nperformative=FipaMessage.Performative.PROPOSE,\ntarget_message=fipa_msg,\nproposal=proposal,\n)\nself.context.outbox.put_message(message=proposal_msg)\nelse:\nself.context.logger.info(\n\"declined the CFP from sender={}\".format(fipa_msg.sender[-5:])\n)\ndecline_msg = fipa_dialogue.reply(\nperformative=FipaMessage.Performative.DECLINE,\ntarget_message=fipa_msg,\n)\nself.context.outbox.put_message(message=decline_msg)\n

The above code sends a PROPOSE message back to the buyer as a response to its CFP if the requested services match our seller agent's supplied services, otherwise it will respond with a DECLINE message.

The next code-block handles the decline message we receive from the buyer. Add the following code below the _handle_cfpfunction:

    def _handle_decline(\nself,\nfipa_msg: FipaMessage,\nfipa_dialogue: FipaDialogue,\nfipa_dialogues: FipaDialogues,\n) -> None:\n\"\"\"\n        Handle the DECLINE.\n        Close the dialogue.\n        :param fipa_msg: the message\n        :param fipa_dialogue: the dialogue object\n        :param fipa_dialogues: the dialogues object\n        \"\"\"\nself.context.logger.info(\n\"received DECLINE from sender={}\".format(fipa_msg.sender[-5:])\n)\nfipa_dialogues.dialogue_stats.add_dialogue_endstate(\nFipaDialogue.EndState.DECLINED_PROPOSE, fipa_dialogue.is_self_initiated\n)\n

If we receive a decline message from the buyer we close the dialogue and terminate this conversation with my_generic_buyer.

Alternatively, we might receive an ACCEPT message. In order to handle this option add the following code below the _handle_decline function:

    def _handle_accept(\nself, fipa_msg: FipaMessage, fipa_dialogue: FipaDialogue\n) -> None:\n\"\"\"\n        Handle the ACCEPT.\n        Respond with a MATCH_ACCEPT_W_INFORM which contains the address to send the funds to.\n        :param fipa_msg: the message\n        :param fipa_dialogue: the dialogue object\n        \"\"\"\nself.context.logger.info(\n\"received ACCEPT from sender={}\".format(fipa_msg.sender[-5:])\n)\ninfo = {\"address\": fipa_dialogue.terms.sender_address}\nmatch_accept_msg = fipa_dialogue.reply(\nperformative=FipaMessage.Performative.MATCH_ACCEPT_W_INFORM,\ntarget_message=fipa_msg,\ninfo=info,\n)\nself.context.logger.info(\n\"sending MATCH_ACCEPT_W_INFORM to sender={} with info={}\".format(\nfipa_msg.sender[-5:],\ninfo,\n)\n)\nself.context.outbox.put_message(message=match_accept_msg)\n

When my_generic_buyer accepts the Proposal we send it and sends an ACCEPT message, we have to respond with another message (MATCH_ACCEPT_W_INFORM) to match the acceptance of the terms of trade and to inform the buyer of the address we would like it to send the funds to.

Lastly, we must handle an INFORM message, which the buyer uses to inform us that it has indeed sent the funds to the provided address. Add the following code at the end of the file:

    def _handle_inform(\nself, fipa_msg: FipaMessage, fipa_dialogue: FipaDialogue\n) -> None:\n\"\"\"\n        Handle the INFORM.\n        If the INFORM message contains the transaction_digest then verify that it is settled, otherwise do nothing.\n        If the transaction is settled, send the data, otherwise do nothing.\n        :param fipa_msg: the message\n        :param fipa_dialogue: the dialogue object\n        \"\"\"\nself.context.logger.info(\n\"received INFORM from sender={}\".format(fipa_msg.sender[-5:])\n)\nstrategy = cast(GenericStrategy, self.context.strategy)\nif strategy.is_ledger_tx and \"transaction_digest\" in fipa_msg.info.keys():\nself.context.logger.info(\n\"checking whether transaction={} has been received ...\".format(\nfipa_msg.info[\"transaction_digest\"]\n)\n)\nledger_api_dialogues = cast(\nLedgerApiDialogues, self.context.ledger_api_dialogues\n)\nledger_api_msg, ledger_api_dialogue = ledger_api_dialogues.create(\ncounterparty=LEDGER_API_ADDRESS,\nperformative=LedgerApiMessage.Performative.GET_TRANSACTION_RECEIPT,\ntransaction_digest=TransactionDigest(\nfipa_dialogue.terms.ledger_id, fipa_msg.info[\"transaction_digest\"]\n),\n)\nledger_api_dialogue = cast(LedgerApiDialogue, ledger_api_dialogue)\nledger_api_dialogue.associated_fipa_dialogue = fipa_dialogue\nself.context.outbox.put_message(message=ledger_api_msg)\nelif strategy.is_ledger_tx:\nself.context.logger.warning(\n\"did not receive transaction digest from sender={}.\".format(\nfipa_msg.sender[-5:]\n)\n)\nelif not strategy.is_ledger_tx and \"Done\" in fipa_msg.info.keys():\ninform_msg = fipa_dialogue.reply(\nperformative=FipaMessage.Performative.INFORM,\ntarget_message=fipa_msg,\ninfo=fipa_dialogue.data_for_sale,\n)\nself.context.outbox.put_message(message=inform_msg)\nfipa_dialogues = cast(FipaDialogues, self.context.fipa_dialogues)\nfipa_dialogues.dialogue_stats.add_dialogue_endstate(\nFipaDialogue.EndState.SUCCESSFUL, fipa_dialogue.is_self_initiated\n)\nself.context.logger.info(\n\"transaction confirmed, sending data={} to buyer={}.\".format(\nfipa_dialogue.data_for_sale,\nfipa_msg.sender[-5:],\n)\n)\nelse:\nself.context.logger.warning(\n\"did not receive transaction confirmation from sender={}.\".format(\nfipa_msg.sender[-5:]\n)\n)\n

In the above code, we check the INFORM message. If it contains a transaction digest, then we verify that the transaction matches the proposal the buyer accepted. If the transaction is valid and we received the funds, then we send the data to the buyer. Otherwise, we do not send the data.

The remaining handlers are as follows:

    def _handle_invalid(\nself, fipa_msg: FipaMessage, fipa_dialogue: FipaDialogue\n) -> None:\n\"\"\"\n        Handle a fipa message of invalid performative.\n        :param fipa_msg: the message\n        :param fipa_dialogue: the dialogue object\n        \"\"\"\nself.context.logger.warning(\n\"cannot handle fipa message of performative={} in dialogue={}.\".format(\nfipa_msg.performative, fipa_dialogue\n)\n)\nclass GenericLedgerApiHandler(Handler):\n\"\"\"Implement the ledger handler.\"\"\"\nSUPPORTED_PROTOCOL = LedgerApiMessage.protocol_id  # type: Optional[PublicId]\ndef setup(self) -> None:\n\"\"\"Implement the setup for the handler.\"\"\"\ndef handle(self, message: Message) -> None:\n\"\"\"\n        Implement the reaction to a message.\n        :param message: the message\n        \"\"\"\nledger_api_msg = cast(LedgerApiMessage, message)\n# recover dialogue\nledger_api_dialogues = cast(\nLedgerApiDialogues, self.context.ledger_api_dialogues\n)\nledger_api_dialogue = cast(\nOptional[LedgerApiDialogue], ledger_api_dialogues.update(ledger_api_msg)\n)\nif ledger_api_dialogue is None:\nself._handle_unidentified_dialogue(ledger_api_msg)\nreturn\n# handle message\nif ledger_api_msg.performative is LedgerApiMessage.Performative.BALANCE:\nself._handle_balance(ledger_api_msg)\nelif (\nledger_api_msg.performative\nis LedgerApiMessage.Performative.TRANSACTION_RECEIPT\n):\nself._handle_transaction_receipt(ledger_api_msg, ledger_api_dialogue)\nelif ledger_api_msg.performative == LedgerApiMessage.Performative.ERROR:\nself._handle_error(ledger_api_msg, ledger_api_dialogue)\nelse:\nself._handle_invalid(ledger_api_msg, ledger_api_dialogue)\ndef teardown(self) -> None:\n\"\"\"Implement the handler teardown.\"\"\"\ndef _handle_unidentified_dialogue(self, ledger_api_msg: LedgerApiMessage) -> None:\n\"\"\"\n        Handle an unidentified dialogue.\n        :param ledger_api_msg: the message\n        \"\"\"\nself.context.logger.info(\n\"received invalid ledger_api message={}, unidentified dialogue.\".format(\nledger_api_msg\n)\n)\ndef _handle_balance(self, ledger_api_msg: LedgerApiMessage) -> None:\n\"\"\"\n        Handle a message of balance performative.\n        :param ledger_api_msg: the ledger api message\n        \"\"\"\nself.context.logger.info(\n\"starting balance on {} ledger={}.\".format(\nledger_api_msg.ledger_id,\nledger_api_msg.balance,\n)\n)\ndef _handle_transaction_receipt(\nself, ledger_api_msg: LedgerApiMessage, ledger_api_dialogue: LedgerApiDialogue\n) -> None:\n\"\"\"\n        Handle a message of balance performative.\n        :param ledger_api_msg: the ledger api message\n        :param ledger_api_dialogue: the ledger api dialogue\n        \"\"\"\nfipa_dialogue = ledger_api_dialogue.associated_fipa_dialogue\nis_settled = LedgerApis.is_transaction_settled(\nfipa_dialogue.terms.ledger_id, ledger_api_msg.transaction_receipt.receipt\n)\nis_valid = LedgerApis.is_transaction_valid(\nfipa_dialogue.terms.ledger_id,\nledger_api_msg.transaction_receipt.transaction,\nfipa_dialogue.terms.sender_address,\nfipa_dialogue.terms.counterparty_address,\nfipa_dialogue.terms.nonce,\nfipa_dialogue.terms.counterparty_payable_amount,\n)\nif is_settled and is_valid:\nlast_message = cast(\nOptional[FipaMessage], fipa_dialogue.last_incoming_message\n)\nif last_message is None:\nraise ValueError(\"Cannot retrieve last fipa message.\")\ninform_msg = fipa_dialogue.reply(\nperformative=FipaMessage.Performative.INFORM,\ntarget_message=last_message,\ninfo=fipa_dialogue.data_for_sale,\n)\nself.context.outbox.put_message(message=inform_msg)\nfipa_dialogues = cast(FipaDialogues, self.context.fipa_dialogues)\nfipa_dialogues.dialogue_stats.add_dialogue_endstate(\nFipaDialogue.EndState.SUCCESSFUL, fipa_dialogue.is_self_initiated\n)\nself.context.logger.info(\n\"transaction confirmed, sending data={} to buyer={}.\".format(\nfipa_dialogue.data_for_sale,\nlast_message.sender[-5:],\n)\n)\nelse:\nself.context.logger.info(\n\"transaction_receipt={} not settled or not valid, aborting\".format(\nledger_api_msg.transaction_receipt\n)\n)\ndef _handle_error(\nself, ledger_api_msg: LedgerApiMessage, ledger_api_dialogue: LedgerApiDialogue\n) -> None:\n\"\"\"\n        Handle a message of error performative.\n        :param ledger_api_msg: the ledger api message\n        :param ledger_api_dialogue: the ledger api dialogue\n        \"\"\"\nself.context.logger.info(\n\"received ledger_api error message={} in dialogue={}.\".format(\nledger_api_msg, ledger_api_dialogue\n)\n)\ndef _handle_invalid(\nself, ledger_api_msg: LedgerApiMessage, ledger_api_dialogue: LedgerApiDialogue\n) -> None:\n\"\"\"\n        Handle a message of invalid performative.\n        :param ledger_api_msg: the ledger api message\n        :param ledger_api_dialogue: the ledger api dialogue\n        \"\"\"\nself.context.logger.warning(\n\"cannot handle ledger_api message of performative={} in dialogue={}.\".format(\nledger_api_msg.performative,\nledger_api_dialogue,\n)\n)\nclass GenericOefSearchHandler(Handler):\n\"\"\"This class implements an OEF search handler.\"\"\"\nSUPPORTED_PROTOCOL = OefSearchMessage.protocol_id  # type: Optional[PublicId]\ndef setup(self) -> None:\n\"\"\"Call to setup the handler.\"\"\"\ndef handle(self, message: Message) -> None:\n\"\"\"\n        Implement the reaction to a message.\n        :param message: the message\n        \"\"\"\noef_search_msg = cast(OefSearchMessage, message)\n# recover dialogue\noef_search_dialogues = cast(\nOefSearchDialogues, self.context.oef_search_dialogues\n)\noef_search_dialogue = cast(\nOptional[OefSearchDialogue], oef_search_dialogues.update(oef_search_msg)\n)\nif oef_search_dialogue is None:\nself._handle_unidentified_dialogue(oef_search_msg)\nreturn\n# handle message\nif oef_search_msg.performative == OefSearchMessage.Performative.SUCCESS:\nself._handle_success(oef_search_msg, oef_search_dialogue)\nelif oef_search_msg.performative == OefSearchMessage.Performative.OEF_ERROR:\nself._handle_error(oef_search_msg, oef_search_dialogue)\nelse:\nself._handle_invalid(oef_search_msg, oef_search_dialogue)\ndef teardown(self) -> None:\n\"\"\"Implement the handler teardown.\"\"\"\ndef _handle_unidentified_dialogue(self, oef_search_msg: OefSearchMessage) -> None:\n\"\"\"\n        Handle an unidentified dialogue.\n        :param oef_search_msg: the message\n        \"\"\"\nself.context.logger.info(\n\"received invalid oef_search message={}, unidentified dialogue.\".format(\noef_search_msg\n)\n)\ndef _handle_success(\nself,\noef_search_success_msg: OefSearchMessage,\noef_search_dialogue: OefSearchDialogue,\n) -> None:\n\"\"\"\n        Handle an oef search message.\n        :param oef_search_success_msg: the oef search message\n        :param oef_search_dialogue: the dialogue\n        \"\"\"\nself.context.logger.info(\n\"received oef_search success message={} in dialogue={}.\".format(\noef_search_success_msg, oef_search_dialogue\n)\n)\ntarget_message = cast(\nOefSearchMessage,\noef_search_dialogue.get_message_by_id(oef_search_success_msg.target),\n)\nif (\ntarget_message.performative\n== OefSearchMessage.Performative.REGISTER_SERVICE\n):\ndescription = target_message.service_description\ndata_model_name = description.data_model.name\nregistration_behaviour = cast(\nGenericServiceRegistrationBehaviour,\nself.context.behaviours.service_registration,\n)\nif \"location_agent\" in data_model_name:\nregistration_behaviour.register_service()\nelif \"set_service_key\" in data_model_name:\nregistration_behaviour.register_genus()\nelif (\n\"personality_agent\" in data_model_name\nand description.values[\"piece\"] == \"genus\"\n):\nregistration_behaviour.register_classification()\nelif (\n\"personality_agent\" in data_model_name\nand description.values[\"piece\"] == \"classification\"\n):\nself.context.logger.info(\n\"the agent, with its genus and classification, and its service are successfully registered on the SOEF.\"\n)\nelse:\nself.context.logger.warning(\nf\"received soef SUCCESS message as a reply to the following unexpected message: {target_message}\"\n)\ndef _handle_error(\nself,\noef_search_error_msg: OefSearchMessage,\noef_search_dialogue: OefSearchDialogue,\n) -> None:\n\"\"\"\n        Handle an oef search message.\n        :param oef_search_error_msg: the oef search message\n        :param oef_search_dialogue: the dialogue\n        \"\"\"\nself.context.logger.info(\n\"received oef_search error message={} in dialogue={}.\".format(\noef_search_error_msg, oef_search_dialogue\n)\n)\ntarget_message = cast(\nOefSearchMessage,\noef_search_dialogue.get_message_by_id(oef_search_error_msg.target),\n)\nif (\ntarget_message.performative\n== OefSearchMessage.Performative.REGISTER_SERVICE\n):\nregistration_behaviour = cast(\nGenericServiceRegistrationBehaviour,\nself.context.behaviours.service_registration,\n)\nregistration_behaviour.failed_registration_msg = target_message\ndef _handle_invalid(\nself, oef_search_msg: OefSearchMessage, oef_search_dialogue: OefSearchDialogue\n) -> None:\n\"\"\"\n        Handle an oef search message.\n        :param oef_search_msg: the oef search message\n        :param oef_search_dialogue: the dialogue\n        \"\"\"\nself.context.logger.warning(\n\"cannot handle oef_search message of performative={} in dialogue={}.\".format(\noef_search_msg.performative,\noef_search_dialogue,\n)\n)\n

The GenericLedgerApiHandler deals with LedgerApiMessages from the ledger connection and the GenericOefSearchHandler handles OefSearchMessages from the SOEF connection.

"},{"location":"aea-framework-documentation/generic-skills-step-by-step/#step-4-create-the-strategy","title":"Step 4: Create the Strategy","text":"

Next, we are going to create the strategy that we want our my_generic_seller AEA to follow. Rename the my_model.py file (my_generic_seller/skills/generic_seller/my_model.py) to strategy.py and replace the stub code with the following:

import uuid\nfrom typing import Any, Dict, Optional, Tuple\nfrom aea.common import Address\nfrom aea.crypto.ledger_apis import LedgerApis\nfrom aea.exceptions import enforce\nfrom aea.helpers.search.generic import (\nAGENT_LOCATION_MODEL,\nAGENT_PERSONALITY_MODEL,\nAGENT_REMOVE_SERVICE_MODEL,\nAGENT_SET_SERVICE_MODEL,\nSIMPLE_SERVICE_MODEL,\n)\nfrom aea.helpers.search.models import Description, Location, Query\nfrom aea.helpers.transaction.base import Terms\nfrom aea.skills.base import Model\nDEFAULT_IS_LEDGER_TX = True\nDEFAULT_UNIT_PRICE = 4\nDEFAULT_SERVICE_ID = \"generic_service\"\nDEFAULT_LOCATION = {\"longitude\": 0.1270, \"latitude\": 51.5194}\nDEFAULT_SERVICE_DATA = {\"key\": \"seller_service\", \"value\": \"generic_service\"}\nDEFAULT_PERSONALITY_DATA = {\"piece\": \"genus\", \"value\": \"data\"}\nDEFAULT_CLASSIFICATION = {\"piece\": \"classification\", \"value\": \"seller\"}\nDEFAULT_HAS_DATA_SOURCE = False\nDEFAULT_DATA_FOR_SALE = {\n\"some_generic_data_key\": \"some_generic_data_value\"\n}  # type: Optional[Dict[str, Any]]\nclass GenericStrategy(Model):\n\"\"\"This class defines a strategy for the agent.\"\"\"\ndef __init__(self, **kwargs: Any) -> None:\n\"\"\"\n        Initialize the strategy of the agent.\n        :param kwargs: keyword arguments\n        \"\"\"\nledger_id = kwargs.pop(\"ledger_id\", None)\ncurrency_id = kwargs.pop(\"currency_id\", None)\nself._is_ledger_tx = kwargs.pop(\"is_ledger_tx\", DEFAULT_IS_LEDGER_TX)\nself._unit_price = kwargs.pop(\"unit_price\", DEFAULT_UNIT_PRICE)\nself._service_id = kwargs.pop(\"service_id\", DEFAULT_SERVICE_ID)\nlocation = kwargs.pop(\"location\", DEFAULT_LOCATION)\nself._agent_location = {\n\"location\": Location(\nlatitude=location[\"latitude\"], longitude=location[\"longitude\"]\n)\n}\nself._set_personality_data = kwargs.pop(\n\"personality_data\", DEFAULT_PERSONALITY_DATA\n)\nenforce(\nlen(self._set_personality_data) == 2\nand \"piece\" in self._set_personality_data\nand \"value\" in self._set_personality_data,\n\"personality_data must contain keys `key` and `value`\",\n)\nself._set_classification = kwargs.pop(\"classification\", DEFAULT_CLASSIFICATION)\nenforce(\nlen(self._set_classification) == 2\nand \"piece\" in self._set_classification\nand \"value\" in self._set_classification,\n\"classification must contain keys `key` and `value`\",\n)\nself._set_service_data = kwargs.pop(\"service_data\", DEFAULT_SERVICE_DATA)\nenforce(\nlen(self._set_service_data) == 2\nand \"key\" in self._set_service_data\nand \"value\" in self._set_service_data,\n\"service_data must contain keys `key` and `value`\",\n)\nself._remove_service_data = {\"key\": self._set_service_data[\"key\"]}\nself._simple_service_data = {\nself._set_service_data[\"key\"]: self._set_service_data[\"value\"]\n}\nself._has_data_source = kwargs.pop(\"has_data_source\", DEFAULT_HAS_DATA_SOURCE)\ndata_for_sale_ordered = kwargs.pop(\"data_for_sale\", DEFAULT_DATA_FOR_SALE)\ndata_for_sale = {\nstr(key): str(value) for key, value in data_for_sale_ordered.items()\n}\nsuper().__init__(**kwargs)\nself._ledger_id = (\nledger_id if ledger_id is not None else self.context.default_ledger_id\n)\nif currency_id is None:\ncurrency_id = self.context.currency_denominations.get(self._ledger_id, None)\nenforce(\ncurrency_id is not None,\nf\"Currency denomination for ledger_id={self._ledger_id} not specified.\",\n)\nself._currency_id = currency_id\nenforce(\nself.context.agent_addresses.get(self._ledger_id, None) is not None,\n\"Wallet does not contain cryptos for provided ledger id.\",\n)\nself._data_for_sale = data_for_sale\n

In the above code snippet, we initialise the strategy class by trying to read the variables specific to the strategy from a YAML configuration file. If any variable is not provided, some default values will be used.

The following properties and methods deal with different aspects of the strategy. They should be relatively self-descriptive. Add them under the initialization of the strategy class:

    @property\ndef data_for_sale(self) -> Dict[str, str]:\n\"\"\"Get the data for sale.\"\"\"\nif self._has_data_source:\nreturn self.collect_from_data_source()  # pragma: nocover\nreturn self._data_for_sale\n@property\ndef ledger_id(self) -> str:\n\"\"\"Get the ledger id.\"\"\"\nreturn self._ledger_id\n@property\ndef is_ledger_tx(self) -> bool:\n\"\"\"Check whether or not tx are settled on a ledger.\"\"\"\nreturn self._is_ledger_tx\ndef get_location_description(self) -> Description:\n\"\"\"\n        Get the location description.\n        :return: a description of the agent's location\n        \"\"\"\ndescription = Description(\nself._agent_location,\ndata_model=AGENT_LOCATION_MODEL,\n)\nreturn description\ndef get_register_service_description(self) -> Description:\n\"\"\"\n        Get the register service description.\n        :return: a description of the offered services\n        \"\"\"\ndescription = Description(\nself._set_service_data,\ndata_model=AGENT_SET_SERVICE_MODEL,\n)\nreturn description\ndef get_register_personality_description(self) -> Description:\n\"\"\"\n        Get the register personality description.\n        :return: a description of the personality\n        \"\"\"\ndescription = Description(\nself._set_personality_data,\ndata_model=AGENT_PERSONALITY_MODEL,\n)\nreturn description\ndef get_register_classification_description(self) -> Description:\n\"\"\"\n        Get the register classification description.\n        :return: a description of the classification\n        \"\"\"\ndescription = Description(\nself._set_classification,\ndata_model=AGENT_PERSONALITY_MODEL,\n)\nreturn description\ndef get_service_description(self) -> Description:\n\"\"\"\n        Get the simple service description.\n        :return: a description of the offered services\n        \"\"\"\ndescription = Description(\nself._simple_service_data,\ndata_model=SIMPLE_SERVICE_MODEL,\n)\nreturn description\ndef get_unregister_service_description(self) -> Description:\n\"\"\"\n        Get the unregister service description.\n        :return: a description of the to be removed service\n        \"\"\"\ndescription = Description(\nself._remove_service_data,\ndata_model=AGENT_REMOVE_SERVICE_MODEL,\n)\nreturn description\ndef is_matching_supply(self, query: Query) -> bool:\n\"\"\"\n        Check if the query matches the supply.\n        :param query: the query\n        :return: bool indicating whether matches or not\n        \"\"\"\nreturn query.check(self.get_service_description())\ndef generate_proposal_terms_and_data(  # pylint: disable=unused-argument\nself, query: Query, counterparty_address: Address\n) -> Tuple[Description, Terms, Dict[str, str]]:\n\"\"\"\n        Generate a proposal matching the query.\n        :param query: the query\n        :param counterparty_address: the counterparty of the proposal.\n        :return: a tuple of proposal, terms and the weather data\n        \"\"\"\ndata_for_sale = self.data_for_sale\nsale_quantity = len(data_for_sale)\nseller_address = self.context.agent_addresses[self.ledger_id]\ntotal_price = sale_quantity * self._unit_price\nif self.is_ledger_tx:\ntx_nonce = LedgerApis.generate_tx_nonce(\nidentifier=self.ledger_id,\nseller=seller_address,\nclient=counterparty_address,\n)\nelse:\ntx_nonce = uuid.uuid4().hex  # pragma: nocover\nproposal = Description(\n{\n\"ledger_id\": self.ledger_id,\n\"price\": total_price,\n\"currency_id\": self._currency_id,\n\"service_id\": self._service_id,\n\"quantity\": sale_quantity,\n\"tx_nonce\": tx_nonce,\n}\n)\nterms = Terms(\nledger_id=self.ledger_id,\nsender_address=seller_address,\ncounterparty_address=counterparty_address,\namount_by_currency_id={self._currency_id: total_price},\nquantities_by_good_id={self._service_id: -sale_quantity},\nis_sender_payable_tx_fee=False,\nnonce=tx_nonce,\nfee_by_currency_id={self._currency_id: 0},\n)\nreturn proposal, terms, data_for_sale\ndef collect_from_data_source(self) -> Dict[str, str]:\n\"\"\"Implement the logic to communicate with the sensor.\"\"\"\nraise NotImplementedError\n

The helper private function collect_from_data_source is where we read data from a sensor or if there are no sensor we use some default data provided (see the data_for_sale property).

"},{"location":"aea-framework-documentation/generic-skills-step-by-step/#step-5-create-the-dialogues","title":"Step 5: Create the Dialogues","text":"

To keep track of the structure and progress of interactions, including negotiations with a buyer AEA and interactions with search nodes and ledgers, we use dialogues. Create a new file in the skill folder (my_generic_seller/skills/generic_seller/) and name it dialogues.py. Inside this file add the following code:

from typing import Any, Dict, Optional, Type\nfrom aea.common import Address\nfrom aea.exceptions import AEAEnforceError, enforce\nfrom aea.helpers.transaction.base import Terms\nfrom aea.protocols.base import Message\nfrom aea.protocols.dialogue.base import Dialogue as BaseDialogue\nfrom aea.protocols.dialogue.base import DialogueLabel as BaseDialogueLabel\nfrom aea.skills.base import Model\nfrom packages.fetchai.protocols.default.dialogues import (\nDefaultDialogue as BaseDefaultDialogue,\n)\nfrom packages.fetchai.protocols.default.dialogues import (\nDefaultDialogues as BaseDefaultDialogues,\n)\nfrom packages.fetchai.protocols.fipa.dialogues import FipaDialogue as BaseFipaDialogue\nfrom packages.fetchai.protocols.fipa.dialogues import FipaDialogues as BaseFipaDialogues\nfrom packages.fetchai.protocols.fipa.message import FipaMessage\nfrom packages.fetchai.protocols.ledger_api.dialogues import (\nLedgerApiDialogue as BaseLedgerApiDialogue,\n)\nfrom packages.fetchai.protocols.ledger_api.dialogues import (\nLedgerApiDialogues as BaseLedgerApiDialogues,\n)\nfrom packages.fetchai.protocols.ledger_api.message import LedgerApiMessage\nfrom packages.fetchai.protocols.oef_search.dialogues import (\nOefSearchDialogue as BaseOefSearchDialogue,\n)\nfrom packages.fetchai.protocols.oef_search.dialogues import (\nOefSearchDialogues as BaseOefSearchDialogues,\n)\nDefaultDialogue = BaseDefaultDialogue\nclass DefaultDialogues(Model, BaseDefaultDialogues):\n\"\"\"The dialogues class keeps track of all dialogues.\"\"\"\ndef __init__(self, **kwargs: Any) -> None:\n\"\"\"\n        Initialize dialogues.\n        :param kwargs: keyword arguments\n        \"\"\"\nModel.__init__(self, **kwargs)\ndef role_from_first_message(  # pylint: disable=unused-argument\nmessage: Message, receiver_address: Address\n) -> BaseDialogue.Role:\n\"\"\"Infer the role of the agent from an incoming/outgoing first message\n            :param message: an incoming/outgoing first message\n            :param receiver_address: the address of the receiving agent\n            :return: The role of the agent\n            \"\"\"\nreturn DefaultDialogue.Role.AGENT\nBaseDefaultDialogues.__init__(\nself,\nself_address=self.context.agent_address,\nrole_from_first_message=role_from_first_message,\n)\nclass FipaDialogue(BaseFipaDialogue):\n\"\"\"The dialogue class maintains state of a dialogue and manages it.\"\"\"\n__slots__ = (\"data_for_sale\", \"_terms\")\ndef __init__(\nself,\ndialogue_label: BaseDialogueLabel,\nself_address: Address,\nrole: BaseDialogue.Role,\nmessage_class: Type[FipaMessage] = FipaMessage,\n) -> None:\n\"\"\"\n        Initialize a dialogue.\n        :param dialogue_label: the identifier of the dialogue\n        :param self_address: the address of the entity for whom this dialogue is maintained\n        :param role: the role of the agent this dialogue is maintained for\n        :param message_class: the message class\n        \"\"\"\nBaseFipaDialogue.__init__(\nself,\ndialogue_label=dialogue_label,\nself_address=self_address,\nrole=role,\nmessage_class=message_class,\n)\nself.data_for_sale = None  # type: Optional[Dict[str, str]]\nself._terms = None  # type: Optional[Terms]\n@property\ndef terms(self) -> Terms:\n\"\"\"Get terms.\"\"\"\nif self._terms is None:\nraise AEAEnforceError(\"Terms not set!\")\nreturn self._terms\n@terms.setter\ndef terms(self, terms: Terms) -> None:\n\"\"\"Set terms.\"\"\"\nenforce(self._terms is None, \"Terms already set!\")\nself._terms = terms\nclass FipaDialogues(Model, BaseFipaDialogues):\n\"\"\"The dialogues class keeps track of all dialogues.\"\"\"\ndef __init__(self, **kwargs: Any) -> None:\n\"\"\"\n        Initialize dialogues.\n        :param kwargs: keyword arguments\n        \"\"\"\nModel.__init__(self, **kwargs)\ndef role_from_first_message(  # pylint: disable=unused-argument\nmessage: Message, receiver_address: Address\n) -> BaseDialogue.Role:\n\"\"\"Infer the role of the agent from an incoming/outgoing first message\n            :param message: an incoming/outgoing first message\n            :param receiver_address: the address of the receiving agent\n            :return: The role of the agent\n            \"\"\"\nreturn FipaDialogue.Role.SELLER\nBaseFipaDialogues.__init__(\nself,\nself_address=self.context.agent_address,\nrole_from_first_message=role_from_first_message,\ndialogue_class=FipaDialogue,\n)\nclass LedgerApiDialogue(BaseLedgerApiDialogue):\n\"\"\"The dialogue class maintains state of a dialogue and manages it.\"\"\"\n__slots__ = (\"_associated_fipa_dialogue\",)\ndef __init__(\nself,\ndialogue_label: BaseDialogueLabel,\nself_address: Address,\nrole: BaseDialogue.Role,\nmessage_class: Type[LedgerApiMessage] = LedgerApiMessage,\n) -> None:\n\"\"\"\n        Initialize a dialogue.\n        :param dialogue_label: the identifier of the dialogue\n        :param self_address: the address of the entity for whom this dialogue is maintained\n        :param role: the role of the agent this dialogue is maintained for\n        :param message_class: the message class\n        \"\"\"\nBaseLedgerApiDialogue.__init__(\nself,\ndialogue_label=dialogue_label,\nself_address=self_address,\nrole=role,\nmessage_class=message_class,\n)\nself._associated_fipa_dialogue = None  # type: Optional[FipaDialogue]\n@property\ndef associated_fipa_dialogue(self) -> FipaDialogue:\n\"\"\"Get associated_fipa_dialogue.\"\"\"\nif self._associated_fipa_dialogue is None:\nraise AEAEnforceError(\"FipaDialogue not set!\")\nreturn self._associated_fipa_dialogue\n@associated_fipa_dialogue.setter\ndef associated_fipa_dialogue(self, fipa_dialogue: FipaDialogue) -> None:\n\"\"\"Set associated_fipa_dialogue\"\"\"\nenforce(self._associated_fipa_dialogue is None, \"FipaDialogue already set!\")\nself._associated_fipa_dialogue = fipa_dialogue\nclass LedgerApiDialogues(Model, BaseLedgerApiDialogues):\n\"\"\"The dialogues class keeps track of all dialogues.\"\"\"\ndef __init__(self, **kwargs: Any) -> None:\n\"\"\"\n        Initialize dialogues.\n        :param kwargs: keyword arguments\n        \"\"\"\nModel.__init__(self, **kwargs)\ndef role_from_first_message(  # pylint: disable=unused-argument\nmessage: Message, receiver_address: Address\n) -> BaseDialogue.Role:\n\"\"\"Infer the role of the agent from an incoming/outgoing first message\n            :param message: an incoming/outgoing first message\n            :param receiver_address: the address of the receiving agent\n            :return: The role of the agent\n            \"\"\"\nreturn BaseLedgerApiDialogue.Role.AGENT\nBaseLedgerApiDialogues.__init__(\nself,\nself_address=str(self.skill_id),\nrole_from_first_message=role_from_first_message,\ndialogue_class=LedgerApiDialogue,\n)\nOefSearchDialogue = BaseOefSearchDialogue\nclass OefSearchDialogues(Model, BaseOefSearchDialogues):\n\"\"\"This class keeps track of all oef_search dialogues.\"\"\"\ndef __init__(self, **kwargs: Any) -> None:\n\"\"\"\n        Initialize dialogues.\n        :param kwargs: keyword arguments\n        \"\"\"\nModel.__init__(self, **kwargs)\ndef role_from_first_message(  # pylint: disable=unused-argument\nmessage: Message, receiver_address: Address\n) -> BaseDialogue.Role:\n\"\"\"Infer the role of the agent from an incoming/outgoing first message\n            :param message: an incoming/outgoing first message\n            :param receiver_address: the address of the receiving agent\n            :return: The role of the agent\n            \"\"\"\nreturn BaseOefSearchDialogue.Role.AGENT\nBaseOefSearchDialogues.__init__(\nself,\nself_address=str(self.skill_id),\nrole_from_first_message=role_from_first_message,\n)\n

The FipaDialogues class contains negotiation dialogues with each my_generic_buyer AEA (and other AEAs) and exposes a number of helpful methods to manage them. This helps us match messages to the dialogues they belong to, access previous messages and enable us to identify possible communications problems between the my_generic_seller AEA and the my_generic_buyer AEA. It also keeps track of the data that we offer for sale during the proposal phase.

The FipaDialogues class extends BaseFipaDialogues, which itself derives from the base Dialogues class. Similarly, the FipaDialogue class extends BaseFipaDialogue which itself derives from the base Dialogue class. To learn more about dialogues have a look here.

"},{"location":"aea-framework-documentation/generic-skills-step-by-step/#step-6-update-the-yaml-files","title":"Step 6: Update the YAML Files","text":"

Since we made so many changes to our AEA we have to update the skill.yaml (at my_generic_seller/skills/generic_seller/skill.yaml). Make sure you update your skill.yaml with the following configuration:

name: generic_seller\nauthor: fetchai\nversion: 0.1.0\ntype: skill\ndescription: The weather station skill implements the functionality to sell weather\ndata.\nlicense: Apache-2.0\naea_version: '>=1.0.0, <2.0.0'\nfingerprint:\nREADME.md: QmPb5kHYZyhUN87EKmuahyGqDGgqVdGPyfC1KpGC3xfmcP\n__init__.py: QmTSEedzQySy2nzRCY3F66CBSX52f8s3pWHZTejX4hKC9h\nbehaviours.py: QmS9sPCv2yBnhWsmHeaCptpApMtYZipbR39TXixeGK64Ks\ndialogues.py: QmdTW8q1xQ7ajFVsWmuV62ypoT5J2b6Hkyz52LFaWuMEtd\nhandlers.py: QmQnQhSaHPUYaJut8bMe2LHEqiZqokMSVfCthVaqrvPbdi\nstrategy.py: QmYTUsfv64eRQDevCfMUDQPx2GCtiMLFdacN4sS1E4Fdfx\nfingerprint_ignore_patterns: []\nconnections:\n- fetchai/ledger:0.21.5\ncontracts: []\nprotocols:\n- fetchai/default:1.1.7\n- fetchai/fipa:1.1.7\n- fetchai/ledger_api:1.1.7\n- fetchai/oef_search:1.1.7\nskills: []\nbehaviours:\nservice_registration:\nargs:\nservices_interval: 20\nclass_name: GenericServiceRegistrationBehaviour\nhandlers:\nfipa:\nargs: {}\nclass_name: GenericFipaHandler\nledger_api:\nargs: {}\nclass_name: GenericLedgerApiHandler\noef_search:\nargs: {}\nclass_name: GenericOefSearchHandler\nmodels:\ndefault_dialogues:\nargs: {}\nclass_name: DefaultDialogues\nfipa_dialogues:\nargs: {}\nclass_name: FipaDialogues\nledger_api_dialogues:\nargs: {}\nclass_name: LedgerApiDialogues\noef_search_dialogues:\nargs: {}\nclass_name: OefSearchDialogues\nstrategy:\nargs:\ndata_for_sale:\ngeneric: data\nhas_data_source: false\nis_ledger_tx: true\nlocation:\nlatitude: 51.5194\nlongitude: 0.127\nservice_data:\nkey: seller_service\nvalue: generic_service\nservice_id: generic_service\nunit_price: 10\nclass_name: GenericStrategy\nis_abstract: false\ndependencies: {}\n

We must pay attention to the models and in particular the strategy\u2019s variables. Here we can change the price we would like to sell each data reading for, or the currency we would like to transact with. Lastly, the dependencies are the third party packages we need to install in order to get readings from the sensor.

Finally, we fingerprint our new skill:

aea fingerprint skill fetchai/generic_seller:0.1.0\n

This will hash each file and save the hash in the fingerprint. This way, in the future we can easily track if any of the files have changed.

"},{"location":"aea-framework-documentation/generic-skills-step-by-step/#generic-buyer-aea","title":"Generic Buyer AEA","text":""},{"location":"aea-framework-documentation/generic-skills-step-by-step/#step-1-create-the-aea_1","title":"Step 1: Create the AEA","text":"

In a new terminal, create a new AEA by typing the following command in the terminal:

aea create my_generic_buyer\ncd my_generic_buyer\naea install\n

Our newly created AEA is inside the current working directory. Let\u2019s create a new skill for purchasing data. Type the following command:

aea scaffold skill generic_buyer\n

This command creates the correct structure for a new skill inside our AEA project. You can locate the newly created skill under the skills folder (my_generic_buyer/skills/generic_buyer/) and it must contain the following files:

  • __init__.py
  • behaviours.py
  • handlers.py
  • my_model.py
  • skills.yaml
"},{"location":"aea-framework-documentation/generic-skills-step-by-step/#step-2-create-the-behaviour_1","title":"Step 2: Create the Behaviour","text":"

Open the behaviours.py file (my_generic_buyer/skills/generic_buyer/behaviours.py) and replace the stub code with the following:

from typing import Any, List, Optional, Set, cast\nfrom aea.protocols.dialogue.base import DialogueLabel\nfrom aea.skills.behaviours import TickerBehaviour\nfrom packages.fetchai.connections.ledger.base import (\nCONNECTION_ID as LEDGER_CONNECTION_PUBLIC_ID,\n)\nfrom packages.fetchai.protocols.ledger_api.message import LedgerApiMessage\nfrom packages.fetchai.protocols.oef_search.message import OefSearchMessage\nfrom packages.fetchai.skills.generic_buyer.dialogues import (\nFipaDialogue,\nLedgerApiDialogue,\nLedgerApiDialogues,\nOefSearchDialogues,\n)\nfrom packages.fetchai.skills.generic_buyer.strategy import GenericStrategy\nDEFAULT_MAX_PROCESSING = 120\nDEFAULT_TX_INTERVAL = 2.0\nDEFAULT_SEARCH_INTERVAL = 5.0\nLEDGER_API_ADDRESS = str(LEDGER_CONNECTION_PUBLIC_ID)\nclass GenericSearchBehaviour(TickerBehaviour):\n\"\"\"This class implements a search behaviour.\"\"\"\ndef __init__(self, **kwargs: Any):\n\"\"\"Initialize the search behaviour.\"\"\"\nsearch_interval = cast(\nfloat, kwargs.pop(\"search_interval\", DEFAULT_SEARCH_INTERVAL)\n)\nsuper().__init__(tick_interval=search_interval, **kwargs)\ndef setup(self) -> None:\n\"\"\"Implement the setup for the behaviour.\"\"\"\nstrategy = cast(GenericStrategy, self.context.strategy)\nif strategy.is_ledger_tx:\nledger_api_dialogues = cast(\nLedgerApiDialogues, self.context.ledger_api_dialogues\n)\nledger_api_msg, _ = ledger_api_dialogues.create(\ncounterparty=LEDGER_API_ADDRESS,\nperformative=LedgerApiMessage.Performative.GET_BALANCE,\nledger_id=strategy.ledger_id,\naddress=cast(str, self.context.agent_addresses.get(strategy.ledger_id)),\n)\nself.context.outbox.put_message(message=ledger_api_msg)\nelse:\nstrategy.is_searching = True\ndef act(self) -> None:\n\"\"\"Implement the act.\"\"\"\nstrategy = cast(GenericStrategy, self.context.strategy)\nif not strategy.is_searching:\nreturn\ntransaction_behaviour = cast(\nGenericTransactionBehaviour, self.context.behaviours.transaction\n)\nremaining_transactions_count = len(transaction_behaviour.waiting)\nif remaining_transactions_count > 0:\nself.context.logger.info(\nf\"Transaction behaviour has {remaining_transactions_count} transactions remaining. Skipping search!\"\n)\nreturn\nstrategy.update_search_query_params()\nquery = strategy.get_location_and_service_query()\noef_search_dialogues = cast(\nOefSearchDialogues, self.context.oef_search_dialogues\n)\noef_search_msg, _ = oef_search_dialogues.create(\ncounterparty=self.context.search_service_address,\nperformative=OefSearchMessage.Performative.SEARCH_SERVICES,\nquery=query,\n)\nself.context.outbox.put_message(message=oef_search_msg)\ndef teardown(self) -> None:\n\"\"\"Implement the task teardown.\"\"\"\nclass GenericTransactionBehaviour(TickerBehaviour):\n\"\"\"A behaviour to sequentially submit transactions to the blockchain.\"\"\"\ndef __init__(self, **kwargs: Any):\n\"\"\"Initialize the transaction behaviour.\"\"\"\ntx_interval = cast(\nfloat, kwargs.pop(\"transaction_interval\", DEFAULT_TX_INTERVAL)\n)\nself.max_processing = cast(\nfloat, kwargs.pop(\"max_processing\", DEFAULT_MAX_PROCESSING)\n)\nself.processing_time = 0.0\nself.waiting: List[FipaDialogue] = []\nself.processing: Optional[LedgerApiDialogue] = None\nself.timedout: Set[DialogueLabel] = set()\nsuper().__init__(tick_interval=tx_interval, **kwargs)\ndef setup(self) -> None:\n\"\"\"Setup behaviour.\"\"\"\ndef act(self) -> None:\n\"\"\"Implement the act.\"\"\"\nif self.processing is not None:\nif self.processing_time <= self.max_processing:\n# already processing\nself.processing_time += self.tick_interval\nreturn\nself._timeout_processing()\nif len(self.waiting) == 0:\n# nothing to process\nreturn\nself._start_processing()\ndef _start_processing(self) -> None:\n\"\"\"Process the next transaction.\"\"\"\nfipa_dialogue = self.waiting.pop(0)\nself.context.logger.info(\nf\"Processing transaction, {len(self.waiting)} transactions remaining\"\n)\nledger_api_dialogues = cast(\nLedgerApiDialogues, self.context.ledger_api_dialogues\n)\nledger_api_msg, ledger_api_dialogue = ledger_api_dialogues.create(\ncounterparty=LEDGER_API_ADDRESS,\nperformative=LedgerApiMessage.Performative.GET_RAW_TRANSACTION,\nterms=fipa_dialogue.terms,\n)\nledger_api_dialogue = cast(LedgerApiDialogue, ledger_api_dialogue)\nledger_api_dialogue.associated_fipa_dialogue = fipa_dialogue\nself.processing_time = 0.0\nself.processing = ledger_api_dialogue\nself.context.logger.info(\nf\"requesting transfer transaction from ledger api for message={ledger_api_msg}...\"\n)\nself.context.outbox.put_message(message=ledger_api_msg)\ndef teardown(self) -> None:\n\"\"\"Teardown behaviour.\"\"\"\ndef _timeout_processing(self) -> None:\n\"\"\"Timeout processing.\"\"\"\nif self.processing is None:\nreturn\nself.timedout.add(self.processing.dialogue_label)\nself.waiting.append(self.processing.associated_fipa_dialogue)\nself.processing_time = 0.0\nself.processing = None\ndef finish_processing(self, ledger_api_dialogue: LedgerApiDialogue) -> None:\n\"\"\"\n        Finish processing.\n        :param ledger_api_dialogue: the ledger api dialogue\n        \"\"\"\nif self.processing == ledger_api_dialogue:\nself.processing_time = 0.0\nself.processing = None\nreturn\nif ledger_api_dialogue.dialogue_label not in self.timedout:\nraise ValueError(\nf\"Non-matching dialogues in transaction behaviour: {self.processing} and {ledger_api_dialogue}\"\n)\nself.timedout.remove(ledger_api_dialogue.dialogue_label)\nself.context.logger.debug(\nf\"Timeout dialogue in transaction processing: {ledger_api_dialogue}\"\n)\n# don't reset, as another might be processing\ndef failed_processing(self, ledger_api_dialogue: LedgerApiDialogue) -> None:\n\"\"\"\n        Failed processing.\n        Currently, we retry processing indefinitely.\n        :param ledger_api_dialogue: the ledger api dialogue\n        \"\"\"\nself.finish_processing(ledger_api_dialogue)\nself.waiting.append(ledger_api_dialogue.associated_fipa_dialogue)\n

This TickerBehaviour will send a search query to the SOEF search node at regular tick intervals.

"},{"location":"aea-framework-documentation/generic-skills-step-by-step/#step-3-create-the-handler_1","title":"Step 3: Create the Handler","text":"

So far, the AEA is tasked with sending search queries to the SOEF search node. However, currently the AEA has no way of handling the responses it receives from the SOEF or messages from other agents.

Let us now implement Handlers to deal with the expected incoming messages. Open the handlers.py file (my_generic_buyer/skills/generic_buyer/handlers.py) and add the following code (replacing the stub code already present in the file):

import pprint\nfrom typing import Optional, cast\nfrom aea.configurations.base import PublicId\nfrom aea.crypto.ledger_apis import LedgerApis\nfrom aea.protocols.base import Message\nfrom aea.skills.base import Handler\nfrom packages.fetchai.connections.ledger.base import (\nCONNECTION_ID as LEDGER_CONNECTION_PUBLIC_ID,\n)\nfrom packages.fetchai.protocols.default.message import DefaultMessage\nfrom packages.fetchai.protocols.fipa.message import FipaMessage\nfrom packages.fetchai.protocols.ledger_api.message import LedgerApiMessage\nfrom packages.fetchai.protocols.oef_search.message import OefSearchMessage\nfrom packages.fetchai.protocols.signing.message import SigningMessage\nfrom packages.fetchai.skills.generic_buyer.behaviours import GenericTransactionBehaviour\nfrom packages.fetchai.skills.generic_buyer.dialogues import (\nDefaultDialogues,\nFipaDialogue,\nFipaDialogues,\nLedgerApiDialogue,\nLedgerApiDialogues,\nOefSearchDialogue,\nOefSearchDialogues,\nSigningDialogue,\nSigningDialogues,\n)\nfrom packages.fetchai.skills.generic_buyer.strategy import GenericStrategy\nLEDGER_API_ADDRESS = str(LEDGER_CONNECTION_PUBLIC_ID)\nclass GenericFipaHandler(Handler):\n\"\"\"This class implements a FIPA handler.\"\"\"\nSUPPORTED_PROTOCOL = FipaMessage.protocol_id  # type: Optional[PublicId]\ndef setup(self) -> None:\n\"\"\"Implement the setup.\"\"\"\ndef handle(self, message: Message) -> None:\n\"\"\"\n        Implement the reaction to a message.\n        :param message: the message\n        \"\"\"\nfipa_msg = cast(FipaMessage, message)\n# recover dialogue\nfipa_dialogues = cast(FipaDialogues, self.context.fipa_dialogues)\nfipa_dialogue = cast(FipaDialogue, fipa_dialogues.update(fipa_msg))\nif fipa_dialogue is None:\nself._handle_unidentified_dialogue(fipa_msg)\nreturn\n# handle message\nif fipa_msg.performative == FipaMessage.Performative.PROPOSE:\nself._handle_propose(fipa_msg, fipa_dialogue)\nelif fipa_msg.performative == FipaMessage.Performative.DECLINE:\nself._handle_decline(fipa_msg, fipa_dialogue, fipa_dialogues)\nelif fipa_msg.performative == FipaMessage.Performative.MATCH_ACCEPT_W_INFORM:\nself._handle_match_accept(fipa_msg, fipa_dialogue)\nelif fipa_msg.performative == FipaMessage.Performative.INFORM:\nself._handle_inform(fipa_msg, fipa_dialogue, fipa_dialogues)\nelse:\nself._handle_invalid(fipa_msg, fipa_dialogue)\ndef teardown(self) -> None:\n\"\"\"Implement the handler teardown.\"\"\"\n

You will see that we are following similar logic to the generic_seller when we develop the generic_buyer\u2019s side of the negotiation. First, we create a new dialogue and store it in the dialogues class. Then we are checking what kind of message we received by checking its performative. So lets start creating our handlers:

    def _handle_unidentified_dialogue(self, fipa_msg: FipaMessage) -> None:\n\"\"\"\n        Handle an unidentified dialogue.\n        :param fipa_msg: the message\n        \"\"\"\nself.context.logger.info(\n\"received invalid fipa message={}, unidentified dialogue.\".format(fipa_msg)\n)\ndefault_dialogues = cast(DefaultDialogues, self.context.default_dialogues)\ndefault_msg, _ = default_dialogues.create(\ncounterparty=fipa_msg.sender,\nperformative=DefaultMessage.Performative.ERROR,\nerror_code=DefaultMessage.ErrorCode.INVALID_DIALOGUE,\nerror_msg=\"Invalid dialogue.\",\nerror_data={\"fipa_message\": fipa_msg.encode()},\n)\nself.context.outbox.put_message(message=default_msg)\n

The above code handles messages referencing unidentified dialogues and responds with an error message to the sender. Next we will handle the PROPOSE message received from the my_generic_seller AEA:

    def _handle_propose(\nself, fipa_msg: FipaMessage, fipa_dialogue: FipaDialogue\n) -> None:\n\"\"\"\n        Handle the propose.\n        :param fipa_msg: the message\n        :param fipa_dialogue: the dialogue object\n        \"\"\"\nself.context.logger.info(\n\"received proposal={} from sender={}\".format(\nfipa_msg.proposal.values,\nfipa_msg.sender[-5:],\n)\n)\nstrategy = cast(GenericStrategy, self.context.strategy)\nacceptable = strategy.is_acceptable_proposal(fipa_msg.proposal)\naffordable = strategy.is_affordable_proposal(fipa_msg.proposal)\nif acceptable and affordable:\nself.context.logger.info(\n\"accepting the proposal from sender={}\".format(fipa_msg.sender[-5:])\n)\nterms = strategy.terms_from_proposal(fipa_msg.proposal, fipa_msg.sender)\nfipa_dialogue.terms = terms\naccept_msg = fipa_dialogue.reply(\nperformative=FipaMessage.Performative.ACCEPT,\ntarget_message=fipa_msg,\n)\nself.context.outbox.put_message(message=accept_msg)\nelse:\nself.context.logger.info(\n\"declining the proposal from sender={}\".format(fipa_msg.sender[-5:])\n)\ndecline_msg = fipa_dialogue.reply(\nperformative=FipaMessage.Performative.DECLINE,\ntarget_message=fipa_msg,\n)\nself.context.outbox.put_message(message=decline_msg)\n

When we receive a proposal, we have to check if we have the funds to complete the transaction and if the proposal is acceptable based on our strategy. If the proposal is not affordable or acceptable, we respond with a DECLINE message. Otherwise, we send an ACCEPT message to the seller.

The next code-block handles the DECLINE message that we may receive from the seller as a response to our CFP or ACCEPT messages:

    def _handle_decline(\nself,\nfipa_msg: FipaMessage,\nfipa_dialogue: FipaDialogue,\nfipa_dialogues: FipaDialogues,\n) -> None:\n\"\"\"\n        Handle the decline.\n        :param fipa_msg: the message\n        :param fipa_dialogue: the fipa dialogue\n        :param fipa_dialogues: the fipa dialogues\n        \"\"\"\nself.context.logger.info(\n\"received DECLINE from sender={}\".format(fipa_msg.sender[-5:])\n)\ntarget_message = fipa_dialogue.get_message_by_id(fipa_msg.target)\nif not target_message:\nraise ValueError(\"Can not find target message!\")  # pragma: nocover\ndeclined_performative = target_message.performative\nif declined_performative == FipaMessage.Performative.CFP:\nfipa_dialogues.dialogue_stats.add_dialogue_endstate(\nFipaDialogue.EndState.DECLINED_CFP, fipa_dialogue.is_self_initiated\n)\nif declined_performative == FipaMessage.Performative.ACCEPT:\nfipa_dialogues.dialogue_stats.add_dialogue_endstate(\nFipaDialogue.EndState.DECLINED_ACCEPT, fipa_dialogue.is_self_initiated\n)\n

The above code terminates each dialogue with the specific AEA and stores the state of the terminated dialogue (whether it was terminated after a CFP or an ACCEPT).

If my_generic_seller AEA wants to move on with the sale, it will send a MATCH_ACCEPT message. In order to handle this we add the following code:

def _handle_match_accept(\nself, fipa_msg: FipaMessage, fipa_dialogue: FipaDialogue\n) -> None:\n\"\"\"\n        Handle the match accept.\n        :param fipa_msg: the message\n        :param fipa_dialogue: the dialogue object\n        \"\"\"\nself.context.logger.info(\n\"received MATCH_ACCEPT_W_INFORM from sender={} with info={}\".format(\nfipa_msg.sender[-5:], fipa_msg.info\n)\n)\nstrategy = cast(GenericStrategy, self.context.strategy)\nif strategy.is_ledger_tx:\ntransfer_address = fipa_msg.info.get(\"address\", None)\nif transfer_address is not None and isinstance(transfer_address, str):\nfipa_dialogue.terms.counterparty_address = (  # pragma: nocover\ntransfer_address\n)\ntx_behaviour = cast(\nGenericTransactionBehaviour, self.context.behaviours.transaction\n)\ntx_behaviour.waiting.append(fipa_dialogue)\nelse:\ninform_msg = fipa_dialogue.reply(\nperformative=FipaMessage.Performative.INFORM,\ntarget_message=fipa_msg,\ninfo={\"Done\": \"Sending payment via bank transfer\"},\n)\nself.context.outbox.put_message(message=inform_msg)\nself.context.logger.info(\n\"informing counterparty={} of payment.\".format(fipa_msg.sender[-5:])\n)\n

The first thing we are checking is if we enabled our AEA to transact with a ledger. If so, we add this negotiation to the queue of transactions to be processed. If not, we simulate non-ledger payment by sending an inform to the seller that the payment is done (say via bank transfer).

Lastly, we need to handle INFORM messages. This is the message that will have our data:

    def _handle_inform(\nself,\nfipa_msg: FipaMessage,\nfipa_dialogue: FipaDialogue,\nfipa_dialogues: FipaDialogues,\n) -> None:\n\"\"\"\n        Handle the match inform.\n        :param fipa_msg: the message\n        :param fipa_dialogue: the fipa dialogue\n        :param fipa_dialogues: the fipa dialogues\n        \"\"\"\nself.context.logger.info(\n\"received INFORM from sender={}\".format(fipa_msg.sender[-5:])\n)\nif len(fipa_msg.info.keys()) >= 1:\ndata = fipa_msg.info\ndata_string = pprint.pformat(data)[:1000]\nself.context.logger.info(f\"received the following data={data_string}\")\nfipa_dialogues.dialogue_stats.add_dialogue_endstate(\nFipaDialogue.EndState.SUCCESSFUL, fipa_dialogue.is_self_initiated\n)\nstrategy = cast(GenericStrategy, self.context.strategy)\nstrategy.successful_trade_with_counterparty(fipa_msg.sender, data)\nelse:\nself.context.logger.info(\n\"received no data from sender={}\".format(fipa_msg.sender[-5:])\n)\ndef _handle_invalid(\nself, fipa_msg: FipaMessage, fipa_dialogue: FipaDialogue\n) -> None:\n\"\"\"\n        Handle a fipa message of invalid performative.\n        :param fipa_msg: the message\n        :param fipa_dialogue: the fipa dialogue\n        \"\"\"\nself.context.logger.warning(\n\"cannot handle fipa message of performative={} in dialogue={}.\".format(\nfipa_msg.performative, fipa_dialogue\n)\n)\n

We now need to add handlers for messages received from the DecisionMaker and the SOEF search node. We need one handler for each type of protocol we use.

To handle the messages in the oef_search protocol used by the SOEF search node we add the following code in the same file (my_generic_buyer/skills/generic_buyer/handlers.py):

class GenericOefSearchHandler(Handler):\n\"\"\"This class implements an OEF search handler.\"\"\"\nSUPPORTED_PROTOCOL = OefSearchMessage.protocol_id  # type: Optional[PublicId]\ndef setup(self) -> None:\n\"\"\"Call to setup the handler.\"\"\"\ndef handle(self, message: Message) -> None:\n\"\"\"\n        Implement the reaction to a message.\n        :param message: the message\n        \"\"\"\noef_search_msg = cast(OefSearchMessage, message)\n# recover dialogue\noef_search_dialogues = cast(\nOefSearchDialogues, self.context.oef_search_dialogues\n)\noef_search_dialogue = cast(\nOptional[OefSearchDialogue], oef_search_dialogues.update(oef_search_msg)\n)\nif oef_search_dialogue is None:\nself._handle_unidentified_dialogue(oef_search_msg)\nreturn\n# handle message\nif oef_search_msg.performative is OefSearchMessage.Performative.OEF_ERROR:\nself._handle_error(oef_search_msg, oef_search_dialogue)\nelif oef_search_msg.performative is OefSearchMessage.Performative.SEARCH_RESULT:\nself._handle_search(oef_search_msg, oef_search_dialogue)\nelse:\nself._handle_invalid(oef_search_msg, oef_search_dialogue)\ndef teardown(self) -> None:\n\"\"\"Implement the handler teardown.\"\"\"\ndef _handle_unidentified_dialogue(self, oef_search_msg: OefSearchMessage) -> None:\n\"\"\"\n        Handle an unidentified dialogue.\n        :param oef_search_msg: the message\n        \"\"\"\nself.context.logger.info(\n\"received invalid oef_search message={}, unidentified dialogue.\".format(\noef_search_msg\n)\n)\ndef _handle_error(\nself, oef_search_msg: OefSearchMessage, oef_search_dialogue: OefSearchDialogue\n) -> None:\n\"\"\"\n        Handle an oef search message.\n        :param oef_search_msg: the oef search message\n        :param oef_search_dialogue: the dialogue\n        \"\"\"\nself.context.logger.info(\n\"received oef_search error message={} in dialogue={}.\".format(\noef_search_msg, oef_search_dialogue\n)\n)\ndef _handle_search(\nself, oef_search_msg: OefSearchMessage, oef_search_dialogue: OefSearchDialogue\n) -> None:\n\"\"\"\n        Handle the search response.\n        :param oef_search_msg: the oef search message\n        :param oef_search_dialogue: the dialogue\n        \"\"\"\nif len(oef_search_msg.agents) == 0:\nself.context.logger.info(\nf\"found no agents in dialogue={oef_search_dialogue}, continue searching.\"\n)\nreturn\nstrategy = cast(GenericStrategy, self.context.strategy)\nif strategy.is_stop_searching_on_result:\nself.context.logger.info(\n\"found agents={}, stopping search.\".format(\nlist(map(lambda x: x[-5:], oef_search_msg.agents)),\n)\n)\nstrategy.is_searching = False  # stopping search\nelse:\nself.context.logger.info(\n\"found agents={}.\".format(\nlist(map(lambda x: x[-5:], oef_search_msg.agents)),\n)\n)\nquery = strategy.get_service_query()\nfipa_dialogues = cast(FipaDialogues, self.context.fipa_dialogues)\ncounterparties = strategy.get_acceptable_counterparties(oef_search_msg.agents)\nfor counterparty in counterparties:\ncfp_msg, _ = fipa_dialogues.create(\ncounterparty=counterparty,\nperformative=FipaMessage.Performative.CFP,\nquery=query,\n)\nself.context.outbox.put_message(message=cfp_msg)\nself.context.logger.info(\n\"sending CFP to agent={}\".format(counterparty[-5:])\n)\ndef _handle_invalid(\nself, oef_search_msg: OefSearchMessage, oef_search_dialogue: OefSearchDialogue\n) -> None:\n\"\"\"\n        Handle an oef search message.\n        :param oef_search_msg: the oef search message\n        :param oef_search_dialogue: the dialogue\n        \"\"\"\nself.context.logger.warning(\n\"cannot handle oef_search message of performative={} in dialogue={}.\".format(\noef_search_msg.performative,\noef_search_dialogue,\n)\n)\n

When we receive a message from the SOEF search node of a type OefSearchMessage.Performative.SEARCH_RESULT, we are passing the details to the relevant handler method. In the _handle_search function, we are checking that the response contains some agents, and we stop the search if it does. We pick our first agent and send a CFP message.

The last handlers we need are the GenericSigningHandler and the GenericLedgerApiHandler. These handlers are responsible for SigningMessages that we receive from the DecisionMaker, and LedgerApiMessages that we receive from the ledger connection, respectively.

class GenericSigningHandler(Handler):\n\"\"\"Implement the signing handler.\"\"\"\nSUPPORTED_PROTOCOL = SigningMessage.protocol_id  # type: Optional[PublicId]\ndef setup(self) -> None:\n\"\"\"Implement the setup for the handler.\"\"\"\ndef handle(self, message: Message) -> None:\n\"\"\"\n        Implement the reaction to a message.\n        :param message: the message\n        \"\"\"\nsigning_msg = cast(SigningMessage, message)\n# recover dialogue\nsigning_dialogues = cast(SigningDialogues, self.context.signing_dialogues)\nsigning_dialogue = cast(\nOptional[SigningDialogue], signing_dialogues.update(signing_msg)\n)\nif signing_dialogue is None:\nself._handle_unidentified_dialogue(signing_msg)\nreturn\n# handle message\nif signing_msg.performative is SigningMessage.Performative.SIGNED_TRANSACTION:\nself._handle_signed_transaction(signing_msg, signing_dialogue)\nelif signing_msg.performative is SigningMessage.Performative.ERROR:\nself._handle_error(signing_msg, signing_dialogue)\nelse:\nself._handle_invalid(signing_msg, signing_dialogue)\ndef teardown(self) -> None:\n\"\"\"Implement the handler teardown.\"\"\"\ndef _handle_unidentified_dialogue(self, signing_msg: SigningMessage) -> None:\n\"\"\"\n        Handle an unidentified dialogue.\n        :param signing_msg: the message\n        \"\"\"\nself.context.logger.info(\n\"received invalid signing message={}, unidentified dialogue.\".format(\nsigning_msg\n)\n)\ndef _handle_signed_transaction(\nself, signing_msg: SigningMessage, signing_dialogue: SigningDialogue\n) -> None:\n\"\"\"\n        Handle an oef search message.\n        :param signing_msg: the signing message\n        :param signing_dialogue: the dialogue\n        \"\"\"\nself.context.logger.info(\"transaction signing was successful.\")\nledger_api_dialogue = signing_dialogue.associated_ledger_api_dialogue\nlast_ledger_api_msg = ledger_api_dialogue.last_incoming_message\nif last_ledger_api_msg is None:\nraise ValueError(\"Could not retrieve last message in ledger api dialogue\")\nledger_api_msg = ledger_api_dialogue.reply(\nperformative=LedgerApiMessage.Performative.SEND_SIGNED_TRANSACTION,\ntarget_message=last_ledger_api_msg,\nsigned_transaction=signing_msg.signed_transaction,\n)\nself.context.outbox.put_message(message=ledger_api_msg)\nself.context.logger.info(\"sending transaction to ledger.\")\ndef _handle_error(\nself, signing_msg: SigningMessage, signing_dialogue: SigningDialogue\n) -> None:\n\"\"\"\n        Handle an oef search message.\n        :param signing_msg: the signing message\n        :param signing_dialogue: the dialogue\n        \"\"\"\nself.context.logger.info(\n\"transaction signing was not successful. Error_code={} in dialogue={}\".format(\nsigning_msg.error_code, signing_dialogue\n)\n)\nsigning_msg_ = cast(\nOptional[SigningMessage], signing_dialogue.last_outgoing_message\n)\nif (\nsigning_msg_ is not None\nand signing_msg_.performative\n== SigningMessage.Performative.SIGN_TRANSACTION\n):\ntx_behaviour = cast(\nGenericTransactionBehaviour, self.context.behaviours.transaction\n)\nledger_api_dialogue = signing_dialogue.associated_ledger_api_dialogue\ntx_behaviour.failed_processing(ledger_api_dialogue)\ndef _handle_invalid(\nself, signing_msg: SigningMessage, signing_dialogue: SigningDialogue\n) -> None:\n\"\"\"\n        Handle an oef search message.\n        :param signing_msg: the signing message\n        :param signing_dialogue: the dialogue\n        \"\"\"\nself.context.logger.warning(\n\"cannot handle signing message of performative={} in dialogue={}.\".format(\nsigning_msg.performative, signing_dialogue\n)\n)\nclass GenericLedgerApiHandler(Handler):\n\"\"\"Implement the ledger handler.\"\"\"\nSUPPORTED_PROTOCOL = LedgerApiMessage.protocol_id  # type: Optional[PublicId]\ndef setup(self) -> None:\n\"\"\"Implement the setup for the handler.\"\"\"\ndef handle(self, message: Message) -> None:\n\"\"\"\n        Implement the reaction to a message.\n        :param message: the message\n        \"\"\"\nledger_api_msg = cast(LedgerApiMessage, message)\n# recover dialogue\nledger_api_dialogues = cast(\nLedgerApiDialogues, self.context.ledger_api_dialogues\n)\nledger_api_dialogue = cast(\nOptional[LedgerApiDialogue], ledger_api_dialogues.update(ledger_api_msg)\n)\nif ledger_api_dialogue is None:\nself._handle_unidentified_dialogue(ledger_api_msg)\nreturn\n# handle message\nif ledger_api_msg.performative is LedgerApiMessage.Performative.BALANCE:\nself._handle_balance(ledger_api_msg)\nelif (\nledger_api_msg.performative is LedgerApiMessage.Performative.RAW_TRANSACTION\n):\nself._handle_raw_transaction(ledger_api_msg, ledger_api_dialogue)\nelif (\nledger_api_msg.performative\n== LedgerApiMessage.Performative.TRANSACTION_DIGEST\n):\nself._handle_transaction_digest(ledger_api_msg, ledger_api_dialogue)\nelif (\nledger_api_msg.performative\n== LedgerApiMessage.Performative.TRANSACTION_RECEIPT\n):\nself._handle_transaction_receipt(ledger_api_msg, ledger_api_dialogue)\nelif ledger_api_msg.performative == LedgerApiMessage.Performative.ERROR:\nself._handle_error(ledger_api_msg, ledger_api_dialogue)\nelse:\nself._handle_invalid(ledger_api_msg, ledger_api_dialogue)\ndef teardown(self) -> None:\n\"\"\"Implement the handler teardown.\"\"\"\ndef _handle_unidentified_dialogue(self, ledger_api_msg: LedgerApiMessage) -> None:\n\"\"\"\n        Handle an unidentified dialogue.\n        :param ledger_api_msg: the message\n        \"\"\"\nself.context.logger.info(\n\"received invalid ledger_api message={}, unidentified dialogue.\".format(\nledger_api_msg\n)\n)\ndef _handle_balance(self, ledger_api_msg: LedgerApiMessage) -> None:\n\"\"\"\n        Handle a message of balance performative.\n        :param ledger_api_msg: the ledger api message\n        \"\"\"\nstrategy = cast(GenericStrategy, self.context.strategy)\nif ledger_api_msg.balance > 0:\nself.context.logger.info(\n\"starting balance on {} ledger={}.\".format(\nstrategy.ledger_id,\nledger_api_msg.balance,\n)\n)\nstrategy.balance = ledger_api_msg.balance\nstrategy.is_searching = True\nelse:\nself.context.logger.warning(\nf\"you have no starting balance on {strategy.ledger_id} ledger! Stopping skill {self.skill_id}.\"\n)\nself.context.is_active = False\ndef _handle_raw_transaction(\nself, ledger_api_msg: LedgerApiMessage, ledger_api_dialogue: LedgerApiDialogue\n) -> None:\n\"\"\"\n        Handle a message of raw_transaction performative.\n        :param ledger_api_msg: the ledger api message\n        :param ledger_api_dialogue: the ledger api dialogue\n        \"\"\"\nself.context.logger.info(\"received raw transaction={}\".format(ledger_api_msg))\nsigning_dialogues = cast(SigningDialogues, self.context.signing_dialogues)\nsigning_msg, signing_dialogue = signing_dialogues.create(\ncounterparty=self.context.decision_maker_address,\nperformative=SigningMessage.Performative.SIGN_TRANSACTION,\nraw_transaction=ledger_api_msg.raw_transaction,\nterms=ledger_api_dialogue.associated_fipa_dialogue.terms,\n)\nsigning_dialogue = cast(SigningDialogue, signing_dialogue)\nsigning_dialogue.associated_ledger_api_dialogue = ledger_api_dialogue\nself.context.decision_maker_message_queue.put_nowait(signing_msg)\nself.context.logger.info(\n\"proposing the transaction to the decision maker. Waiting for confirmation ...\"\n)\ndef _handle_transaction_digest(\nself, ledger_api_msg: LedgerApiMessage, ledger_api_dialogue: LedgerApiDialogue\n) -> None:\n\"\"\"\n        Handle a message of transaction_digest performative.\n        :param ledger_api_msg: the ledger api message\n        :param ledger_api_dialogue: the ledger api dialogue\n        \"\"\"\nself.context.logger.info(\n\"transaction was successfully submitted. Transaction digest={}\".format(\nledger_api_msg.transaction_digest\n)\n)\nledger_api_msg_ = ledger_api_dialogue.reply(\nperformative=LedgerApiMessage.Performative.GET_TRANSACTION_RECEIPT,\ntarget_message=ledger_api_msg,\ntransaction_digest=ledger_api_msg.transaction_digest,\n)\nself.context.logger.info(\"checking transaction is settled.\")\nself.context.outbox.put_message(message=ledger_api_msg_)\ndef _handle_transaction_receipt(\nself, ledger_api_msg: LedgerApiMessage, ledger_api_dialogue: LedgerApiDialogue\n) -> None:\n\"\"\"\n        Handle a message of balance performative.\n        :param ledger_api_msg: the ledger api message\n        :param ledger_api_dialogue: the ledger api dialogue\n        \"\"\"\nfipa_dialogue = ledger_api_dialogue.associated_fipa_dialogue\nis_settled = LedgerApis.is_transaction_settled(\nfipa_dialogue.terms.ledger_id, ledger_api_msg.transaction_receipt.receipt\n)\ntx_behaviour = cast(\nGenericTransactionBehaviour, self.context.behaviours.transaction\n)\nif is_settled:\ntx_behaviour.finish_processing(ledger_api_dialogue)\nledger_api_msg_ = cast(\nOptional[LedgerApiMessage], ledger_api_dialogue.last_outgoing_message\n)\nif ledger_api_msg_ is None:\nraise ValueError(  # pragma: nocover\n\"Could not retrieve last ledger_api message\"\n)\nfipa_msg = cast(Optional[FipaMessage], fipa_dialogue.last_incoming_message)\nif fipa_msg is None:\nraise ValueError(\"Could not retrieve last fipa message\")\ninform_msg = fipa_dialogue.reply(\nperformative=FipaMessage.Performative.INFORM,\ntarget_message=fipa_msg,\ninfo={\"transaction_digest\": ledger_api_msg_.transaction_digest.body},\n)\nself.context.outbox.put_message(message=inform_msg)\nself.context.logger.info(\n\"transaction confirmed, informing counterparty={} of transaction digest.\".format(\nfipa_dialogue.dialogue_label.dialogue_opponent_addr[-5:],\n)\n)\nelse:\ntx_behaviour.failed_processing(ledger_api_dialogue)\nself.context.logger.info(\n\"transaction_receipt={} not settled or not valid, aborting\".format(\nledger_api_msg.transaction_receipt\n)\n)\ndef _handle_error(\nself, ledger_api_msg: LedgerApiMessage, ledger_api_dialogue: LedgerApiDialogue\n) -> None:\n\"\"\"\n        Handle a message of error performative.\n        :param ledger_api_msg: the ledger api message\n        :param ledger_api_dialogue: the ledger api dialogue\n        \"\"\"\nself.context.logger.info(\n\"received ledger_api error message={} in dialogue={}.\".format(\nledger_api_msg, ledger_api_dialogue\n)\n)\nledger_api_msg_ = cast(\nOptional[LedgerApiMessage], ledger_api_dialogue.last_outgoing_message\n)\nif (\nledger_api_msg_ is not None\nand ledger_api_msg_.performative\n!= LedgerApiMessage.Performative.GET_BALANCE\n):\ntx_behaviour = cast(\nGenericTransactionBehaviour, self.context.behaviours.transaction\n)\ntx_behaviour.failed_processing(ledger_api_dialogue)\ndef _handle_invalid(\nself, ledger_api_msg: LedgerApiMessage, ledger_api_dialogue: LedgerApiDialogue\n) -> None:\n\"\"\"\n        Handle a message of invalid performative.\n        :param ledger_api_msg: the ledger api message\n        :param ledger_api_dialogue: the ledger api dialogue\n        \"\"\"\nself.context.logger.warning(\n\"cannot handle ledger_api message of performative={} in dialogue={}.\".format(\nledger_api_msg.performative,\nledger_api_dialogue,\n)\n)\n
"},{"location":"aea-framework-documentation/generic-skills-step-by-step/#step-4-create-the-strategy_1","title":"Step 4: Create the Strategy","text":"

We are going to create the strategy that we want our AEA to follow. Rename the my_model.py file (in my_generic_buyer/skills/generic_buyer/) to strategy.py and replace the stub code with the following:

from typing import Any, Dict, List, Tuple\nfrom aea.common import Address\nfrom aea.exceptions import enforce\nfrom aea.helpers.search.generic import SIMPLE_SERVICE_MODEL\nfrom aea.helpers.search.models import (\nConstraint,\nConstraintType,\nDescription,\nLocation,\nQuery,\n)\nfrom aea.helpers.transaction.base import Terms\nfrom aea.skills.base import Model\nDEFAULT_IS_LEDGER_TX = True\nDEFAULT_MAX_UNIT_PRICE = 5\nDEFAULT_MAX_TX_FEE = 2\nDEFAULT_SERVICE_ID = \"generic_service\"\nDEFAULT_MIN_QUANTITY = 1\nDEFAULT_MAX_QUANTITY = 100\nDEFAULT_LOCATION = {\"longitude\": 0.1270, \"latitude\": 51.5194}\nDEFAULT_SEARCH_QUERY = {\n\"search_key\": \"seller_service\",\n\"search_value\": \"generic_service\",\n\"constraint_type\": \"==\",\n}\nDEFAULT_SEARCH_RADIUS = 5.0\nDEFAULT_MAX_NEGOTIATIONS = 2\nclass GenericStrategy(Model):\n\"\"\"This class defines a strategy for the agent.\"\"\"\ndef __init__(self, **kwargs: Any) -> None:\n\"\"\"\n        Initialize the strategy of the agent.\n        :param kwargs: keyword arguments\n        \"\"\"\nledger_id = kwargs.pop(\"ledger_id\", None)\ncurrency_id = kwargs.pop(\"currency_id\", None)\nself._is_ledger_tx = kwargs.pop(\"is_ledger_tx\", DEFAULT_IS_LEDGER_TX)\nself._max_unit_price = kwargs.pop(\"max_unit_price\", DEFAULT_MAX_UNIT_PRICE)\nself._min_quantity = kwargs.pop(\"min_quantity\", DEFAULT_MIN_QUANTITY)\nself._max_quantity = kwargs.pop(\"max_quantity\", DEFAULT_MAX_QUANTITY)\nself._max_tx_fee = kwargs.pop(\"max_tx_fee\", DEFAULT_MAX_TX_FEE)\nself._service_id = kwargs.pop(\"service_id\", DEFAULT_SERVICE_ID)\nself._search_query = kwargs.pop(\"search_query\", DEFAULT_SEARCH_QUERY)\nlocation = kwargs.pop(\"location\", DEFAULT_LOCATION)\nself._agent_location = Location(\nlatitude=location[\"latitude\"], longitude=location[\"longitude\"]\n)\nself._radius = kwargs.pop(\"search_radius\", DEFAULT_SEARCH_RADIUS)\nself._max_negotiations = kwargs.pop(\n\"max_negotiations\", DEFAULT_MAX_NEGOTIATIONS\n)\nself._is_stop_searching_on_result = kwargs.pop(\"stop_searching_on_result\", True)\nsuper().__init__(**kwargs)\nself._ledger_id = (\nledger_id if ledger_id is not None else self.context.default_ledger_id\n)\nif currency_id is None:\ncurrency_id = self.context.currency_denominations.get(self._ledger_id, None)\nenforce(\ncurrency_id is not None,\nf\"Currency denomination for ledger_id={self._ledger_id} not specified.\",\n)\nself._currency_id = currency_id\nself._is_searching = False\nself._balance = 0\n

Similar to the seller AEA, we initialize the strategy class by trying to read the strategy variables from the YAML file, and if not possible, use some default values. In the following snippet, the two methods after the properties are related to the OEF search service. Add this snippet under the initialization of the strategy class:

    @property\ndef ledger_id(self) -> str:\n\"\"\"Get the ledger id.\"\"\"\nreturn self._ledger_id\n@property\ndef is_ledger_tx(self) -> bool:\n\"\"\"Check whether or not tx are settled on a ledger.\"\"\"\nreturn self._is_ledger_tx\n@property\ndef is_stop_searching_on_result(self) -> bool:\n\"\"\"Check if search is stopped on result.\"\"\"\nreturn self._is_stop_searching_on_result\n@property\ndef is_searching(self) -> bool:\n\"\"\"Check if the agent is searching.\"\"\"\nreturn self._is_searching\n@is_searching.setter\ndef is_searching(self, is_searching: bool) -> None:\n\"\"\"Check if the agent is searching.\"\"\"\nenforce(isinstance(is_searching, bool), \"Can only set bool on is_searching!\")\nself._is_searching = is_searching\n@property\ndef balance(self) -> int:\n\"\"\"Get the balance.\"\"\"\nreturn self._balance\n@balance.setter\ndef balance(self, balance: int) -> None:\n\"\"\"Set the balance.\"\"\"\nself._balance = balance\n@property\ndef max_negotiations(self) -> int:\n\"\"\"Get the maximum number of negotiations the agent can start.\"\"\"\nreturn self._max_negotiations\ndef get_location_and_service_query(self) -> Query:\n\"\"\"\n        Get the location and service query of the agent.\n        :return: the query\n        \"\"\"\nclose_to_my_service = Constraint(\n\"location\", ConstraintType(\"distance\", (self._agent_location, self._radius))\n)\nservice_key_filter = Constraint(\nself._search_query[\"search_key\"],\nConstraintType(\nself._search_query[\"constraint_type\"],\nself._search_query[\"search_value\"],\n),\n)\nquery = Query(\n[close_to_my_service, service_key_filter],\n)\nreturn query\ndef get_service_query(self) -> Query:\n\"\"\"\n        Get the service query of the agent.\n        :return: the query\n        \"\"\"\nservice_key_filter = Constraint(\nself._search_query[\"search_key\"],\nConstraintType(\nself._search_query[\"constraint_type\"],\nself._search_query[\"search_value\"],\n),\n)\nquery = Query([service_key_filter], model=SIMPLE_SERVICE_MODEL)\nreturn query\n

The following code block checks if the proposal that we received is acceptable according to a strategy:

    def is_acceptable_proposal(self, proposal: Description) -> bool:\n\"\"\"\n        Check whether it is an acceptable proposal.\n        :param proposal: a description\n        :return: whether it is acceptable\n        \"\"\"\nresult = (\nall(\nkey in proposal.values\nfor key in [\n\"ledger_id\",\n\"currency_id\",\n\"price\",\n\"service_id\",\n\"quantity\",\n\"tx_nonce\",\n]\n)\nand proposal.values[\"ledger_id\"] == self.ledger_id\nand proposal.values[\"price\"] > 0\nand proposal.values[\"quantity\"] >= self._min_quantity\nand proposal.values[\"quantity\"] <= self._max_quantity\nand proposal.values[\"price\"]\n<= proposal.values[\"quantity\"] * self._max_unit_price\nand proposal.values[\"currency_id\"] == self._currency_id\nand proposal.values[\"service_id\"] == self._service_id\nand isinstance(proposal.values[\"tx_nonce\"], str)\nand proposal.values[\"tx_nonce\"] != \"\"\n)\nreturn result\n

The is_affordable_proposal method in the following code block checks if we can afford the transaction based on the funds we have in our wallet on the ledger. The rest of the methods are self-explanatory.

    def is_affordable_proposal(self, proposal: Description) -> bool:\n\"\"\"\n        Check whether it is an affordable proposal.\n        :param proposal: a description\n        :return: whether it is affordable\n        \"\"\"\nif self.is_ledger_tx:\npayable = proposal.values.get(\"price\", 0) + self._max_tx_fee\nresult = self.balance >= payable\nelse:\nresult = True\nreturn result\ndef get_acceptable_counterparties(\nself, counterparties: Tuple[str, ...]\n) -> Tuple[str, ...]:\n\"\"\"\n        Process counterparties and drop unacceptable ones.\n        :param counterparties: a tuple of counterparties\n        :return: list of counterparties\n        \"\"\"\nvalid_counterparties: List[str] = []\nfor idx, counterparty in enumerate(counterparties):\nif idx < self.max_negotiations:\nvalid_counterparties.append(counterparty)\nreturn tuple(valid_counterparties)\ndef terms_from_proposal(\nself, proposal: Description, counterparty_address: Address\n) -> Terms:\n\"\"\"\n        Get the terms from a proposal.\n        :param proposal: the proposal\n        :param counterparty_address: the counterparty\n        :return: terms\n        \"\"\"\nbuyer_address = self.context.agent_addresses[proposal.values[\"ledger_id\"]]\nterms = Terms(\nledger_id=proposal.values[\"ledger_id\"],\nsender_address=buyer_address,\ncounterparty_address=counterparty_address,\namount_by_currency_id={\nproposal.values[\"currency_id\"]: -proposal.values[\"price\"]\n},\nquantities_by_good_id={\nproposal.values[\"service_id\"]: proposal.values[\"quantity\"]\n},\nis_sender_payable_tx_fee=True,\nnonce=proposal.values[\"tx_nonce\"],\nfee_by_currency_id={proposal.values[\"currency_id\"]: self._max_tx_fee},\n)\nreturn terms\ndef successful_trade_with_counterparty(\nself, counterparty: str, data: Dict[str, str]\n) -> None:\n\"\"\"\n        Do something on successful trade.\n        :param counterparty: the counterparty address\n        :param data: the data\n        \"\"\"\ndef update_search_query_params(self) -> None:\n\"\"\"Update agent location and query for search.\"\"\"\n
"},{"location":"aea-framework-documentation/generic-skills-step-by-step/#step-5-create-the-dialogues_1","title":"Step 5: Create the Dialogues","text":"

As mentioned during the creation of the seller AEA, we should keep track of the various interactions an AEA has with others and this is done via dialogues. Create a new file and name it dialogues.py (in my_generic_buyer/skills/generic_buyer/). Inside this file add the following code:

from typing import Any, Optional, Type\nfrom aea.common import Address\nfrom aea.exceptions import AEAEnforceError, enforce\nfrom aea.helpers.transaction.base import Terms\nfrom aea.protocols.base import Message\nfrom aea.protocols.dialogue.base import Dialogue as BaseDialogue\nfrom aea.protocols.dialogue.base import DialogueLabel as BaseDialogueLabel\nfrom aea.skills.base import Model\nfrom packages.fetchai.protocols.default.dialogues import (\nDefaultDialogue as BaseDefaultDialogue,\n)\nfrom packages.fetchai.protocols.default.dialogues import (\nDefaultDialogues as BaseDefaultDialogues,\n)\nfrom packages.fetchai.protocols.fipa.dialogues import FipaDialogue as BaseFipaDialogue\nfrom packages.fetchai.protocols.fipa.dialogues import FipaDialogues as BaseFipaDialogues\nfrom packages.fetchai.protocols.fipa.message import FipaMessage\nfrom packages.fetchai.protocols.ledger_api.dialogues import (\nLedgerApiDialogue as BaseLedgerApiDialogue,\n)\nfrom packages.fetchai.protocols.ledger_api.dialogues import (\nLedgerApiDialogues as BaseLedgerApiDialogues,\n)\nfrom packages.fetchai.protocols.ledger_api.message import LedgerApiMessage\nfrom packages.fetchai.protocols.oef_search.dialogues import (\nOefSearchDialogue as BaseOefSearchDialogue,\n)\nfrom packages.fetchai.protocols.oef_search.dialogues import (\nOefSearchDialogues as BaseOefSearchDialogues,\n)\nfrom packages.fetchai.protocols.signing.dialogues import (\nSigningDialogue as BaseSigningDialogue,\n)\nfrom packages.fetchai.protocols.signing.dialogues import (\nSigningDialogues as BaseSigningDialogues,\n)\nfrom packages.fetchai.protocols.signing.message import SigningMessage\nDefaultDialogue = BaseDefaultDialogue\nclass DefaultDialogues(Model, BaseDefaultDialogues):\n\"\"\"The dialogues class keeps track of all dialogues.\"\"\"\ndef __init__(self, **kwargs: Any) -> None:\n\"\"\"\n        Initialize dialogues.\n        :param kwargs: keyword arguments\n        \"\"\"\nModel.__init__(self, **kwargs)\ndef role_from_first_message(  # pylint: disable=unused-argument\nmessage: Message, receiver_address: Address\n) -> BaseDialogue.Role:\n\"\"\"Infer the role of the agent from an incoming/outgoing first message\n            :param message: an incoming/outgoing first message\n            :param receiver_address: the address of the receiving agent\n            :return: The role of the agent\n            \"\"\"\nreturn DefaultDialogue.Role.AGENT\nBaseDefaultDialogues.__init__(\nself,\nself_address=self.context.agent_address,\nrole_from_first_message=role_from_first_message,\n)\nclass FipaDialogue(BaseFipaDialogue):\n\"\"\"The dialogue class maintains state of a dialogue and manages it.\"\"\"\n__slots__ = (\n\"_terms\",\n\"_associated_ledger_api_dialogue\",\n)\ndef __init__(\nself,\ndialogue_label: BaseDialogueLabel,\nself_address: Address,\nrole: BaseDialogue.Role,\nmessage_class: Type[FipaMessage] = FipaMessage,\n) -> None:\n\"\"\"\n        Initialize a dialogue.\n        :param dialogue_label: the identifier of the dialogue\n        :param self_address: the address of the entity for whom this dialogue is maintained\n        :param role: the role of the agent this dialogue is maintained for\n        :param message_class: the message class\n        \"\"\"\nBaseFipaDialogue.__init__(\nself,\ndialogue_label=dialogue_label,\nself_address=self_address,\nrole=role,\nmessage_class=message_class,\n)\nself._terms = None  # type: Optional[Terms]\n@property\ndef terms(self) -> Terms:\n\"\"\"Get terms.\"\"\"\nif self._terms is None:\nraise AEAEnforceError(\"Terms not set!\")\nreturn self._terms\n@terms.setter\ndef terms(self, terms: Terms) -> None:\n\"\"\"Set terms.\"\"\"\nenforce(self._terms is None, \"Terms already set!\")\nself._terms = terms\nclass FipaDialogues(Model, BaseFipaDialogues):\n\"\"\"The dialogues class keeps track of all dialogues.\"\"\"\ndef __init__(self, **kwargs: Any) -> None:\n\"\"\"\n        Initialize dialogues.\n        :param kwargs: keyword arguments\n        \"\"\"\nModel.__init__(self, **kwargs)\ndef role_from_first_message(  # pylint: disable=unused-argument\nmessage: Message, receiver_address: Address\n) -> BaseDialogue.Role:\n\"\"\"Infer the role of the agent from an incoming/outgoing first message\n            :param message: an incoming/outgoing first message\n            :param receiver_address: the address of the receiving agent\n            :return: The role of the agent\n            \"\"\"\nreturn BaseFipaDialogue.Role.BUYER\nBaseFipaDialogues.__init__(\nself,\nself_address=self.context.agent_address,\nrole_from_first_message=role_from_first_message,\ndialogue_class=FipaDialogue,\n)\nclass LedgerApiDialogue(BaseLedgerApiDialogue):\n\"\"\"The dialogue class maintains state of a dialogue and manages it.\"\"\"\n__slots__ = (\"_associated_fipa_dialogue\",)\ndef __init__(\nself,\ndialogue_label: BaseDialogueLabel,\nself_address: Address,\nrole: BaseDialogue.Role,\nmessage_class: Type[LedgerApiMessage] = LedgerApiMessage,\n) -> None:\n\"\"\"\n        Initialize a dialogue.\n        :param dialogue_label: the identifier of the dialogue\n        :param self_address: the address of the entity for whom this dialogue is maintained\n        :param role: the role of the agent this dialogue is maintained for\n        :param message_class: the message class\n        \"\"\"\nBaseLedgerApiDialogue.__init__(\nself,\ndialogue_label=dialogue_label,\nself_address=self_address,\nrole=role,\nmessage_class=message_class,\n)\nself._associated_fipa_dialogue = None  # type: Optional[FipaDialogue]\n@property\ndef associated_fipa_dialogue(self) -> FipaDialogue:\n\"\"\"Get associated_fipa_dialogue.\"\"\"\nif self._associated_fipa_dialogue is None:\nraise AEAEnforceError(\"FipaDialogue not set!\")\nreturn self._associated_fipa_dialogue\n@associated_fipa_dialogue.setter\ndef associated_fipa_dialogue(self, fipa_dialogue: FipaDialogue) -> None:\n\"\"\"Set associated_fipa_dialogue\"\"\"\nenforce(self._associated_fipa_dialogue is None, \"FipaDialogue already set!\")\nself._associated_fipa_dialogue = fipa_dialogue\nclass LedgerApiDialogues(Model, BaseLedgerApiDialogues):\n\"\"\"The dialogues class keeps track of all dialogues.\"\"\"\ndef __init__(self, **kwargs: Any) -> None:\n\"\"\"\n        Initialize dialogues.\n        :param kwargs: keyword arguments\n        \"\"\"\nModel.__init__(self, **kwargs)\ndef role_from_first_message(  # pylint: disable=unused-argument\nmessage: Message, receiver_address: Address\n) -> BaseDialogue.Role:\n\"\"\"Infer the role of the agent from an incoming/outgoing first message\n            :param message: an incoming/outgoing first message\n            :param receiver_address: the address of the receiving agent\n            :return: The role of the agent\n            \"\"\"\nreturn BaseLedgerApiDialogue.Role.AGENT\nBaseLedgerApiDialogues.__init__(\nself,\nself_address=str(self.skill_id),\nrole_from_first_message=role_from_first_message,\ndialogue_class=LedgerApiDialogue,\n)\nOefSearchDialogue = BaseOefSearchDialogue\nclass OefSearchDialogues(Model, BaseOefSearchDialogues):\n\"\"\"This class keeps track of all oef_search dialogues.\"\"\"\ndef __init__(self, **kwargs: Any) -> None:\n\"\"\"\n        Initialize dialogues.\n        :param kwargs: keyword arguments\n        \"\"\"\nModel.__init__(self, **kwargs)\ndef role_from_first_message(  # pylint: disable=unused-argument\nmessage: Message, receiver_address: Address\n) -> BaseDialogue.Role:\n\"\"\"Infer the role of the agent from an incoming/outgoing first message\n            :param message: an incoming/outgoing first message\n            :param receiver_address: the address of the receiving agent\n            :return: The role of the agent\n            \"\"\"\nreturn BaseOefSearchDialogue.Role.AGENT\nBaseOefSearchDialogues.__init__(\nself,\nself_address=str(self.skill_id),\nrole_from_first_message=role_from_first_message,\n)\nclass SigningDialogue(BaseSigningDialogue):\n\"\"\"The dialogue class maintains state of a dialogue and manages it.\"\"\"\n__slots__ = (\"_associated_ledger_api_dialogue\",)\ndef __init__(\nself,\ndialogue_label: BaseDialogueLabel,\nself_address: Address,\nrole: BaseDialogue.Role,\nmessage_class: Type[SigningMessage] = SigningMessage,\n) -> None:\n\"\"\"\n        Initialize a dialogue.\n        :param dialogue_label: the identifier of the dialogue\n        :param self_address: the address of the entity for whom this dialogue is maintained\n        :param role: the role of the agent this dialogue is maintained for\n        :param message_class: the message class\n        \"\"\"\nBaseSigningDialogue.__init__(\nself,\ndialogue_label=dialogue_label,\nself_address=self_address,\nrole=role,\nmessage_class=message_class,\n)\nself._associated_ledger_api_dialogue = None  # type: Optional[LedgerApiDialogue]\n@property\ndef associated_ledger_api_dialogue(self) -> LedgerApiDialogue:\n\"\"\"Get associated_ledger_api_dialogue.\"\"\"\nif self._associated_ledger_api_dialogue is None:\nraise AEAEnforceError(\"LedgerApiDialogue not set!\")\nreturn self._associated_ledger_api_dialogue\n@associated_ledger_api_dialogue.setter\ndef associated_ledger_api_dialogue(\nself, ledger_api_dialogue: LedgerApiDialogue\n) -> None:\n\"\"\"Set associated_ledger_api_dialogue\"\"\"\nenforce(\nself._associated_ledger_api_dialogue is None,\n\"LedgerApiDialogue already set!\",\n)\nself._associated_ledger_api_dialogue = ledger_api_dialogue\nclass SigningDialogues(Model, BaseSigningDialogues):\n\"\"\"This class keeps track of all oef_search dialogues.\"\"\"\ndef __init__(self, **kwargs: Any) -> None:\n\"\"\"\n        Initialize dialogues.\n        :param kwargs: keyword arguments\n        \"\"\"\nModel.__init__(self, **kwargs)\ndef role_from_first_message(  # pylint: disable=unused-argument\nmessage: Message, receiver_address: Address\n) -> BaseDialogue.Role:\n\"\"\"Infer the role of the agent from an incoming/outgoing first message\n            :param message: an incoming/outgoing first message\n            :param receiver_address: the address of the receiving agent\n            :return: The role of the agent\n            \"\"\"\nreturn BaseSigningDialogue.Role.SKILL\nBaseSigningDialogues.__init__(\nself,\nself_address=str(self.skill_id),\nrole_from_first_message=role_from_first_message,\ndialogue_class=SigningDialogue,\n)\n

The various dialogues classes in the above code snippet store dialogues with other AEAs, services and components, (e.g. SOEF search node via the fetchai/soef connection, ledgers via the fetchai/ledger connection and the decision maker). They expose useful methods to manipulate these interactions, access previous messages, and enable us to identify possible communications problems between my_generic_seller and my_generic_buyer AEAs.

"},{"location":"aea-framework-documentation/generic-skills-step-by-step/#step-6-update-the-yaml-files_1","title":"Step 6: Update the YAML Files","text":"

After making so many changes to our skill, we have to update the skill.yaml configuration file so it reflects our newly created classes, and contains the values used by the strategy. Make sure skill.yaml contains the following configuration:

name: generic_buyer\nauthor: fetchai\nversion: 0.1.0\ntype: skill\ndescription: The weather client skill implements the skill to purchase weather data.\nlicense: Apache-2.0\naea_version: '>=1.0.0, <2.0.0'\nfingerprint:\nREADME.md: QmTR91jm7WfJpmabisy74NR5mc35YXjDU1zQAUKZeHRw8L\n__init__.py: QmU5vrC8FipyjfS5biNa6qDWdp4aeH5h4YTtbFDmCg8Chj\nbehaviours.py: QmNwvSjEz4kzM3gWtnKbZVFJc2Z85Nb748CWAK4C4Sa4nT\ndialogues.py: QmNen91qQDWy4bNBKrB3LabAP5iRf29B8iwYss4NB13iNU\nhandlers.py: QmZfudXXbdiREiViuwPZDXoQQyXT2ySQHdF7psQsohZXQy\nstrategy.py: QmcrwaEWvKHDCNti8QjRhB4utJBJn5L8GpD27Uy9zHwKhY\nfingerprint_ignore_patterns: []\nconnections:\n- fetchai/ledger:0.21.5\ncontracts: []\nprotocols:\n- fetchai/default:1.1.7\n- fetchai/fipa:1.1.7\n- fetchai/ledger_api:1.1.7\n- fetchai/oef_search:1.1.7\n- fetchai/signing:1.1.7\nskills: []\nbehaviours:\nsearch:\nargs:\nsearch_interval: 5\nclass_name: GenericSearchBehaviour\ntransaction:\nargs:\nmax_processing: 420\ntransaction_interval: 2\nclass_name: GenericTransactionBehaviour\nhandlers:\nfipa:\nargs: {}\nclass_name: GenericFipaHandler\nledger_api:\nargs: {}\nclass_name: GenericLedgerApiHandler\noef_search:\nargs: {}\nclass_name: GenericOefSearchHandler\nsigning:\nargs: {}\nclass_name: GenericSigningHandler\nmodels:\ndefault_dialogues:\nargs: {}\nclass_name: DefaultDialogues\nfipa_dialogues:\nargs: {}\nclass_name: FipaDialogues\nledger_api_dialogues:\nargs: {}\nclass_name: LedgerApiDialogues\noef_search_dialogues:\nargs: {}\nclass_name: OefSearchDialogues\nsigning_dialogues:\nargs: {}\nclass_name: SigningDialogues\nstrategy:\nargs:\nis_ledger_tx: true\nlocation:\nlatitude: 51.5194\nlongitude: 0.127\nmax_negotiations: 1\nmax_tx_fee: 3550000000000000\nmax_unit_price: 20\nmin_quantity: 1\nsearch_query:\nconstraint_type: ==\nsearch_key: seller_service\nsearch_value: generic_service\nsearch_radius: 5.0\nservice_id: generic_service\nstop_searching_on_result: true\nclass_name: GenericStrategy\nis_abstract: false\ndependencies: {}\n

We must pay attention to the models and the strategy\u2019s variables. Here we can change the price we would like to buy each reading at, the maximum transaction fee we are prepared to pay, and so on.

Finally, we fingerprint our new skill:

aea fingerprint skill fetchai/generic_buyer:0.1.0\n

This will hash each file and save the hash in the fingerprint. This way, in the future we can easily track if any of the files have changed.

"},{"location":"aea-framework-documentation/generic-skills-step-by-step/#run-the-aeas","title":"Run the AEAs","text":""},{"location":"aea-framework-documentation/generic-skills-step-by-step/#create-private-keys","title":"Create Private Keys","text":"

For each AEA, create a private key:

aea generate-key fetchai\naea add-key fetchai fetchai_private_key.txt\n

Next, create a private key to secure the AEA's communications:

aea generate-key fetchai fetchai_connection_private_key.txt\naea add-key fetchai fetchai_connection_private_key.txt --connection\n

Finally, certify the key for use by the connections that request that:

aea issue-certificates\n
"},{"location":"aea-framework-documentation/generic-skills-step-by-step/#update-the-aea-configurations","title":"Update the AEA Configurations","text":"

In both AEAs run:

aea config set --type dict agent.default_routing \\\n'{\n  \"fetchai/ledger_api:1.1.7\": \"fetchai/ledger:0.21.5\",\n  \"fetchai/oef_search:1.1.7\": \"fetchai/soef:0.27.6\"\n}'\n
"},{"location":"aea-framework-documentation/generic-skills-step-by-step/#fund-the-buyer-aea","title":"Fund the Buyer AEA","text":"

Create some wealth for your buyer on the Fetch.ai testnet (this operation might take a while).

aea generate-wealth fetchai --sync\n
"},{"location":"aea-framework-documentation/generic-skills-step-by-step/#run-seller-aea","title":"Run Seller AEA","text":"

Add the remaining packages for the seller AEA, then run it:

aea add connection fetchai/p2p_libp2p:0.27.5\naea add connection fetchai/soef:0.27.6\naea add connection fetchai/ledger:0.21.5\naea add protocol fetchai/fipa:1.1.7\naea install\naea build\naea config set agent.default_connection fetchai/p2p_libp2p:0.27.5\naea run\n

Once you see a message of the form To join its network use multiaddr: ['SOME_ADDRESS'] take note of the address.

"},{"location":"aea-framework-documentation/generic-skills-step-by-step/#run-buyer-aea","title":"Run Buyer AEA","text":"

Add the remaining packages for the buyer AEA:

aea add connection fetchai/p2p_libp2p:0.27.5\naea add connection fetchai/soef:0.27.6\naea add connection fetchai/ledger:0.21.5\naea add protocol fetchai/fipa:1.1.7\naea add protocol fetchai/signing:1.1.7\naea install\naea build\naea config set agent.default_connection fetchai/p2p_libp2p:0.27.5\n

Then, update the configuration of the buyer AEA's P2P connection:

aea config set --type dict vendor.fetchai.connections.p2p_libp2p.config \\\n'{\n  \"delegate_uri\": \"127.0.0.1:11001\",\n  \"entry_peers\": [\"SOME_ADDRESS\"],\n  \"local_uri\": \"127.0.0.1:9001\",\n  \"log_file\": \"libp2p_node.log\",\n  \"public_uri\": \"127.0.0.1:9001\"\n}'\n

where SOME_ADDRESS is replaced accordingly.

Then run the buyer AEA:

aea run\n

You will see that the AEAs negotiate and then transact using the Dorado testnet.

"},{"location":"aea-framework-documentation/generic-skills-step-by-step/#delete-the-aeas","title":"Delete the AEAs","text":"

When you are done, go up a level and delete the AEAs.

cd ..\naea delete my_generic_seller\naea delete my_generic_buyer\n
"},{"location":"aea-framework-documentation/generic-skills-step-by-step/#next-steps","title":"Next Steps","text":"

You have completed the \"Getting Started\" series. Congratulations!

The following guide provides some hints on AEA development setup.

"},{"location":"aea-framework-documentation/generic-skills-step-by-step/#recommended","title":"Recommended","text":"

We recommend you build your own AEA next. There are many helpful guides and demos in the documentation, and a developer community on Discord. Speak to you there!

"},{"location":"aea-framework-documentation/generic-skills/","title":"Generic Skills","text":"

The AEA generic buyer and seller skills demonstrate an interaction between two AEAs:

  • An AEA that provides a (data selling) service.
  • An AEA that demands this service.
"},{"location":"aea-framework-documentation/generic-skills/#discussion","title":"Discussion","text":"

The scope of this guide is demonstrating how to create easily configurable AEAs. The buyer AEA finds the seller, negotiates the terms of trade, and if successful purchases the data by sending payment. The seller AEA sells the service specified in its skill.yaml file, delivering it to the buyer upon receiving payment.

Note that these agents do not utilize a smart contract but interact with a ledger to complete a transaction. Moreover, in this setup, the buyer agent has to trust the seller to send the data upon successful payment.

The corresponding packages can be customised to allow for a database or sensor to be defined from which data is loaded. This is done by first modifying the has_data_source variable in skill.yaml file of the generic_seller skill to True. Then you have to provide an implementation for the collect_from_data_source(self) method in the strategy.py file. More detailed instructions is beyond the scope of this guide.

"},{"location":"aea-framework-documentation/generic-skills/#communication","title":"Communication","text":"

The following diagram shows the communication between various entities in this interaction.

    sequenceDiagram\n        participant Search\n        participant Buyer_AEA\n        participant Seller_AEA\n        participant Blockchain\n\n        activate Buyer_AEA\n        activate Search\n        activate Seller_AEA\n        activate Blockchain\n\n        Seller_AEA->>Search: register_service\n        Buyer_AEA->>Search: search_agents\n        Search-->>Buyer_AEA: list_of_agents\n        Buyer_AEA->>Seller_AEA: call_for_proposal\n        Seller_AEA->>Buyer_AEA: propose\n        Buyer_AEA->>Seller_AEA: accept\n        Seller_AEA->>Buyer_AEA: match_accept\n        Buyer_AEA->>Blockchain: transfer_funds\n        Buyer_AEA->>Seller_AEA: send_transaction_hash\n        Seller_AEA->>Blockchain: check_transaction_status\n        Seller_AEA->>Buyer_AEA: send_data\n\n        deactivate Buyer_AEA\n        deactivate Search\n        deactivate Seller_AEA\n        deactivate Blockchain 
"},{"location":"aea-framework-documentation/generic-skills/#preparation-instructions","title":"Preparation Instructions","text":""},{"location":"aea-framework-documentation/generic-skills/#dependencies","title":"Dependencies","text":"

Follow the Preliminaries and Installation sections from the AEA quick start.

"},{"location":"aea-framework-documentation/generic-skills/#demo-instructions","title":"Demo Instructions","text":""},{"location":"aea-framework-documentation/generic-skills/#create-the-seller-aea","title":"Create the Seller AEA","text":"

First, fetch the seller AEA:

aea fetch fetchai/generic_seller:0.29.5 --alias my_seller_aea\ncd my_seller_aea\naea install\naea build\n
Alternatively, create from scratch:

The following steps create the seller from scratch:

aea create my_seller_aea\ncd my_seller_aea\naea add connection fetchai/p2p_libp2p:0.27.5\naea add connection fetchai/soef:0.27.6\naea add connection fetchai/ledger:0.21.5\naea add skill fetchai/generic_seller:0.28.6\naea config set --type dict agent.dependencies \\\n'{\n  \"aea-ledger-fetchai\": {\"version\": \"<2.0.0,>=1.0.0\"}\n}'\naea config set agent.default_connection fetchai/p2p_libp2p:0.27.5\naea config set --type dict agent.default_routing \\\n'{\n  \"fetchai/ledger_api:1.1.7\": \"fetchai/ledger:0.21.5\",\n  \"fetchai/oef_search:1.1.7\": \"fetchai/soef:0.27.6\"\n}'\naea install\naea build\n
"},{"location":"aea-framework-documentation/generic-skills/#create-the-buyer-aea","title":"Create the Buyer AEA","text":"

Then, in another terminal fetch the buyer AEA:

aea fetch fetchai/generic_buyer:0.30.5 --alias my_buyer_aea\ncd my_buyer_aea\naea install\naea build\n
Alternatively, create from scratch:

The following steps create the buyer from scratch:

aea create my_buyer_aea\ncd my_buyer_aea\naea add connection fetchai/p2p_libp2p:0.27.5\naea add connection fetchai/soef:0.27.6\naea add connection fetchai/ledger:0.21.5\naea add skill fetchai/generic_buyer:0.27.6\naea config set --type dict agent.dependencies \\\n'{\n  \"aea-ledger-fetchai\": {\"version\": \"<2.0.0,>=1.0.0\"}\n}'\naea config set agent.default_connection fetchai/p2p_libp2p:0.27.5\naea config set --type dict agent.default_routing \\\n'{\n  \"fetchai/ledger_api:1.1.7\": \"fetchai/ledger:0.21.5\",\n  \"fetchai/oef_search:1.1.7\": \"fetchai/soef:0.27.6\"\n}'\naea install\naea build\n
"},{"location":"aea-framework-documentation/generic-skills/#add-keys-for-the-seller-aea","title":"Add Keys for the Seller AEA","text":"

Create the private key for the seller AEA based on the network you want to transact. To generate and add a private-public key pair for Fetch.ai Dorado use:

aea generate-key fetchai\naea add-key fetchai fetchai_private_key.txt\n

Next, create a private key used to secure the AEA's communications:

aea generate-key fetchai fetchai_connection_private_key.txt\naea add-key fetchai fetchai_connection_private_key.txt --connection\n

Finally, certify the key for use by the connections that request that:

aea issue-certificates\n
"},{"location":"aea-framework-documentation/generic-skills/#add-keys-and-generate-wealth-for-the-buyer-aea","title":"Add Keys and Generate Wealth for the Buyer AEA","text":"

The buyer needs to have some wealth to purchase the data from the seller.

First, create the private key for the buyer AEA based on the network you want to transact. To generate and add a private-public key pair for Fetch.ai Dorado use:

aea generate-key fetchai\naea add-key fetchai fetchai_private_key.txt\n

Then, create some wealth for your buyer based on the network you want to transact with. On the Fetch.ai Dorado network:

aea generate-wealth fetchai\n

Next, create a private key used to secure the AEA's communications:

aea generate-key fetchai fetchai_connection_private_key.txt\naea add-key fetchai fetchai_connection_private_key.txt --connection\n

Finally, certify the key for use by the connections that request that:

aea issue-certificates\n
"},{"location":"aea-framework-documentation/generic-skills/#update-the-skill-configurations","title":"Update the Skill Configurations","text":"

The default skill configurations assume that the transaction is settled against the Fetch.ai ledger.

In the generic seller's skill configuration file (my_seller_aea/vendor/fetchai/skills/generi_seller/skill.yaml) the data_for_sale is the data the seller AEA is offering for sale. In the following case, this is a one item dictionary where key is generic and value is data.

Furthermore, the service_data is used to register the seller's service in the SOEF search node and make your agent discoverable.

models:\n...\nstrategy:\nargs:\ncurrency_id: FET\ndata_for_sale:\ngeneric: data\nhas_data_source: false\nis_ledger_tx: true\nledger_id: fetchai\nlocation:\nlatitude: 51.5194\nlongitude: 0.127\nservice_data:\nkey: seller_service\nvalue: generic_service\nservice_id: generic_service\nunit_price: 10\nclass_name: GenericStrategy\n

The generic buyer skill configuration file (my_buyer_aea/vendor/fetchai/skills/generic_buyer/skill.yaml) includes the search_query which has to match the service_data of the seller.

models:\n...\nstrategy:\nargs:\ncurrency_id: FET\nis_ledger_tx: true\nledger_id: fetchai\nlocation:\nlatitude: 51.5194\nlongitude: 0.127\nmax_negotiations: 1\nmax_tx_fee: 3550000000000000\nmax_unit_price: 20\nsearch_query:\nconstraint_type: ==\nsearch_key: seller_service\nsearch_value: generic_service\nsearch_radius: 5.0\nservice_id: generic_service\nclass_name: GenericStrategy\n
"},{"location":"aea-framework-documentation/generic-skills/#update-the-skill-configurations_1","title":"Update the Skill Configurations","text":"

Both skills are abstract skills, make them instantiable:

cd my_seller_aea\naea config set vendor.fetchai.skills.generic_seller.is_abstract false --type bool\n
cd my_buyer_aea\naea config set vendor.fetchai.skills.generic_buyer.is_abstract false --type bool\n
"},{"location":"aea-framework-documentation/generic-skills/#run-the-aeas","title":"Run the AEAs","text":"

First, run the seller AEA:

aea run\n

Once you see a message of the form To join its network use multiaddr 'SOME_ADDRESS' take note of this address. (Alternatively, use aea get-multiaddress fetchai -c -i fetchai/p2p_libp2p:0.27.5 -u public_uri to retrieve the address.) This is the entry peer address for the local agent communication network created by the seller.

Then, configure the buyer to connect to this same local ACN by running the following command in the buyer terminal, replacing SOME_ADDRESS with the value you noted above:

aea config set --type dict vendor.fetchai.connections.p2p_libp2p.config \\\n'{\n  \"delegate_uri\": \"127.0.0.1:11001\",\n  \"entry_peers\": [\"SOME_ADDRESS\"],\n  \"local_uri\": \"127.0.0.1:9001\",\n  \"log_file\": \"libp2p_node.log\",\n  \"public_uri\": \"127.0.0.1:9001\"\n}'\n

Then run the buyer AEA:

aea run\n

You will see that the AEAs negotiate and then transact using the Fetch.ai testnet.

"},{"location":"aea-framework-documentation/generic-skills/#delete-the-aeas","title":"Delete the AEAs","text":"

When you're done, stop the agents (CTRL+C), go up a level and delete the AEAs.

cd ..\naea delete my_seller_aea\naea delete my_buyer_aea\n
"},{"location":"aea-framework-documentation/generic-storage/","title":"Generic Storage","text":"

The AEA generic storage: description and usage.

"},{"location":"aea-framework-documentation/generic-storage/#aea-generic-storage","title":"AEA Generic Storage","text":"

AEA generic storage allows AEA skill's components to store data permanently and use it any time. The primary scenario: to save AEA data on shutdown and load back on startup. Generic storage provides an API for general data manipulation in key-object style.

"},{"location":"aea-framework-documentation/generic-storage/#configuration","title":"Configuration","text":"

Storage is enabled by providing in the agent configuration (aea-config.yaml) an optional storage_uri. The storage URI consists of the backend name and string data provided to selected backend.

The storage URI schema is <BACKEND_NAME>://[Optional string] Example: storage_uri: sqlite://./some_file.db tells the AEA to use SQLite backend and store data in ./some_file.db.

Supported backends:

  • SQLite - bundled with python simple SQL engine that uses file or in-memory storage.
"},{"location":"aea-framework-documentation/generic-storage/#dialogues-and-storage-integration","title":"Dialogues and Storage Integration","text":"

One of the most useful cases is the integration of the dialogues subsystem and storage. It helps maintain dialogues state during agent restarts and reduced memory requirements due to the offloading feature.

"},{"location":"aea-framework-documentation/generic-storage/#keep-terminal-state-dialogues","title":"Keep Terminal State Dialogues","text":"

The Dialogues class has the optional boolean argument keep_terminal_state_dialogues which specifies whether a dialogue which has reached its terminal state is kept in memory or not. If keep_terminal_state_dialogues is False, dialogues that reach a terminal state are removed from memory and can not be used anymore. If keep_terminal_state_dialogues is True, dialogues that reach a terminal state are kept in memory or storage (if configured). If storage is configured, all dialogues in memory are stored on agent stop and restored on agent start.

It is useful to save memory with terminated dialogues that will (possibly) be never used again.

Default behaviour on keep terminals state dialogues is set according to the protocol specification but can be set explicitly with skill configuration section.

Skill configuration to keep terminated dialogues for DefaultDialogues. Example:

"},{"location":"aea-framework-documentation/generic-storage/#dialogues-dumprestore-on-agent-restart","title":"Dialogues Dump/Restore on Agent Restart","text":"

If storage is enabled then all the dialogues present in memory will be stored on agent's teardown and loaded on agent's start.

"},{"location":"aea-framework-documentation/generic-storage/#offload-terminal-state-dialogues","title":"Offload Terminal State Dialogues","text":"

If keep options is set and storage is available dialogues in terminal state will be dumped to generic storage and removed from memory. This option helps to save memory and handle terminated dialogues with the same functionality as when they are kept in memory.

All the active dialogues will be stored and loaded during agent restart. All the terminated offloaded dialogues will stay in storage on agent restart.

To enable dialogues offloading keep_terminal_state_dialogues has to be enabled and storage configured.

"},{"location":"aea-framework-documentation/generic-storage/#manual-usage-with-skill-components","title":"Manual Usage with Skill Components","text":"

Handlers, Behaviours and Models are able to use storage if enabled.

Storage is available with skill context: self.context.storage if self.context.storage is not None, storage is enabled and ready to use.

Generic storage consists of two parts: objects and collections. Objects consist of the object_id (unique string) and object body. The object body is any JSON friendly python data type: list, dict, int, float, string, bool.

Collection is a group of the objects, objects data types can vary in the same collection. Collection name is name consists of letters, numbers and _.

To get/put specific object collection instance should be used.

my_collection = self.context.storage.get_sync_connection('my_collection')\n

Collection instance provide set of methods to handle data objects. List of collection methods:

    def put(self, object_id: str, object_body: JSON_TYPES) -> None:\n\"\"\"\n        Put object into collection.\n        :param object_id: str object id\n        :param object_body: python dict, json compatible.\n        :return: None\n        \"\"\"\ndef get(self, object_id: str) -> Optional[JSON_TYPES]:\n\"\"\"\n        Get object from the collection.\n        :param object_id: str object id\n        :return: dict if object exists in collection otherwise None\n        \"\"\"\ndef remove(self, object_id: str) -> None:\n\"\"\"\n        Remove object from the collection.\n        :param object_id: str object id\n        :return: None\n        \"\"\"\ndef find(self, field: str, equals: EQUALS_TYPE) -> List[OBJECT_ID_AND_BODY]:\n\"\"\"\n        Get objects from the collection by filtering by field value.\n        :param field: field name to search: example \"parent.field\"\n        :param equals: value field should be equal to\n        :return: List of object bodies\n        \"\"\"\ndef list(self) -> List[OBJECT_ID_AND_BODY]:\n\"\"\"\n        List all objects with keys from the collection.\n        :return: Tuple of objects keys, bodies.\n        \"\"\"\n

Simple behaviour example:

It saves the datetime string of the first act and print it to stdout.

class TestBehaviour(TickerBehaviour):\n\"\"\"Simple behaviour to count how many acts were called.\"\"\"\ndef setup(self) -> None:\n\"\"\"Set up behaviour.\"\"\"\ndef act(self) -> None:\n\"\"\"Make an action.\"\"\"\nif not (self.context.storage and self.context.storage.is_connected):\nreturn\ncollection = self.context.storage.get_sync_collection('my_collection')\nfirst_call_datetime = collection.get(\"first_call_ts\")\nif not first_call_ts:\n# there is no object with \"first_call_ts\" id.\nfirst_call_datetime = str(datetime.datetime.now())\ncol.put(first_call_ts, first_call_datetime)\nprint(\"Act was called for the first time on:\", first_call_datetime)\n

Please, pay attention: datetime object is not JSON friendly and can not be stored directly. it should be transformed to timestamp or string before put into the storage.

"},{"location":"aea-framework-documentation/glossary/","title":"Glossary","text":"

This glossary defines a number of terms commonly used across the documentation. For the definitions of framework components consult the API docs.

  • AEA (Autonomous Economic Agent): An AEA is \"an intelligent agent acting on an owner's behalf, with limited or no interference, and whose goal is to generate economic value to its owner\". AEAs are a special type of agent. [more]

  • Software Agent: a software agent is a computer program that acts on behalf of an entity (e.g. individual, organisation, business). [more]

  • sOEF (Simple Open Economic Framework): The simple-OEF, or sOEF, is a search and discovery service for autonomous economic agents. [more]

  • ACN (Agent Communication Network): The ACN is a peer-to-peer communication network for autonomous economic agents. [more]

"},{"location":"aea-framework-documentation/gym-example/","title":"Gym Example","text":"

The gym example demonstrates the AEA framework's flexibility with respect to Reinforcement Learning using OpenAI's gym framework.

"},{"location":"aea-framework-documentation/gym-example/#discussion","title":"Discussion","text":"

There is no immediate use case for this example as you can train an RL agent without the AEA proxy layer just fine (and faster).

However, the example decouples the RL agent from the gym.Env allowing them to run in separate execution environments, potentially owned by different entities.

"},{"location":"aea-framework-documentation/gym-example/#preparation-instructions","title":"Preparation Instructions","text":""},{"location":"aea-framework-documentation/gym-example/#dependencies","title":"Dependencies","text":"

Follow the Preliminaries and Installation sections from the AEA quick start.

Download the necessary directories into your working directory:

svn export https://github.com/fetchai/agents-aea.git/trunk/examples\nsvn export https://github.com/fetchai/agents-aea.git/trunk/packages\n

Install the gym and numpy library.

pip install numpy gym\n
"},{"location":"aea-framework-documentation/gym-example/#demo-instructions","title":"Demo Instructions","text":""},{"location":"aea-framework-documentation/gym-example/#run-the-example","title":"Run the Example","text":"
python examples/gym_ex/train.py\n

Notice the usual RL setup, i.e. the fit method of the RL agent has the typical signature and a familiar implementation.

Note how train.py demonstrates how easy it is to use an AEA agent as a proxy layer between an OpenAI gym.Env and a standard RL agent.

It is just one line of code to introduce the proxy agent and proxy environment!

from gyms.env import BanditNArmedRandom\nfrom proxy.env import ProxyEnv\nfrom rl.agent import RLAgent\nif __name__ == \"__main__\":\nNB_GOODS = 10\nNB_PRICES_PER_GOOD = 100\nNB_STEPS = 4000\n# Use any gym.Env compatible environment:\ngym_env = BanditNArmedRandom(nb_bandits=NB_GOODS, nb_prices_per_bandit=NB_PRICES_PER_GOOD)\n# Pass the gym environment to a proxy environment:\nproxy_env = ProxyEnv(gym_env)\n# Use any RL agent compatible with the gym environment and call the fit method:\nrl_agent = RLAgent(nb_goods=NB_GOODS)\nrl_agent.fit(env=proxy_env, nb_steps=NB_STEPS)\n
"},{"location":"aea-framework-documentation/gym-skill/","title":"Gym Skill","text":"

The AEA gym skill demonstrates how a custom Reinforcement Learning agent, that uses OpenAI's gym library, may be embedded into an AEA skill and connection.

"},{"location":"aea-framework-documentation/gym-skill/#discussion","title":"Discussion","text":"

The gym skills demonstrate how to wrap a Reinforcement Learning agent in a skill. The example decouples the RL agent from the gym.Env allowing them to run in separate execution environments, potentially owned by different entities.

"},{"location":"aea-framework-documentation/gym-skill/#preparation-instructions","title":"Preparation Instructions","text":""},{"location":"aea-framework-documentation/gym-skill/#dependencies","title":"Dependencies","text":"

Follow the Preliminaries and Installation sections from the AEA quick start.

Download the necessary directories into your working directory:

mkdir gym_skill_agent\nsvn export https://github.com/fetchai/agents-aea.git/trunk/examples\n

Install the gym and numpy library.

pip install numpy gym\n
"},{"location":"aea-framework-documentation/gym-skill/#demo-instructions","title":"Demo Instructions","text":""},{"location":"aea-framework-documentation/gym-skill/#create-the-aea","title":"Create the AEA","text":"

First, fetch the gym AEA:

aea fetch fetchai/gym_aea:0.26.5 --alias my_gym_aea\ncd my_gym_aea\naea install\n
Alternatively, create from scratch:"},{"location":"aea-framework-documentation/gym-skill/#create-the-aea_1","title":"Create the AEA","text":"

In the root directory, create the gym AEA and enter the project.

aea create my_gym_aea\ncd my_gym_aea\n
"},{"location":"aea-framework-documentation/gym-skill/#add-the-gym-skill","title":"Add the gym skill","text":"
aea add skill fetchai/gym:0.21.6\n
"},{"location":"aea-framework-documentation/gym-skill/#set-gym-connection-as-default","title":"Set gym connection as default","text":"
aea config set agent.default_connection fetchai/gym:0.20.6\n
"},{"location":"aea-framework-documentation/gym-skill/#install-the-skill-dependencies","title":"Install the skill dependencies","text":"

To install the gym package, a dependency of the gym skill, from PyPI run

aea install\n
"},{"location":"aea-framework-documentation/gym-skill/#set-up-the-training-environment","title":"Set up the Training Environment","text":""},{"location":"aea-framework-documentation/gym-skill/#copy-the-gym-environment-to-the-aea-directory","title":"Copy the Gym Environment to the AEA Directory","text":"
mkdir gyms\ncp -a ../examples/gym_ex/gyms/. gyms/\n
"},{"location":"aea-framework-documentation/gym-skill/#update-the-connection-configuration","title":"Update the Connection Configuration","text":"
aea config set vendor.fetchai.connections.gym.config.env 'gyms.env.BanditNArmedRandom'\n
"},{"location":"aea-framework-documentation/gym-skill/#create-and-add-a-private-key","title":"Create and Add a Private Key","text":"
aea generate-key fetchai\naea add-key fetchai\n
"},{"location":"aea-framework-documentation/gym-skill/#run-the-aea-with-the-gym-connection","title":"Run the AEA with the Gym Connection","text":"
aea run\n

You will see the gym training logs.

"},{"location":"aea-framework-documentation/gym-skill/#delete-the-aea","title":"Delete the AEA","text":"

When you're done, you can go up a level and delete the AEA.

cd ..\naea delete my_gym_aea\n
"},{"location":"aea-framework-documentation/gym-skill/#communication","title":"Communication","text":"

This diagram shows the communication between the AEA and the gym environment

    sequenceDiagram\n        participant AEA\n        participant Environment\n\n        activate AEA\n        activate Environment\n        AEA->>Environment: reset\n        loop learn\n            AEA->>Environment: act\n            Environment->>AEA: percept\n        end\n        AEA->>Environment: close\n\n        deactivate AEA\n        deactivate Environment
"},{"location":"aea-framework-documentation/gym-skill/#skill-architecture","title":"Skill Architecture","text":"

The skill consists of two core components: GymHandler and GymTask.

In the setup method of the GymHandler the GymTask is initialized, as well as its setup and execute methods called. The handler, which is registered against the GymMessage.protocol_id then filters for messages of that protocol with the performative GymMessage.Performative.PERCEPT. These messages are passed to the proxy_env_queue of the task.

The GymTask is responsible for training the RL agent. In particular, MyRLAgent is initialized and trained against ProxyEnv. The ProxyEnv instantiates a gym.Env class and therefore implements its API. This means the proxy environment is compatible with any gym compatible RL agent. However, unlike other environments it only acts as a proxy and does not implement an environment of its own. It allows for the decoupling of the process environment of the gym.env from the process environment of the RL agent. The actual gym.env against which the agent is trained is wrapped by the gym connection. The proxy environment and gym connection communicate via a protocol, the gym protocol. Note, it would trivially be possible to implement the gym environment in another AEA; this way one AEA could provide gym environments as a service. Naturally, the overhead created by the introduction of the extra layers causes a higher latency when training the RL agent.

In this particular skill, which chiefly serves for demonstration purposes, we implement a very basic RL agent. The agent trains a model of price of n goods: it aims to discover the most likely price of each good. To this end, the agent randomly selects one of the n goods on each training step and then chooses as an action the price which it deems is most likely accepted. Each good is represented by an id and the possible price range [1,100] divided into 100 integer bins. For each price bin, a PriceBandit is created which models the likelihood of this price. In particular, a price bandit maintains a beta distribution. The beta distribution is initialized to the uniform distribution. Each time the price associated with a given PriceBandit is accepted or rejected the distribution maintained by the PriceBandit is updated. For each good, the agent can therefore over time learn which price is most likely.

The illustration shows how the RL agent only interacts with the proxy environment by sending it action (A) and receiving observation (O), reward (R), done (D) and info (I).

"},{"location":"aea-framework-documentation/http-connection-and-skill/","title":"HTTP Connection","text":""},{"location":"aea-framework-documentation/http-connection-and-skill/#description","title":"Description","text":"

The HTTP client and HTTP server connections enable an AEA to communicate with external servers, respectively clients, via HTTP.

The HTTP client connection receives request envelops from an agent's skill, translates each into an HTTP request and sends it to a server external to the agent. If it receives an HTTP response from the server within a timeout window, it translates it into a response envelope, and sends this back to the relevant skill inside the agent.

The HTTP server connection allows you to run a server inside the connection itself which accepts requests from clients external to the agent. The HTTP server connection validates requests it receives against a provided OpenAPI file. It translates each valid request into an envelope and sends it to the skill specified in the connections configuration. If it receives a valid response envelope from the skill within a timeout window, the connection translates the response envelope into an HTTP response and serves it to the client.

"},{"location":"aea-framework-documentation/http-connection-and-skill/#http-client","title":"HTTP Client","text":"

The fetchai/simple_data_request:0.14.6 skill demonstrates a simple use case of the HTTP Client connection.

The HttpRequestBehaviour in behaviours.py periodically sends HTTP envelops to the HTTP client connection. Its act() method, periodically called, simply calls _generate_http_request which contains the logic for enqueueing an HTTP request envelop.

The HttpHandler in handler.py is a basic handler for dealing with HTTP response envelops received from the HTTP client connection. In the handle() method, the responses are dealt with by the private _handle_response method which essentially logs the response and adds the body of the response into the skill's shared state.

"},{"location":"aea-framework-documentation/http-connection-and-skill/#http-server","title":"HTTP Server","text":"

Create a new AEA:

aea create my_aea\ncd my_aea\n

Add the http server connection package:

aea add connection fetchai/http_server:0.23.6\n

Update the default connection:

aea config set agent.default_connection fetchai/http_server:0.23.6\n

Modify the api_spec_path:

aea config set vendor.fetchai.connections.http_server.config.api_spec_path \"../examples/http_ex/petstore.yaml\"\n

Ensure the file exists under the specified path!

Create and add a private key:

aea generate-key fetchai\naea add-key fetchai\n

Install the dependencies:

aea install\n

Write and add your skill:

aea scaffold skill http_echo\n

You can implement a simple http echo skill (modelled after the standard echo skill) which prints out the content of received messages and responds with success.

First, delete the my_model.py and behaviour.py files (in my_aea/skills/http_echo/). The server will be purely reactive, so you only need the handlers.py file, and the dialogues.py to record the state of the dialogues. Update skill.yaml accordingly, so set models: {} and behaviours: {}.

Next implement a basic handler which prints the received envelopes and responds.

Then, replace the content of handlers.py with the following code snippet, after having replaced the placeholder YOUR_USERNAME with the author username (i.e. the output of aea config get agent.author):

import json\nfrom typing import cast\nfrom aea.protocols.base import Message\nfrom aea.skills.base import Handler\nfrom packages.fetchai.protocols.default import DefaultMessage\nfrom packages.fetchai.protocols.http.message import HttpMessage\nfrom packages.YOUR_USERNAME.skills.http_echo.dialogues import (\nDefaultDialogues,\nHttpDialogue,\nHttpDialogues,\n)\nclass HttpHandler(Handler):\n\"\"\"This implements the echo handler.\"\"\"\nSUPPORTED_PROTOCOL = HttpMessage.protocol_id\ndef setup(self) -> None:\n\"\"\"Implement the setup.\"\"\"\ndef handle(self, message: Message) -> None:\n\"\"\"\n        Implement the reaction to an envelope.\n        :param message: the message\n        \"\"\"\nhttp_msg = cast(HttpMessage, message)\n# recover dialogue\nhttp_dialogues = cast(HttpDialogues, self.context.http_dialogues)\nhttp_dialogue = cast(HttpDialogue, http_dialogues.update(http_msg))\nif http_dialogue is None:\nself._handle_unidentified_dialogue(http_msg)\nreturn\n# handle message\nif http_msg.performative == HttpMessage.Performative.REQUEST:\nself._handle_request(http_msg, http_dialogue)\nelse:\nself._handle_invalid(http_msg, http_dialogue)\ndef _handle_unidentified_dialogue(self, http_msg: HttpMessage) -> None:\n\"\"\"\n        Handle an unidentified dialogue.\n        :param http_msg: the message\n        \"\"\"\nself.context.logger.info(\n\"received invalid http message={}, unidentified dialogue.\".format(http_msg)\n)\ndefault_dialogues = cast(DefaultDialogues, self.context.default_dialogues)\ndefault_msg, _ = default_dialogues.create(\ncounterparty=http_msg.sender,\nperformative=DefaultMessage.Performative.ERROR,\nerror_code=DefaultMessage.ErrorCode.INVALID_DIALOGUE,\nerror_msg=\"Invalid dialogue.\",\nerror_data={\"http_message\": http_msg.encode()},\n)\nself.context.outbox.put_message(message=default_msg)\ndef _handle_request(\nself, http_msg: HttpMessage, http_dialogue: HttpDialogue\n) -> None:\n\"\"\"\n        Handle a Http request.\n        :param http_msg: the http message\n        :param http_dialogue: the http dialogue\n        \"\"\"\nself.context.logger.info(\n\"received http request with method={}, url={} and body={!r}\".format(\nhttp_msg.method,\nhttp_msg.url,\nhttp_msg.body,\n)\n)\nif http_msg.method == \"get\":\nself._handle_get(http_msg, http_dialogue)\nelif http_msg.method == \"post\":\nself._handle_post(http_msg, http_dialogue)\ndef _handle_get(self, http_msg: HttpMessage, http_dialogue: HttpDialogue) -> None:\n\"\"\"\n        Handle a Http request of verb GET.\n        :param http_msg: the http message\n        :param http_dialogue: the http dialogue\n        \"\"\"\nhttp_response = http_dialogue.reply(\nperformative=HttpMessage.Performative.RESPONSE,\ntarget_message=http_msg,\nversion=http_msg.version,\nstatus_code=200,\nstatus_text=\"Success\",\nheaders=http_msg.headers,\nbody=json.dumps({\"tom\": {\"type\": \"cat\", \"age\": 10}}).encode(\"utf-8\"),\n)\nself.context.logger.info(\"responding with: {}\".format(http_response))\nself.context.outbox.put_message(message=http_response)\ndef _handle_post(self, http_msg: HttpMessage, http_dialogue: HttpDialogue) -> None:\n\"\"\"\n        Handle a Http request of verb POST.\n        :param http_msg: the http message\n        :param http_dialogue: the http dialogue\n        \"\"\"\nhttp_response = http_dialogue.reply(\nperformative=HttpMessage.Performative.RESPONSE,\ntarget_message=http_msg,\nversion=http_msg.version,\nstatus_code=200,\nstatus_text=\"Success\",\nheaders=http_msg.headers,\nbody=http_msg.body,\n)\nself.context.logger.info(\"responding with: {}\".format(http_response))\nself.context.outbox.put_message(message=http_response)\ndef _handle_invalid(\nself, http_msg: HttpMessage, http_dialogue: HttpDialogue\n) -> None:\n\"\"\"\n        Handle an invalid http message.\n        :param http_msg: the http message\n        :param http_dialogue: the http dialogue\n        \"\"\"\nself.context.logger.warning(\n\"cannot handle http message of performative={} in dialogue={}.\".format(\nhttp_msg.performative, http_dialogue\n)\n)\ndef teardown(self) -> None:\n\"\"\"Implement the handler teardown.\"\"\"\n

Moreover, add a dialogues.py file with the following code:

from typing import Any\nfrom aea.protocols.base import Address, Message\nfrom aea.protocols.dialogue.base import Dialogue as BaseDialogue\nfrom aea.skills.base import Model\nfrom packages.fetchai.protocols.default.dialogues import (\nDefaultDialogue as BaseDefaultDialogue,\n)\nfrom packages.fetchai.protocols.default.dialogues import (\nDefaultDialogues as BaseDefaultDialogues,\n)\nfrom packages.fetchai.protocols.http.dialogues import HttpDialogue as BaseHttpDialogue\nfrom packages.fetchai.protocols.http.dialogues import HttpDialogues as BaseHttpDialogues\nDefaultDialogue = BaseDefaultDialogue\nclass DefaultDialogues(Model, BaseDefaultDialogues):\n\"\"\"The dialogues class keeps track of all dialogues.\"\"\"\ndef __init__(self, **kwargs: Any) -> None:\n\"\"\"\n        Initialize dialogues.\n        :param kwargs: keyword arguments\n        \"\"\"\nModel.__init__(self, **kwargs)\ndef role_from_first_message(  # pylint: disable=unused-argument\nmessage: Message, receiver_address: Address\n) -> BaseDialogue.Role:\n\"\"\"Infer the role of the agent from an incoming/outgoing first message\n            :param message: an incoming/outgoing first message\n            :param receiver_address: the address of the receiving agent\n            :return: The role of the agent\n            \"\"\"\nreturn DefaultDialogue.Role.AGENT\nBaseDefaultDialogues.__init__(\nself,\nself_address=self.context.agent_address,\nrole_from_first_message=role_from_first_message,\n)\nHttpDialogue = BaseHttpDialogue\nclass HttpDialogues(Model, BaseHttpDialogues):\n\"\"\"The dialogues class keeps track of all dialogues.\"\"\"\ndef __init__(self, **kwargs: Any) -> None:\n\"\"\"\n        Initialize dialogues.\n        :param kwargs: keyword arguments\n        \"\"\"\nModel.__init__(self, **kwargs)\ndef role_from_first_message(  # pylint: disable=unused-argument\nmessage: Message, receiver_address: Address\n) -> BaseDialogue.Role:\n\"\"\"Infer the role of the agent from an incoming/outgoing first message\n            :param message: an incoming/outgoing first message\n            :param receiver_address: the address of the receiving agent\n            :return: The role of the agent\n            \"\"\"\nreturn BaseHttpDialogue.Role.SERVER\nBaseHttpDialogues.__init__(\nself,\nself_address=str(self.skill_id),\nrole_from_first_message=role_from_first_message,\n)\n

Then, update the skill.yaml accordingly:

handlers:\nhttp_handler:\nargs: {}\nclass_name: HttpHandler\nmodels:\ndefault_dialogues:\nargs: {}\nclass_name: DefaultDialogues\nhttp_dialogues:\nargs: {}\nclass_name: HttpDialogues\n

Run the fingerprinter (note, you will have to replace the author name with your author handle):

aea fingerprint skill fetchai/http_echo:0.21.6\n

Moreover, we need to tell to the http_server connection to what skill the HTTP requests should be forwarded. In our case, this is the http_echo that you have just scaffolded. Its public id will be <your-author-name>/http_echo:0.1.0.

aea config set vendor.fetchai.connections.http_server.config.target_skill_id \"$(aea config get agent.author)/http_echo:0.1.0\" 

You can now run the AEA:

aea run\n

In a separate terminal, you can create a client and communicate with the server:

import requests\nresponse = requests.get('http://127.0.0.1:8000')\nresponse.status_code\n# >>> 404\n# we receive a not found since the path is not available in the api spec\nresponse = requests.get('http://127.0.0.1:8000/pets')\nresponse.status_code\n# >>> 200\nresponse.content\n# >>> b'{\"tom\": {\"type\": \"cat\", \"age\": 10}}'\nresponse = requests.post('http://127.0.0.1:8000/pets')\nresponse.status_code\n# >>> 200\nresponse.content\n# >>> b''\n
"},{"location":"aea-framework-documentation/identity/","title":"Identity","text":"

Note

This section is incomplete and will soon be updated.

The AEAs currently use the addresses associated with their private-public key pairs to identify themselves.

To learn how to generate a private-public key pair check out the relevant CLI commands .

To learn more about public-key cryptography check out Wikipedia.

An AEA can provide evidence of its identity using third-party solutions. We have implemented a demo using Aries Hyperledger Cloud Agent which is available here and another demo using Yoti which is available here.

"},{"location":"aea-framework-documentation/install/","title":"Installation","text":"

Platforms

The AEA framework can be used on Windows, Ubuntu/Debian and MacOS.

"},{"location":"aea-framework-documentation/install/#system-requirements","title":"System Requirements","text":"
  1. You need Python 3.8, 3.9 or 3.10 on your system.
  2. GCC installation is also required:

    UbuntuMacOS X (with Homebrew)Windows (with choco)
    apt-get install gcc\n
    brew install gcc\n
    choco install mingw\n
Tips
  • Ubuntu/Debian: install Python headers, depending on the Python version you have installed on your machine. For example for Python 3.8:

    sudo apt-get install python3.8-dev\n
  • Windows: install tools for Visual Studio.

"},{"location":"aea-framework-documentation/install/#alternatively-use-docker","title":"Alternatively: Use Docker","text":"

We also provide a Docker image with all the needed dependencies.

  1. Pull the image:

    docker pull fetchai/aea-user:latest\n
  2. Run the image with your current local directory mounted as a docker volume. This allows you to keep your agents local while working on them from within the docker container:

    Linux and MacOsWindows
    docker run -it -v $(pwd):/agents --workdir=/agents fetchai/aea-user:latest\n
    docker run -it -v %cd%:/agents --workdir=/agents fetchai/aea-user:latest\n

Once successfully logged into the docker container, you can follow the rest of the guide the same way as if not using docker.

"},{"location":"aea-framework-documentation/install/#for-agent-development","title":"For Agent Development","text":""},{"location":"aea-framework-documentation/install/#preliminaries","title":"Preliminaries","text":"
  1. Create a new working directory. Let's call it my_aea_projects. This is where you will create your agent projects.

  2. Inside my_aea_projects, add an empty directory called packages. This is a local registry for your agents' components.

You should now have the following directory structure:

my_aea_projects\n\u2514\u2500\u2500 packages\n

Alternatively, clone a template repo:

Instead of the above, you can clone the template repo as described in Approach 1 in the development setup guide.

"},{"location":"aea-framework-documentation/install/#virtual-environment","title":"Virtual Environment","text":"

Unless you are using the docker image, we highly recommend using a virtual environment so that your setup is isolated from the rest of your system. This prevents clashes and ensures consistency across dependencies.

You can use any common virtual environment manager for Python, such as pipenv and poetry. If you do not have either, install one.

Once installed, create a new virtual environment in the my_aea_projects directory and enter it:

pipenvpoetry

Use any Python version supported in the command:

pipenv --python 3.9 && pipenv shell\n

poetry init -n && poetry shell\n
"},{"location":"aea-framework-documentation/install/#installation_1","title":"Installation","text":"

The latest version of the Python implementation of the AEA Framework is:

Note

If you are upgrading your AEA project from a previous version of the AEA framework, make sure you check out the upgrading notes.

"},{"location":"aea-framework-documentation/install/#using-pip","title":"Using pip","text":"

Install the AEA framework using pip:

bash/windowszsh
pip install aea[all]\n
pip install 'aea[all]'\n
Troubleshooting

To ensure no cache is used, add --force --no-cache-dir to the installation command.

"},{"location":"aea-framework-documentation/install/#using-pipx","title":"Using pipx","text":"

Install the AEA framework using pipx:

pipx install aea[all]\n
"},{"location":"aea-framework-documentation/install/#for-contributing-to-the-aea-framework","title":"For Contributing to the AEA Framework","text":"

To contribute to the development of the framework or related tools (e.g. ACN), please refer to the Contribution and Development guides in our GitHub repository.

"},{"location":"aea-framework-documentation/install/#other-tools-you-might-need","title":"Other Tools You Might Need","text":"

Depending on what you want to do, you might need extra tools on your system:

  • To use the Agent Communication Network (ACN) for peer-to-peer communication between agents (e.g. using the fetchai/p2p_libp2p connection) you will need Golang 1.14.2 or higher.
  • The framework uses Google Protocol Buffers for message serialization. If you want to develop protocols, install the protobuf compiler on your system. The version you install must match the protobuf library installed with the project (see pyproject.toml).
  • To update fingerprint hashes of packages, you will need the IPFS daemon.
"},{"location":"aea-framework-documentation/interaction-protocol/","title":"How AEAs Talk to Each Other - Interaction Protocols","text":"

Although one can imagine scenarios where single AEAs pursue their goals in isolation without interacting with other AEAs, there is no doubt that by working together, AEAs have the potential of achieving much more, especially when taking into account agents' heterogeneity, specialisations, and differing and often complimentary local views of the environment.

Interactions in the AEA world are in the form of communication. This is influenced by established practices in the field of multi-agent systems and the prominent speech-act theory which suggests that a communicative expression is not only about transferring information from the speaker to the hearer, but that there may be meanings and commitments beyond the statement's appearance. Therefore, speech may more suitably be considered as action. For example, \"I hereby appoint you as chairman\" is not just a sequence of words, but an action done by the speaker with wide-ranging consequences for the hearer and any other audience to that sentence.

Interaction protocols are thus possible communication scenarios between agents or agent components (specifically, skills and connections).

There are multiple types of interactions an AEA can have:

  • AEA-to-AEA interactions. You can find some examples in the demo section.

  • Interactions between an AEA's internal components.

Usually, an interaction involves three types of framework packages: skills, protocols and connections.

"},{"location":"aea-framework-documentation/interaction-protocol/#examples","title":"Examples","text":""},{"location":"aea-framework-documentation/interaction-protocol/#example-1-negotiation","title":"Example 1: Negotiation","text":"

The generic buyer/seller skills use the fetchai/fipa protocol which defines the negotiation dialogue between two AEAs. The fetchai/generic_buyer and fetchai/generic_seller skills implement specific strategies for engaging in such negotiations, by providing the logic for producing negotiation messages to be sent, handling negotiation messages received. The fetchai/p2p_libp2p connection is then used for connecting to the agent communication network enabling two AEAs with these skills to deliver negotiation messages to each other.

"},{"location":"aea-framework-documentation/interaction-protocol/#example-2-aea-web-client","title":"Example 2: AEA <> Web Client","text":"

In the http connection guide we demonstrate how an AEA with a http server connection (e.g. fetchai/http_server) receives http payloads from web clients, translates them to messages conforming with the fetchai/http protocol and passes it to a skill (e.g. fetchai/http_echo) to process. The fetchai/http protocol in this case is used for communication between the connection and the skill.

"},{"location":"aea-framework-documentation/interaction-protocol/#example-3-aea-3rd-party-server","title":"Example 3 : AEA <> 3rd Party Server","text":"

The fetchai/http_client connection can be used to make requests to third party servers. In this case, a skill containing the logic for the production of http requests would create messages conforming with the fetchai/http protocol and sends it to the fetchai/http_client connection which in turn translates it into http payload and sends it to the destination server.

Note that in general, third party SDKs can be wrapped in a connection and shared with other developers as a package. Often this also involves creating a custom protocol to enforce the type of interactions permitted between skills and the connection wrapping the SDK.

"},{"location":"aea-framework-documentation/interaction-protocol/#next-steps","title":"Next Steps","text":""},{"location":"aea-framework-documentation/interaction-protocol/#recommended","title":"Recommended","text":"

We recommend you continue with the next step in the 'Getting Started' series:

  • Trade between two AEAs
"},{"location":"aea-framework-documentation/interaction-protocol/#relevant-deep-dives","title":"Relevant Deep-Dives","text":"

Most AEA development focuses on developing the Skills and Protocols necessary for an AEA to deliver against its economic objectives and implement interaction protocols.

Understanding Protocols is core to developing your own agent. You can learn more about the Protocols agents use to communicate with each other and how they are created in the following section:

  • Protocols

Most of an AEA developer's time is spent on Skill development. Skills are the core business logic components of an AEA. Check out the following guide to learn more:

  • Skills

In most cases, one of the available Connection packages can be used. Occasionally, you might develop your own Connection:

  • Connections
"},{"location":"aea-framework-documentation/known-limits/","title":"Known Limitations","text":"

The AEA framework makes a multitude of tradeoffs.

Here we present an incomplete list of known limitations:

  • The AEABuilder checks the consistency of packages at the add stage. However, it does not currently check the consistency again at the load stage. This means, if a package is tampered with after it is added to the AEABuilder then these inconsistencies might not be detected by the AEABuilder.

  • The AEABuilder assumes that packages with public ids of identical author and package name have a matching version. As a result, if a developer uses a package with matching author and package name but different version in the public id, then the AEABuilder will not detect this and simply use the last loaded package.

  • The order in which setup and teardown are called on the skills, and act is called on the behaviours, is not guaranteed. Skills should be designed to work independently. Where skills use the shared_context to exchange information they must do so safely.

"},{"location":"aea-framework-documentation/language-agnostic-definition/","title":"Language Agnostic Definition","text":"

Currently, there is an implementation of the AEA framework in Python which enables the development of AEAs in Python, and allows AEAs which are built with it to run.

However, AEAs can be developed in different programming languages. This is further backed by the idea that agent-based solutions are suited for multi-stakeholder environments where the different AEAs may be developed independently of one another, resulting in heterogeneous systems.

This means that in principle, there could be different implementations of the AEA framework, in various programming languages and for different platforms. However, to ensure that AEAs under any implementation are compatible with one another and able to interact, they must satisfy specific definitions. In this page, we compile a set of definitions which any AEA independent of its implementation must satisfy in order to be able to interact with other AEAs.

An AEA, in technical terms, must satisfy the following requirements:

  • It MUST be capable of receiving and sending Envelopes which satisfy the following protobuf schema:

    syntax = \"proto3\";\npackage aea.base.v0_1_0;\nmessage Envelope{\nstring to = 1;\nstring sender = 2;\nstring protocol_id = 3;\nbytes message = 4;\nstring uri = 5;\n}\n

    The format for the above fields are as follows:

    • to and sender: an address derived from the private key of a secp256k1-compatible elliptic curve
    • protocol_id: this must match a defined regular expression (see below)
    • message: a bytes string representing a serialized message in the specified protocol
    • URI: follows this syntax
  • It MUST implement each protocol's message with the required meta-fields:

    syntax = \"proto3\";\npackage aea.base.v0_1_0;\nimport \"google/protobuf/struct.proto\";\nmessage DialogueMessage {\nint32 message_id = 1;\nstring dialogue_starter_reference = 2;\nstring dialogue_responder_reference = 3;\nint32 target = 4;\nbytes content = 5;\n}\nmessage Message {\noneof message {\ngoogle.protobuf.Struct body = 1;\nDialogueMessage dialogue_message = 2;\n}\n}\nmessage Envelope{\nstring to = 1;\nstring sender = 2;\nstring protocol_id = 3;\nbytes message = 4;\nstring uri = 5;\n}\n

    where content is replaced with the protocol specific content (see here for details).

  • It MUST implement protocols according to their specification (see here for details).

  • It SHOULD implement the fetchai/default:1.1.7 protocol which satisfies the following protobuf schema:

    syntax = \"proto3\";\npackage aea.fetchai.default.v1_0_0;\nmessage DefaultMessage{\n// Custom Types\nmessage ErrorCode{\nenum ErrorCodeEnum {\nUNSUPPORTED_PROTOCOL = 0;\nDECODING_ERROR = 1;\nINVALID_MESSAGE = 2;\nUNSUPPORTED_SKILL = 3;\nINVALID_DIALOGUE = 4;\n}\nErrorCodeEnum error_code = 1;\n}\n// Performatives and contents\nmessage Bytes_Performative{\nbytes content = 1;\n}\nmessage Error_Performative{\nErrorCode error_code = 1;\nstring error_msg = 2;\nmap<string, bytes> error_data = 3;\n}\nmessage End_Performative{\n}\noneof performative{\nBytes_Performative bytes = 5;\nEnd_Performative end = 6;\nError_Performative error = 7;\n}\n}\n
  • The protocol id MUST match the following regular expression: ^([a-zA-Z_][a-zA-Z0-9_]{0,127})/([a-zA-Z_][a-zA-Z0-9_]{0,127})(:((any|latest|((0|[1-9]\\d*))\\.((0|[1-9]\\d*))\\.((0|[1-9]\\d*))(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)))?$

  • It is recommended that it processes Envelopes asynchronously. Note, the specification regarding the processing of messages does not impose any particular implementation, and the AEA can be designed to process envelopes either synchronously and asynchronously. However, asynchronous message handling enables the agent to be more responsive and scalable in maintaining many concurrent dialogues with its peers.
  • It MUST have an identity in the form of, at a minimum, an address derived from a public key and its associated private key (where the elliptic curve must be of type SECP256k1).
  • It SHOULD implement handling of errors using the fetchai/default:1.1.7 protocol. The protobuf schema is given above.
  • It MUST implement the following principles when handling messages:
    • It MUST ALWAYS handle incoming envelopes/messages and NEVER raise an exception when decoding and validating the message. This ensures another AEA cannot cause the agent to fail by sending a malicious envelope/message.
    • It MUST NEVER handle outgoing messages and ALWAYS raise an exception when validating the message. An exception implies that the handler is resolving a bug in the implementation.

Note

Additional constraints will be added soon!

"},{"location":"aea-framework-documentation/ledger-integration/","title":"Ledger & Crypto APIs","text":"

In this section, we show you how to integrate the AEA with the Fetch.ai and third-party ledgers.

"},{"location":"aea-framework-documentation/ledger-integration/#ledger-support","title":"Ledger Support","text":"

For a ledger to be considered supported in the framework, three abstract base classes need to be implemented:

  • the LedgerApi class wraps the API to talk to the ledger and its helper methods
  • the Crypto class wraps the API to perform cryptographic operations for the relevant ledger
  • the FaucetApi class wraps the API to talk to a faucet on a testnet

These three classes have their own registries, which allow the developer to import the relevant object where needed.

"},{"location":"aea-framework-documentation/ledger-integration/#ledger-plug-in-architecture","title":"Ledger Plug-in Architecture","text":"

The AEA framework provides a plug-in mechanism to support ledger functionalities in an easily extendable way. At import time, the framework will load all the crypto plug-ins available in the current Python environment.

A crypto plug-in is a Python package which declares some specific setuptools \"entry points\" in its setup.py script. In particular, there are three types of entry points the framework looks up:

  • aea.ledger_apis, which points to instantiable classes implementing the LedgerApi interface;
  • aea.cryptos, which points to instantiable classes implementing the Crypto interface;
  • aea.faucet_apis, which points to instantiable classes implementing the FaucetApi interface.

This is an example of setup.py script for a ledger plug-in aea-ledger-myledger:

# sample ./setup.py file\nfrom setuptools import setup\nsetup(\nname=\"aea-ledger-myledger\",\npackages=[\"aea_ledger_myledger\"],\n# plugins must depend on 'aea'  \ninstall_requires=[\"aea\"], # add other dependencies...\n# the following makes a plugin available to aea\nentry_points={\n\"aea.cryptos\": [\"myledger = aea_ledger_myledger:MyLedgerCrypto\"],\n\"aea.ledger_apis\": [\"myledger = aea_ledger_myledger:MyLedgerApi\"],\n\"aea.faucet_apis\": [\"myledger = aea_ledger_myledger:MyLedgerFaucetApi\"],\n},\n# PyPI classifier for AEA plugins\nclassifiers=[\"Framework :: AEA\"],\n)\n

By convention, such plug-in packages should be named aea-ledger-${LEDGER_ID}, and the importable package name aea_ledger_${LEDGER_ID}. In the example above, the package name is aea-ledger-myledger, and the importable package name is aea_ledger_myledger.

You can search for AEA ledger plug-ins on PyPI: https://pypi.org/search/?q=aea-ledger

"},{"location":"aea-framework-documentation/ledger-integration/#maintained-plug-ins","title":"Maintained Plug-ins","text":"

At the moment, the framework natively supports the following three ledgers:

  • Fetch.ai: PyPI package: aea-ledger-fetchai, and source code.
  • Ethereum: PyPI package: aea-ledger-ethereum, and source code.
  • Cosmos: PyPI package: aea-ledger-cosmos, and source code.

However, support for additional ledgers can be added to the framework at runtime.

"},{"location":"aea-framework-documentation/ledger-integration/#examples","title":"Examples","text":"
  • Examples of how to interact with the crypto registry:
from aea.crypto.registries import crypto_registry, make_crypto, register_crypto\n# by default we can use the native cryptos\nfetchai_crypto = make_crypto(\"fetchai\")\n# we can check what cryptos are registered\ncrypto_registry.supported_ids\n# we can also add a new crypto to the registry\nregister_crypto(id_=\"my_ledger_id\", entry_point=\"some.dotted.path:MyLedgerCrypto\")\n# and then make it anywhere\nmy_ledger_crypto = make_crypto(\"my_ledger_id\")\n
  • Examples of how to interact with the ledger API registry:
from aea.crypto.registries import ledger_apis_registry, make_ledger_api, register_ledger_api\n# by default we can use the native ledger apis\nCONFIG = {\"network\": \"testnet\"}\nfetchai_ledger_api = make_ledger_api(\"fetchai\", **CONFIG)\n# we can check what ledger apis are registered\nledger_apis_registry.supported_ids\n# we can also add a new ledger api to the registry\nregister_ledger_api(id_=\"my_ledger_id\", entry_point=\"some.dotted.path:MyLedgerApi\")\n# and then make it anywhere\nmy_ledger_api = make_ledger_api(\"my_ledger_id\")\n
  • Examples of how to interact with the faucet API registry:
from aea.crypto.registries import faucet_apis_registry, make_faucet_api, register_faucet_api\n# by default we can use the native faucet apis\nCONFIG = dict(poll_interval=1.0)\nfetchai_faucet_api = make_faucet_api(\"fetchai\", **CONFIG)\n# we can check what faucet apis are registered\nfaucet_apis_registry.supported_ids\n# we can also add a new faucet api to the registry\nregister_faucet_api(id_=\"my_ledger_id\", entry_point=\"some.dotted.path:MyLedgerFaucetApi\")\n# and then make it anywhere\nmy_faucet_api = make_faucet_api(\"my_ledger_id\")\n

The framework wraps all LedgerApi classes and exposes them in the LedgerApis classes. The framework also wraps the crypto APIs to create identities on both ledgers and exposes them in the Wallet.

The separation between the Crypto and LedgerApi is fundamental to the framework design. In particular, the object which holds the private key is separated from the object which interacts with the ledger. This design pattern is repeated throughout the framework: the decision maker is the only entity with access to the AEA's Wallet whilst LedgerApis are accessible by all skills.

"},{"location":"aea-framework-documentation/ledger-integration/#stargate-world-fetchai-testnet-for-agents","title":"Stargate World - Fetch.ai Testnet for Agents","text":"

Stargate World is our stable, public testnet for the Fetch Ledger v2. As such, most developers will be interacting with this testnet. This is specifically designed and supported for AEA development.

Parameter Value Chain ID dorado-1 Denomination atestfet Decimals 18 Version v0.8.x RPC Endpoint https://rpc-dorado.fetch.ai:443 REST Endpoint https://rest-dorado.fetch.ai:443 Block Explorer https://explore-dorado.fetch.ai Token Faucet Use block explorer

You can access more details on docs section.

The configurations can be specified for the fetchai/ledger:0.21.5 connection.

"},{"location":"aea-framework-documentation/ledger-integration/#cosmwasm-supporting-chains","title":"CosmWasm Supporting Chains","text":"

The Fetch.ai networks use CosmWasm for smart contract support.

"},{"location":"aea-framework-documentation/limits/","title":"Limitations of v1","text":"

This document describes some of the limitations of v1 of the AEA framework and tradeoffs made in its design.

"},{"location":"aea-framework-documentation/limits/#rejected-ideas","title":"Rejected Ideas","text":""},{"location":"aea-framework-documentation/limits/#handlers-implemented-as-behaviours","title":"Handlers Implemented as Behaviours","text":"

Handlers can be considered a special cases of a \"behaviour that listens for specific events to happen\".

One could implement Handler classes in terms of Behaviours, after having implemented the feature that behaviours can be activated after an event happens (e.g. receiving a message of a certain protocol).

This was rejected in favour of a clear separation of concerns, and to avoid purely reactive (handlers) and proactive (behaviours) components to be conflated into one concept. The proposal would also add complexity to behaviour development.

"},{"location":"aea-framework-documentation/limits/#multiple-versions-of-the-same-package","title":"Multiple Versions of the Same Package","text":"

The framework does not allow for the usage of multiple versions of the same package in a given project.

Although one could re-engineer the project to allow for this, it does introduce significant additional complexities. Furthermore, Python modules are by design only allowed to exist as one version in a given process. Hence, it seems sensible to maintain this approach in the AEA.

"},{"location":"aea-framework-documentation/limits/#potential-extensions-considered-yet-not-decided","title":"Potential Extensions, Considered yet not Decided","text":""},{"location":"aea-framework-documentation/limits/#alternative-skill-design","title":"Alternative Skill Design","text":"

For very simple skills, the splitting of skills into Behaviour, Handler, Model and Task classes can add unnecessary complexity to the framework and a counter-intuitive responsibility split. The splitting also implies the framework needs to introduce the SkillContext object to allow for access to data across the skill. Furthermore, the framework requires implementing all functionality in SkillComponent classes Handler, Behaviour or Model. This approach is consistent and transparent, however it creates a lot of boilerplate code for simple skills.

Hence, for some use cases it would be useful to have a single Skill class with abstract methods setup, act, handle and teardown. Then the developer can decide how to split up their code.

class SkillTemplate(SimpleSkill):\nprotocol_ids: Optional[List[PublicId]] = None\ndef setup():\n# setup skill\ndef handle(message: Message):\n# handle messages\ndef act():\nfor b in behaviours:\nb.act()\ndef teardown():\n# teardown skill\n

Alternatively, we could use decorators to let a developer define whether a function is part of a handler or behaviour. That way, a single file with a number of functions could implement a skill. (Behind the scenes this would utilise a number of virtual Behaviour and Handler classes provided by the framework).

The downside of this approach is that it does not advocate for much modularity on the skill level. Part of the role of a framework is to propose a common way to do things. The above approach can cause for a larger degree of heterogeneity in the skill design which makes it harder for developers to understand each other's code.

The separation between all four base classes does exist both in convention and at the code level. Handlers deal with skill-external events (messages), behaviours deal with scheduled events (ticks), models represent data and tasks are used to manage long-running business logic.

By adopting strong convention around skill development we allow for the framework to take a more active role in providing guarantees. E.g. handlers' and behaviours' execution can be limited to avoid them being blocking, models can be persisted and recreated, tasks can be executed with different task backends. The opinionated approach is thought to allow for better scaling.

"},{"location":"aea-framework-documentation/limits/#further-modularity-for-skill-level-code","title":"Further Modularity for Skill Level Code","text":"

Currently, we have three levels of modularity:

  • PyPI packages
  • framework packages: protocols, contracts, connections and skills
  • framework plugins: CLI, ledger

We could consider having a fourth level: common behaviours, handlers, models exposed as modules which can then speed up skill development.

"},{"location":"aea-framework-documentation/limits/#promise-pattern","title":"\"promise\" Pattern","text":"

Given the asynchronous nature of the framework, it is often hard to implement reactions to specific messages, without making a \"fat\" handler. Take the example of a handler for a certain type of message A for a certain protocol p. The handler for protocol p would look something like this:

class PHandler:\n...\ndef handle(msg):\nif message type is A:\nself._handle_a(msg)\n

However, it could be helpful to overwrite this handler reaction with another callback (e.g. consider this in context):

# callable that handles the reply\ndef my_callback(msg):\n# handle reply\nself.context.outbox.put_message(message, handler_func=my_callback, failure_func=...)\n

This feature would introduce additional complexity for the framework to correctly wire up the callbacks and messages with the dialogues.

"},{"location":"aea-framework-documentation/limits/#cli-using-standard-lib","title":"CLI using Standard Lib","text":"

Removing the click dependency from the CLI would further reduce the dependencies in the AEA framework which is overall desirable.

"},{"location":"aea-framework-documentation/limits/#metadata-vs-configurations","title":"Metadata vs Configurations","text":"

The current approach uses yaml files to specify both metadata and component configuration. It would be desirable to introduce the following separation:

  • package metadata
  • package default developer configuration
  • package default user configuration

A user can only configure a subset of the configuration. The developer should be able to define these constraints for the user. Similarly, a developer cannot modify all fields in a package, some of them are determined by the framework.

"},{"location":"aea-framework-documentation/limits/#configuring-agent-goal-setup","title":"Configuring Agent Goal Setup","text":"

By default, the agent's goals are implicitly defined by its skills and the configurations thereof. This is because the default decision maker signs every message and transaction presented to it.

It is already possible to design a custom decision maker. However, more work needs to be done to understand how to improve the usability and configuration of the decision maker. In this context different types of decision makers can be implemented for the developer/user.

"},{"location":"aea-framework-documentation/limits/#connection-status-monitoring","title":"Connection Status Monitoring","text":"

Currently, connections are responsible for managing their own status after they have been \"connected\" by the Multiplexer. Developers writing connections must take care to properly set its connection status at all times and manage any disconnection. It would potentially be desirable to offer different policies to deal with connection problems on the multiplexer level:

  • disconnect one, keep others alive
  • disconnect all
  • try to reconnect indefinitely
"},{"location":"aea-framework-documentation/limits/#agent-snapshots-on-teardown-or-error","title":"Agent Snapshots on Teardown or Error","text":"

Currently, the developer must implement snapshots on the component level. It would be desirable if the framework offered more help to persist the agent state on teardown or error.

"},{"location":"aea-framework-documentation/limits/#dialogues-management","title":"Dialogues Management","text":"

The current implementation of Dialogues is verbose. Developers often need to subclass Dialogues and Dialogue classes. More effort can be made to simplify and streamline dialogues management.

"},{"location":"aea-framework-documentation/limits/#instantiate-multiple-instances-of-the-same-class-of-skillcomponent","title":"Instantiate Multiple Instances of the Same Class of SkillComponent","text":"

Currently, configuration and metadata of a package are conflated making it not straightforward to run one package component with multiple sets of configuration. It could be desirable to configure an agent to run a given package with multiple different configurations.

This feature could be problematic with respect to component-to-component messaging which currently relies on component ids, which are bound to the package and not its instance.

"},{"location":"aea-framework-documentation/limits/#containerized-agents","title":"Containerized Agents","text":"

Agent management, especially when many of them live on the same host, can be cumbersome. The framework should provide more utilities for these large-scale use cases. But a proper isolation of the agent environment is something that helps also simple use cases.

A new software architecture, somehow inspired to the Docker system. The CLI only involves the initialization of the building of the agent (think of it as the specification of the Dockerfile: the Agentfile), but the actual build and run are done by the AEA engine, a daemon process analogous of the Docker Engine, which exposes APIs for these operations.

Users and developers would potentially like to run many AEAs of different versions and with differences in the versions of their dependencies. It is not possible to import different versions of the same Python (PyPI) package in the same process in a clean way. However, in different processes this is trivial with virtual environments. It would be desirable to consider this in the context of a container solution for agents.

"},{"location":"aea-framework-documentation/limits/#dependency-light-version-of-the-aea-framework","title":"Dependency Light Version of the AEA Framework","text":"

The v1 of the Python AEA implementation makes every effort to minimise the amount of third-party dependencies. However, some dependencies remain to lower development time.

It would be desirable to further reduce the dependencies, and potentially have an implementation that only relies on the Python standard library.

This could be taken further, and a reduced spec version for micropython could be designed.

"},{"location":"aea-framework-documentation/limits/#compiled-aea","title":"Compiled AEA","text":"

Python is not a compiled language. However, various projects attempt this, e.g. Nuitka and it would be desirable to explore how useful and practical this would be in the context of AEA.

"},{"location":"aea-framework-documentation/limits/#did-integration","title":"DID Integration","text":"

It would be great to integrate DID in the framework design, specifically identification of packages (most urgently protocols). Other projects and standards worth reviewing in the context (in particular with respect to identity):

  • ERC 725: Ethereum Identity Standard and here.
  • ERC 735: Claim Holder
"},{"location":"aea-framework-documentation/limits/#optimise-protocol-schemas-and-messages","title":"Optimise Protocol Schemas and Messages","text":"

The focus of protocol development was on extensibility and compatibility, not on optimisation. For instance, the dialogue references use inefficient string representations.

"},{"location":"aea-framework-documentation/limits/#constraints-on-primitive-types-in-protocols","title":"Constraints on Primitive Types in Protocols","text":"

The protocol generator currently does not support custom constraints. The framework could add support for custom constraints for the protocol generator and specification.

There are many types of constraints that could be supported in specification and generator. One could perhaps add support based on the popularity of specific constraints from users/developers.

Example constraints:

  • strings following specific regular expression format (e.g. all lower case, any arbitrary regex format)
  • max number of elements on lists/sets
  • keys in one dict type be equal to keys in another dict type
  • other logical constraints, e.g. as supported in ontological languages
  • support for bounds (i.e. min, max) for numerical types (i.e. int and float) in protocol specification.

Example syntax:

  • pt:int[0, ]
  • pt:float[1.0, 10.0]
  • pt:int[-1000, 1000]
  • pt:float[, 0]

This would automatically enable support for signed/unsigned int and float. This syntax would allow for unbounded positive/negative/both, or arbitrary bounds to be placed on numerical types.

Currently, the developer has to specify a custom type to implement any constraints on primitive types.

"},{"location":"aea-framework-documentation/limits/#sub-protocols-multi-party-interactions","title":"Sub-protocols & Multi-Party Interactions","text":"

Protocols can be allowed to depend on each other. Similarly, protocols might have multiple parties.

Furthermore, a turn-taking function that specifies who's turn it is at any given point in the dialogue could be added.

Then the current fipa setup is a specific case of turn-taking where the turn shifts after a player sends a single move (unique-reply). But generally, it does not have to be like this. Players could be allowed to send multiple messages until the turn shifts, or until they send specific speech-acts (multiple-replies).

"},{"location":"aea-framework-documentation/limits/#timeouts-in-protocols","title":"Timeouts in Protocols","text":"

Protocols currently do not implement the concept of timeouts. We leave it to the skill developer to implement any time-specific protocol rules.

"},{"location":"aea-framework-documentation/limits/#framework-internal-messages","title":"Framework Internal Messages","text":"

The activation/deactivation of skills and addition/removal of components is implemented in a \"passive\" way - the skill posts a request in its skill context queue (in the case of new behaviours), or it just sets a flag (in case of activation/deactivation of skills).

One could consider that a skill can send requests to the framework, via the internal protocol, to modify its resources or its status. The DecisionMaker or the Filter can be the components that take such actions.

This is a further small but meaningful step toward an actor-based model for agent internals.

"},{"location":"aea-framework-documentation/limits/#ledger-transaction-management","title":"Ledger Transaction Management","text":"

Currently, the framework does not manage any aspect of submitting multiple transactions to the ledgers. This responsibility is left to skills. Additionally, the ledger APIs/contract APIs take the ledger as a reference to determine the nonce for a transaction. If a new transaction is sent before a previous transaction has been processed then the nonce will not be incremented correctly for the second transaction. This can lead to submissions of multiple transactions with the same nonce, and therefore failure of subsequent transactions.

A naive approach would involve manually incrementing the nonce and then submitting transactions into the pool with the correct nonce for eventual inclusion. The problem with this approach is that any failure of a transaction will cause none of the subsequent transactions to be processed for some ledgers (https://ethereum.stackexchange.com/questions/2808/what-happens-when-a-transaction-nonce-is-too-high). To recover from a transaction failure not only the failed transaction would need to be handled, but potentially also all subsequent transactions. It is easy to see that logic required to recover from a transaction failure early in a sequence can be arbitrarily complex (involving potentially new negotiations between agents, new signatures having to be generated etc.).

A further problem with the naive approach is that it (imperfectly) replicates the ledger state (with respect to (subset of state of) a specific account).

A simple solution looks as follows: each time a transaction is constructed (requiring a new nonce) the transaction construction is queued until all previous transactions have been included in the ledger or failed. This way, at any one time the agent has only at most one transaction pending with the ledger. Benefits: simple to understand and maintain, transaction only enter the mempool when they are ready for inclusion which has privacy benefits over submitting a whole sequence of transaction at once. Downside: at most one transaction per block.

This approach is currently used and implemented across all the reference skills.

Related, the topic of latency in transactions. State channels provide a solution. E.g. Perun. There could also be an interesting overlap with our protocols here.

"},{"location":"aea-framework-documentation/limits/#unsolved-problems-in-multiplexer-agentloop-interplay","title":"Unsolved Problems in Multiplexer - AgentLoop Interplay","text":"

Problem 1: connection generates too many messages in a short amount of time, that are not consumed by the multiplexer Solution: Can be solved by slowing down connections receive method called, controlled by the inbox messages amount Side effects: Most of the connections should have an internal queue because there is no synchronization between internal logic and multiplexer connection receive calls.

Problem 2: the send method can take a long time (because send retries logic in connection) Solution: Currently, we apply timeouts on send. Other solutions could be considered, like parallelization.

Problem 3: too many messages are produced by a skill. Solution: Raise an exception on outbox is full or slow down agent loop?

"},{"location":"aea-framework-documentation/limits/#acn","title":"ACN","text":""},{"location":"aea-framework-documentation/limits/#agent-mobility-on-acn","title":"Agent Mobility on ACN","text":"

If a peer-client or full client switches peer, then the DHT is not updated properly at the moment under certain conditions.

"},{"location":"aea-framework-documentation/limits/#mailbox-connection","title":"Mailbox Connection","text":"

The two available connections p2p_libp2p and p2p_libp2p_client imply that the agent is continuously connected and therefore must have uninterrupted network access and the resources to maintain a connection.

For more lightweight implementations, a mailbox connection is desirable, as outlined in the ACN documentation.

"},{"location":"aea-framework-documentation/logging/","title":"Logging","text":"

The AEA framework supports flexible logging capabilities with the standard Python logging library.

In this tutorial, we configure logging for an AEA.

First, create your AEA.

aea create my_aea\ncd my_aea\n

The aea-config.yaml file should look like this.

agent_name: my_aea\nauthor: fetchai\nversion: 0.1.0\ndescription: ''\nlicense: Apache-2.0\naea_version: 0.6.0\nfingerprint: {}\nfingerprint_ignore_patterns: []\nconnections:\n- fetchai/stub:0.21.3\ncontracts: []\nprotocols:\n- fetchai/default:1.1.7\nskills:\n- fetchai/error:0.18.6\ndefault_connection: fetchai/stub:0.21.3\ndefault_ledger: fetchai\nrequired_ledgers:\n- fetchai\nlogging_config:\ndisable_existing_loggers: false\nversion: 1\nprivate_key_paths: {}\n

By updating the logging_config section, you can configure the loggers of your application.

The format of this section is specified in the logging.config module.

At this section you'll find the definition of the configuration dictionary schema.

Below is an example of the logging_config value.

logging_config:\nversion: 1\ndisable_existing_loggers: False\nformatters:\nstandard:\nformat: '%(asctime)s [%(levelname)s] %(name)s: %(message)s'\nhandlers:\nlogfile:\nclass: logging.FileHandler\nformatter: standard\nlevel: DEBUG\nfilename: logconfig.log\nconsole:\nclass: logging.StreamHandler\nformatter: standard\nlevel: DEBUG\nloggers:\naea:\nhandlers:\n- logfile\n- console\nlevel: DEBUG\npropagate: False\n

This configuration will set up a logger with name aea. It prints both on console and on file with a format specified by the standard formatter.

"},{"location":"aea-framework-documentation/logging/#streaming-to-browser","title":"Streaming to Browser","text":"

It is possible to configure the AEA to stream logs to a browser.

First, add the following configuration to your AEA:

logging_config:\nversion: 1\ndisable_existing_loggers: false\nformatters:\nstandard:\nformat: '%(asctime)s [%(levelname)s] %(name)s: %(message)s'\nhandlers:\nhttp:\nclass: logging.handlers.HTTPHandler\nformatter: standard\nlevel: INFO\nhost: localhost:5000\nurl: /stream\nmethod: POST\nloggers:\naea:\nhandlers:\n- http\nlevel: INFO\npropagate: false\n

Second, create a log server:

# -*- coding: utf-8 -*-\n\"\"\"A simple flask server to serve logs.\"\"\"\nimport datetime\nimport itertools\nimport queue\nfrom flask import Flask, Response, request, stream_with_context\ndef format_log(log_dict):\n\"\"\"Format a log record.\"\"\"\ndate = datetime.datetime.fromtimestamp(float(log_dict[\"created\"]))\nformatted_log = f\"[{date.isoformat()}] [{log_dict['levelname']}] {log_dict['name']}: {log_dict['msg']}\"\nreturn formatted_log\ndef create_app():\n\"\"\"Create Flask app for streaming logs.\"\"\"\nall_logs = []\nunread_logs = queue.Queue()\napp = Flask(__name__)\n@app.route(\"/\")\ndef index():\n\"\"\"Stream logs to client.\"\"\"\ndef generate():\n# stream old logs\ndiv = \"<div>{}</div>\"\nfor old_row in all_logs:\nyield div.format(old_row)\n# stream unread logs\nwhile True:\nrow = unread_logs.get()\nall_logs.append(row)\nyield f\"<div>{row}</div>\"\nrows = generate()\ntitle = \"<p>Waiting for logs...</p>\"\nreturn Response(stream_with_context(itertools.chain([title], rows)))\n@app.route(\"/stream\", methods=[\"POST\"])\ndef stream():\n\"\"\"Save log record from AEA.\"\"\"\nlog_record_formatted = format_log(dict(request.form))\nunread_logs.put(log_record_formatted)\nreturn {}, 200\napp.run()\nif __name__ == \"__main__\":\ncreate_app()\n

Save the script in a file called server.py, install flask with pip install flask and run the server with python server.py.

Third, run your AEA and visit localhost:5000 in your browser.

"},{"location":"aea-framework-documentation/message-routing/","title":"Message Routing","text":"

Message routing can be split up into the routing of incoming and outgoing Messages.

It is important to keep in mind that interaction protocols can be maintained between agents (agent to agent) and between components of the AEA (component to component). In the former case, the to/sender fields of the Envelope are agent addresses which must follow the address standard of agents, in the latter case they are component public ids. Crucially, both addresses must reference the same type: agent or component.

"},{"location":"aea-framework-documentation/message-routing/#incoming-messages","title":"Incoming Messages","text":"
  • Connections receive or create Envelopes which they deposit in the InBox
  • for agent-to-agent communication only, the Multiplexer keeps track of the connection_id via which the Envelope was received.
  • the AgentLoop picks Envelopes off the InBox
  • the AEA tries to decode the message; errors are handled by the ErrorHandler
  • Messages are dispatched based on two rules:

    1. checks if to field can be interpreted as skill_id, if so uses that together with the protocol_id to dispatch to the protocol's Handler in the specified Skill, else
    2. uses the protocol_id to dispatch to the protocol's Handler in all skills supporting the protocol.

Note

For agent-to-agent communication it is advisable to have a single skill implement a given protocol. Skills can then forward the messages via skill-to-skill communication to other skills where required. Otherwise, received agent-to-agent messages will be forwarded to all skills implementing a handler for the specified protocol and the developer needs to take care to handle them appropriately (e.g. avoid multiple replies to a single message).

"},{"location":"aea-framework-documentation/message-routing/#outgoing-messages","title":"Outgoing Messages","text":"
  • Skills deposit Messages in OutBox
  • OutBox constructs an Envelope from the Message
  • Multiplexer assigns messages to relevant Connection based on the following rules:

    1. Component to component messages are routed by their component_id
    2. Agent to agent messages are routed following four rules:
      1. checks if EnvelopeContext exists and specifies a Connection, if so uses that else
      2. checks which connection handled the last message from sender, if present uses that else
      3. checks if default routing is specified for the protocol_id referenced in the Envelope, if so uses that else
      4. sends to default Connection.
  • Connections can process Envelopes directly or encode them for transport to another agent.

"},{"location":"aea-framework-documentation/message-routing/#usage-of-the-envelopecontext","title":"Usage of the EnvelopeContext","text":"

The EnvelopeContext is used to maintain agent-to-agent communication only and is managed almost entirely by the framework. The developer can set the EnvelopeContext explicitly for the first message in a dialogue to achieve targeted routing to connections (see 2. for outgoing messages). This is relevant when the same agent can be reached via multiple connections.

The EnvelopeContext is not sent to another agent.

"},{"location":"aea-framework-documentation/ml-skills/","title":"ML Skills","text":"

The AEA ML (machine learning) skills demonstrate an interaction between two AEAs, one purchasing data from the other and training a machine learning model with it.

There are two types of AEAs:

  • The ml_data_provider which sells training data.
  • The ml_model_trainer which purchases data and trains a model
"},{"location":"aea-framework-documentation/ml-skills/#discussion","title":"Discussion","text":"

This demo aims to demonstrate the integration of a simple AEA with machine learning using the AEA framework. The ml_data_provider AEA provides some sample data and delivers to the client upon payment. Once the client receives the data, it trains a model. This process can be found in tasks.py. This demo does not utilize a smart contract. As a result, the ledger interaction is only for completing a transaction.

Since the AEA framework enables using third-party libraries from PyPI, we can directly reference any external dependencies. The aea install command installs all dependencies an AEA needs that is listed in one of its skills' YAML file.

"},{"location":"aea-framework-documentation/ml-skills/#communication","title":"Communication","text":"

This diagram shows the communication between the two AEAs.

    sequenceDiagram\n        participant ml_model_trainer\n        participant ml_data_provider\n        participant Search\n        participant Ledger\n\n        activate ml_model_trainer\n        activate ml_data_provider\n        activate Search\n        activate Ledger\n\n        ml_data_provider->>Search: register_service\n        ml_model_trainer->>Search: search\n        Search-->>ml_model_trainer: list_of_agents\n        ml_model_trainer->>ml_data_provider: call_for_terms\n        ml_data_provider->>ml_model_trainer: terms\n        ml_model_trainer->>Ledger: request_transaction\n        ml_model_trainer->>ml_data_provider: accept (incl transaction_hash)\n        ml_data_provider->>Ledger: check_transaction_status\n        ml_data_provider->>ml_model_trainer: data\n        loop train\n            ml_model_trainer->>ml_model_trainer: tran_model\n        end\n\n        deactivate ml_model_trainer\n        deactivate ml_data_provider\n        deactivate Search\n        deactivate Ledger
"},{"location":"aea-framework-documentation/ml-skills/#option-1-aea-manager-approach","title":"Option 1: AEA Manager Approach","text":"

Follow this approach when using the AEA Manager Desktop app. Otherwise, skip and follow the CLI approach below.

"},{"location":"aea-framework-documentation/ml-skills/#preparation-instructions","title":"Preparation Instructions","text":"
  • Install the AEA Manager.
  • Install Tensorflow
"},{"location":"aea-framework-documentation/ml-skills/#demo-instructions","title":"Demo Instructions","text":"

The following steps assume you have launched the AEA Manager Desktop app.

  1. Add a new AEA called ml_data_provider with public id fetchai/ml_data_provider:0.28.0.

  2. Add another new AEA called ml_model_trainer with public id fetchai/ml_model_trainer:0.29.0.

  3. Copy the address from the ml_model_trainer into your clip board. Then go to the Dorado block explorer and request some test tokens via Get Funds.

  4. Run the ml_data_provider AEA. Navigate to its logs and copy the multiaddress displayed.

  5. Navigate to the settings of the ml_model_trainer and under components > connection > fetchai/p2p_libp2p:0.22.0 update as follows (make sure to replace the placeholder with the multiaddress):

    {\n\"delegate_uri\": \"127.0.0.1:11001\",\n  \"entry_peers\": [\"REPLACE_WITH_MULTI_ADDRESS_HERE\"],\n  \"local_uri\": \"127.0.0.1:9001\",\n  \"log_file\": \"libp2p_node.log\",\n  \"public_uri\": \"127.0.0.1:9001\"\n}\n
  6. Run the ml_model_trainer.

In the AEA's logs, you should see the agents trading successfully, and the training agent training its machine learning model using the data purchased. The trainer keeps purchasing data and training its model until stopped.

"},{"location":"aea-framework-documentation/ml-skills/#option-2-cli-approach","title":"Option 2: CLI Approach","text":"

Follow this approach when using the aea CLI.

"},{"location":"aea-framework-documentation/ml-skills/#preparation-instructions_1","title":"Preparation Instructions","text":""},{"location":"aea-framework-documentation/ml-skills/#dependencies","title":"Dependencies","text":"
  • Follow the Preliminaries and Installation sections from the AEA quick start.
  • Install Tensorflow
"},{"location":"aea-framework-documentation/ml-skills/#demo-instructions_1","title":"Demo Instructions","text":""},{"location":"aea-framework-documentation/ml-skills/#create-data-provider-aea","title":"Create Data Provider AEA","text":"

First, fetch the data provider AEA:

aea fetch fetchai/ml_data_provider:0.32.5\ncd ml_data_provider\naea install\naea build\n
Alternatively, create from scratch:

The following steps create the data provider from scratch:

aea create ml_data_provider\ncd ml_data_provider\naea add connection fetchai/p2p_libp2p:0.27.5\naea add connection fetchai/soef:0.27.6\naea add connection fetchai/ledger:0.21.5\naea add skill fetchai/ml_data_provider:0.27.6\naea config set --type dict agent.dependencies \\\n'{\n  \"aea-ledger-fetchai\": {\"version\": \"<2.0.0,>=1.0.0\"}\n}'\naea config set agent.default_connection fetchai/p2p_libp2p:0.27.5\naea config set --type dict agent.default_routing \\\n'{\n  \"fetchai/ledger_api:1.1.7\": \"fetchai/ledger:0.21.5\",\n  \"fetchai/oef_search:1.1.7\": \"fetchai/soef:0.27.6\"\n}'\naea install\naea build\n
"},{"location":"aea-framework-documentation/ml-skills/#create-model-trainer-aea","title":"Create Model Trainer AEA","text":"

Then, fetch the model trainer AEA:

aea fetch fetchai/ml_model_trainer:0.33.5\ncd ml_model_trainer\naea install\naea build\n
Alternatively, create from scratch:

The following steps create the model trainer from scratch:

aea create ml_model_trainer\ncd ml_model_trainer\naea add connection fetchai/p2p_libp2p:0.27.5\naea add connection fetchai/soef:0.27.6\naea add connection fetchai/ledger:0.21.5\naea add skill fetchai/ml_train:0.29.6\naea config set --type dict agent.dependencies \\\n'{\n  \"aea-ledger-fetchai\": {\"version\": \"<2.0.0,>=1.0.0\"}\n}'\naea config set agent.default_connection fetchai/p2p_libp2p:0.27.5\naea config set --type dict agent.default_routing \\\n'{\n  \"fetchai/ledger_api:1.1.7\": \"fetchai/ledger:0.21.5\",\n  \"fetchai/oef_search:1.1.7\": \"fetchai/soef:0.27.6\"\n}'\naea install\naea build\n
"},{"location":"aea-framework-documentation/ml-skills/#add-keys-for-the-data-provider-aea","title":"Add Keys for the Data Provider AEA","text":"

First, create the private key for the data provider AEA based on the network you want to transact. To generate and add a private-public key pair for Fetch.ai Dorado use:

aea generate-key fetchai\naea add-key fetchai fetchai_private_key.txt\n

Next, create a private key used to secure the AEA's communications:

aea generate-key fetchai fetchai_connection_private_key.txt\naea add-key fetchai fetchai_connection_private_key.txt --connection\n

Finally, certify the key for use by the connections that request that:

aea issue-certificates\n
"},{"location":"aea-framework-documentation/ml-skills/#add-keys-and-generate-wealth-for-the-model-trainer-aea","title":"Add Keys and Generate Wealth for the Model Trainer AEA","text":"

The model trainer needs to have some wealth to purchase the data from the data provider.

First, create the private key for the model trainer AEA based on the network you want to transact. To generate and add a private-public key pair for Fetch.ai Dorado use:

aea generate-key fetchai\naea add-key fetchai fetchai_private_key.txt\n

Then, create some wealth for your model trainer based on the network you want to transact with. On the Fetch.ai Dorado network:

aea generate-wealth fetchai\n

Next, create a private key used to secure the AEA's communications:

aea generate-key fetchai fetchai_connection_private_key.txt\naea add-key fetchai fetchai_connection_private_key.txt --connection\n

Finally, certify the key for use by the connections that request that:

aea issue-certificates\n
"},{"location":"aea-framework-documentation/ml-skills/#run-both-aeas","title":"Run both AEAs","text":"

Run both AEAs from their respective terminals.

First, run the data provider AEA:

aea run\n

Once you see a message of the form To join its network use multiaddr 'SOME_ADDRESS' take note of the address. (Alternatively, use aea get-multiaddress fetchai -c -i fetchai/p2p_libp2p:0.27.5 -u public_uri to retrieve the address.) This is the entry peer address for the local agent communication network created by the ML data provider.

Then, in the ML model trainer, run this command (replace SOME_ADDRESS with the correct value as described above):

aea config set --type dict vendor.fetchai.connections.p2p_libp2p.config \\\n'{\n  \"delegate_uri\": \"127.0.0.1:11001\",\n  \"entry_peers\": [\"SOME_ADDRESS\"],\n  \"local_uri\": \"127.0.0.1:9001\",\n  \"log_file\": \"libp2p_node.log\",\n  \"public_uri\": \"127.0.0.1:9001\"\n}'\n

This allows the model trainer to connect to the same local agent communication network as the data provider.

Then run the model trainer AEA:

aea run\n

You can see that the AEAs find each other, negotiate and eventually trade. After the trade, the model trainer AEA trains its ML model using the data it has purchased. This AEA keeps purchasing data and training its model until stopped.

"},{"location":"aea-framework-documentation/ml-skills/#cleaning-up","title":"Cleaning up","text":"

When you're finished, delete your AEAs:

cd ..\naea delete ml_data_provider\naea delete ml_model_trainer\n
"},{"location":"aea-framework-documentation/modes/","title":"Modes of Running an AEA","text":"

We can run an AEA in multiple modes thanks to the configurable design of the framework.

The AEA contains two runnable parts, the AgentLoop, which operates the skills, and the Multiplexer, which operates the connections. The AgentLoop can be configured to run in async or sync mode. The Multiplexer by default runs in async mode. The AEA itself, can be configured to run in async mode, if both the Multiplexer and AgentLoop have the same mode, or in threaded mode. The latter ensures that AgentLoop and Multiplexer are run in separate threads.

"},{"location":"aea-framework-documentation/multi-agent-manager/","title":"Multi Agent Manager","text":"

The MultiAgentManager allows managing multiple agent projects programmatically.

"},{"location":"aea-framework-documentation/multi-agent-manager/#setup","title":"Setup","text":"

We instantiate the manager by providing it with the working directory in which to operate and starting it:

import os\nfrom pathlib import Path\nfrom aea.manager import MultiAgentManager\nWORKING_DIR = \"mam\"\nmanager = MultiAgentManager(WORKING_DIR)\nmanager.start_manager()\n
"},{"location":"aea-framework-documentation/multi-agent-manager/#adding-projects","title":"Adding Projects","text":"

We first add a couple of finished AEA project:

from aea.configurations.base import PublicId\nweather_station_id = PublicId.from_str(\"fetchai/weather_station:0.32.5\")\nweather_client_id = PublicId.from_str(\"fetchai/weather_client:0.33.5\")\nmanager.add_project(weather_station_id)\nmanager.add_project(weather_client_id)\nweather_station_name = weather_station_id.name\nweather_client_name = weather_client_id.name\n
"},{"location":"aea-framework-documentation/multi-agent-manager/#adding-agent-instances","title":"Adding Agent Instances","text":"

Add the agent instances

agent_overrides = {\n\"private_key_paths\": {\"fetchai\": \"fetchai_private_key.txt\"},\n\"connection_private_key_paths\": {\"fetchai\": \"fetchai_connection_private_key.txt\"}\n}\np2p_public_id = PublicId.from_str(\"fetchai/p2p_libp2p:0.27.5\")\nsoef_public_id = PublicId.from_str(\"fetchai/soef:0.27.6\")\ncomponent_overrides = [{\n**p2p_public_id.json,\n\"type\": \"connection\",\n\"cert_requests\": [{\n\"identifier\": \"acn\",\n\"ledger_id\": \"fetchai\",\n\"not_after\": '2022-01-01',\n\"not_before\": '2021-01-01',\n\"public_key\": \"fetchai\",\n\"message_format\": \"{public_key}\",\n\"save_path\": \"conn_cert.txt\"\n}]\n}, {\n**soef_public_id.json,\n\"type\": \"connection\",\n\"config\": {\n\"token_storage_path\": \"soef_token.txt\"\n}\n}]\nmanager.add_agent(weather_station_id, component_overrides=component_overrides, agent_overrides=agent_overrides)\nagent_overrides = {\n\"private_key_paths\": {\"fetchai\": \"fetchai_private_key.txt\"},\n\"connection_private_key_paths\": {\"fetchai\": \"fetchai_connection_private_key.txt\"}\n}\ncomponent_overrides = [{\n**p2p_public_id.json,\n\"type\": \"connection\",\n\"config\": {\n\"delegate_uri\": \"127.0.0.1:11001\",\n\"entry_peers\": ['/dns4/127.0.0.1/tcp/9000/p2p/16Uiu2HAkzgZYyk25XjAhmgXcdMbahrHYi18uuAzHuxPn1KkdmLRw'],\n\"local_uri\": \"127.0.0.1:9001\",\n\"public_uri\": \"127.0.0.1:9001\",\n},\n\"cert_requests\": [{\n\"identifier\": \"acn\",\n\"ledger_id\": \"fetchai\",\n\"not_after\": '2022-01-01',\n\"not_before\": '2021-01-01',\n\"public_key\": \"fetchai\",\n\"message_format\": \"{public_key}\",\n\"save_path\": \"conn_cert.txt\"\n}]\n}, {\n**soef_public_id.json,\n\"type\": \"connection\",\n\"config\": {\n\"token_storage_path\": \"soef_token.txt\"\n}\n}]\nmanager.add_agent(weather_client_id, component_overrides=component_overrides, agent_overrides=agent_overrides)\n

Save the following private keys in the respective files.

FET_PRIVATE_KEY_STATION = b\"72d3149f5689f0749eaec5ebf6dba5deeb1e89b93ae1c58c71fd43dfaa231e87\"\nFET_PRIVATE_KEY_PATH_STATION = Path(manager.data_dir, weather_station_name, \"fetchai_private_key.txt\").absolute()\nFET_PRIVATE_KEY_PATH_STATION.write_bytes(FET_PRIVATE_KEY_STATION)\nFET_CONNECTION_PRIVATE_KEY_STATION = b\"bf529acb2546e13615ef6004c48e393f0638a5dc0c4979631a9a4bc554079f6f\"\nFET_CONNECTION_PRIVATE_KEY_PATH_STATION = Path(manager.data_dir, weather_station_name, \"fetchai_connection_private_key.txt\").absolute()\nFET_CONNECTION_PRIVATE_KEY_PATH_STATION.write_bytes(FET_CONNECTION_PRIVATE_KEY_STATION)\nFET_PRIVATE_KEY_CLIENT = b\"589839ae54b71b8754a7fe96b52045364077c28705a1806b74441debcae16e0a\"\nFET_PRIVATE_KEY_PATH_CLIENT = Path(manager.data_dir, weather_client_name, \"fetchai_private_key.txt\").absolute()\nFET_PRIVATE_KEY_PATH_CLIENT.write_bytes(FET_PRIVATE_KEY_CLIENT)\nFET_CONNECTION_PRIVATE_KEY_CLIENT = b\"c9b38eff57f678f5ab5304447997351edb08eceb883267fa4ad849074bec07e4\"\nFET_CONNECTION_PRIVATE_KEY_PATH_CLIENT = Path(manager.data_dir, weather_client_name, \"fetchai_connection_private_key.txt\").absolute()\nFET_CONNECTION_PRIVATE_KEY_PATH_CLIENT.write_bytes(FET_CONNECTION_PRIVATE_KEY_CLIENT)\n
"},{"location":"aea-framework-documentation/multi-agent-manager/#running-the-agents","title":"Running the Agents","text":"
import time\nmanager.start_agent(weather_station_id.name)\n# wait for ~10 seconds for peer node to go live\ntime.sleep(10.0)\nmanager.start_agent(weather_client_id.name)\ntime.sleep(5.0)\n
"},{"location":"aea-framework-documentation/multi-agent-manager/#stopping-the-agents","title":"Stopping the Agents","text":"
manager.stop_all_agents()\n
"},{"location":"aea-framework-documentation/multi-agent-manager/#cleaning-up","title":"Cleaning up","text":"
manager.stop_manager()\n
"},{"location":"aea-framework-documentation/multi-agent-manager/#limitations","title":"Limitations","text":"

The MultiAgentManager can only be used with compatible package versions, in particular the same package (with respect to author and name) cannot be used in different versions. If you want to run multiple agents with differing versions of the same package then use the aea launch command in the multi-processing mode, or simply launch each agent individually with aea run.

"},{"location":"aea-framework-documentation/multiplexer-standalone/","title":"Use Multiplexer Stand-Alone","text":"

The Multiplexer can be used stand-alone. This way a developer can utilise the protocols and connections independent of the Agent or AEA classes.

First, import the Python and application specific libraries and set the static variables. (Get the packages directory from the AEA repository svn export https://github.com/fetchai/agents-aea.git/trunk/packages.)

import os\nimport time\nfrom copy import copy\nfrom threading import Thread\nfrom typing import Optional\nfrom aea.configurations.base import ConnectionConfig\nfrom aea.helpers.file_io import write_with_lock\nfrom aea.identity.base import Identity\nfrom aea.mail.base import Envelope\nfrom aea.multiplexer import Multiplexer\nfrom packages.fetchai.connections.stub.connection import StubConnection\nfrom packages.fetchai.protocols.default.message import DefaultMessage\nINPUT_FILE = \"input.txt\"\nOUTPUT_FILE = \"output.txt\"\n
"},{"location":"aea-framework-documentation/multiplexer-standalone/#instantiate-a-multiplexer","title":"Instantiate a Multiplexer","text":"

A Multiplexer only needs a list of connections. The StubConnection is a simple connection which reads from and writes to file.

    # Ensure the input and output files do not exist initially\nif os.path.isfile(INPUT_FILE):\nos.remove(INPUT_FILE)\nif os.path.isfile(OUTPUT_FILE):\nos.remove(OUTPUT_FILE)\n# create the connection and multiplexer objects\nconfiguration = ConnectionConfig(\ninput_file=INPUT_FILE,\noutput_file=OUTPUT_FILE,\nconnection_id=StubConnection.connection_id,\n)\nstub_connection = StubConnection(\nconfiguration=configuration,\ndata_dir=\".\",\nidentity=Identity(\"some_agent\", \"some_address\", \"some_public_key\"),\n)\nmultiplexer = Multiplexer([stub_connection], protocols=[DefaultMessage])\n
"},{"location":"aea-framework-documentation/multiplexer-standalone/#start-the-multiplexer","title":"Start the Multiplexer","text":"

We can run a multiplexer by calling, connect() which starts the 'receive' and 'send' loops. We run the multiplexer from a different thread so that we can still use the main thread to pass it messages.

    try:\n# Set the multiplexer running in a different thread\nt = Thread(target=multiplexer.connect)\nt.start()\n# Wait for everything to start up\nfor _ in range(20):\nif multiplexer.is_connected:\nbreak\ntime.sleep(1)\nelse:\nraise Exception(\"Not connected\")\n
"},{"location":"aea-framework-documentation/multiplexer-standalone/#send-and-receive-an-envelope","title":"Send and Receive an Envelope","text":"

We use the input and output text files to send an envelope to our agent and receive a response

        # Create a message inside an envelope and get the stub connection to pass it into the multiplexer\nmessage_text = (\n\"multiplexer,some_agent,fetchai/default:1.0.0,\\x08\\x01*\\x07\\n\\x05hello,\"\n)\nwith open(INPUT_FILE, \"w\") as f:\nwrite_with_lock(f, message_text)\n# Wait for the envelope to get processed\nfor _ in range(20):\nif not multiplexer.in_queue.empty():\nbreak\ntime.sleep(1)\nelse:\nraise Exception(\"No message!\")\n# get the envelope\nenvelope = multiplexer.get()  # type: Optional[Envelope]\nassert envelope is not None\n# Inspect its contents\nprint(\n\"Envelope received by Multiplexer: sender={}, to={}, protocol_specification_id={}, message={}\".format(\nenvelope.sender,\nenvelope.to,\nenvelope.protocol_specification_id,\nenvelope.message,\n)\n)\n# Create a mirrored response envelope\nresponse_envelope = copy(envelope)\nresponse_envelope.to = envelope.sender\nresponse_envelope.sender = envelope.to\n# Send the envelope back\nmultiplexer.put(response_envelope)\n# Read the output envelope generated by the multiplexer\nwith open(OUTPUT_FILE, \"r\") as f:\nprint(\"Envelope received from Multiplexer: \" + f.readline())\n
"},{"location":"aea-framework-documentation/multiplexer-standalone/#shutdown","title":"Shutdown","text":"

Finally, stop our multiplexer and wait for it to finish

    finally:\n# Shut down the multiplexer\nmultiplexer.disconnect()\nt.join()\n
"},{"location":"aea-framework-documentation/multiplexer-standalone/#your-turn","title":"Your Turn","text":"

Now it is your turn to develop a simple use case which utilises the Multiplexer to send and receive Envelopes.

"},{"location":"aea-framework-documentation/multiplexer-standalone/#entire-code-listing","title":"Entire Code Listing","text":"

If you just want to copy and paste the entire script in you can find it here:

Click here to see full listing:
import os\nimport time\nfrom copy import copy\nfrom threading import Thread\nfrom typing import Optional\nfrom aea.configurations.base import ConnectionConfig\nfrom aea.helpers.file_io import write_with_lock\nfrom aea.identity.base import Identity\nfrom aea.mail.base import Envelope\nfrom aea.multiplexer import Multiplexer\nfrom packages.fetchai.connections.stub.connection import StubConnection\nfrom packages.fetchai.protocols.default.message import DefaultMessage\nINPUT_FILE = \"input.txt\"\nOUTPUT_FILE = \"output.txt\"\ndef run():\n\"\"\"Run demo.\"\"\"\n# Ensure the input and output files do not exist initially\nif os.path.isfile(INPUT_FILE):\nos.remove(INPUT_FILE)\nif os.path.isfile(OUTPUT_FILE):\nos.remove(OUTPUT_FILE)\n# create the connection and multiplexer objects\nconfiguration = ConnectionConfig(\ninput_file=INPUT_FILE,\noutput_file=OUTPUT_FILE,\nconnection_id=StubConnection.connection_id,\n)\nstub_connection = StubConnection(\nconfiguration=configuration,\ndata_dir=\".\",\nidentity=Identity(\"some_agent\", \"some_address\", \"some_public_key\"),\n)\nmultiplexer = Multiplexer([stub_connection], protocols=[DefaultMessage])\ntry:\n# Set the multiplexer running in a different thread\nt = Thread(target=multiplexer.connect)\nt.start()\n# Wait for everything to start up\nfor _ in range(20):\nif multiplexer.is_connected:\nbreak\ntime.sleep(1)\nelse:\nraise Exception(\"Not connected\")\n# Create a message inside an envelope and get the stub connection to pass it into the multiplexer\nmessage_text = (\n\"multiplexer,some_agent,fetchai/default:1.0.0,\\x08\\x01*\\x07\\n\\x05hello,\"\n)\nwith open(INPUT_FILE, \"w\") as f:\nwrite_with_lock(f, message_text)\n# Wait for the envelope to get processed\nfor _ in range(20):\nif not multiplexer.in_queue.empty():\nbreak\ntime.sleep(1)\nelse:\nraise Exception(\"No message!\")\n# get the envelope\nenvelope = multiplexer.get()  # type: Optional[Envelope]\nassert envelope is not None\n# Inspect its contents\nprint(\n\"Envelope received by Multiplexer: sender={}, to={}, protocol_specification_id={}, message={}\".format(\nenvelope.sender,\nenvelope.to,\nenvelope.protocol_specification_id,\nenvelope.message,\n)\n)\n# Create a mirrored response envelope\nresponse_envelope = copy(envelope)\nresponse_envelope.to = envelope.sender\nresponse_envelope.sender = envelope.to\n# Send the envelope back\nmultiplexer.put(response_envelope)\n# Read the output envelope generated by the multiplexer\nwith open(OUTPUT_FILE, \"r\") as f:\nprint(\"Envelope received from Multiplexer: \" + f.readline())\nfinally:\n# Shut down the multiplexer\nmultiplexer.disconnect()\nt.join()\nif __name__ == \"__main__\":\nrun()\n
"},{"location":"aea-framework-documentation/oracle-demo/","title":"Oracle Skills","text":"

This demo shows how an AEA can be used to maintain an oracle and how another AEA can request the oracle value.

"},{"location":"aea-framework-documentation/oracle-demo/#discussion","title":"Discussion","text":"

Oracle agents are agents that have permission to update or validate updates to state variables in a smart contract and whose goal is to accurately estimate or predict some real world quantity or quantities.

This demonstration shows how to set up a simple oracle agent who deploys an oracle contract and updates the contract with a token price fetched from a public API. It also shows how to create an oracle client agent that can request the value from the oracle contract.

"},{"location":"aea-framework-documentation/oracle-demo/#preparation-instructions","title":"Preparation Instructions","text":""},{"location":"aea-framework-documentation/oracle-demo/#dependencies","title":"Dependencies","text":"

Follow the Preliminaries and Installation sections from the AEA quick start.

"},{"location":"aea-framework-documentation/oracle-demo/#demo","title":"Demo","text":""},{"location":"aea-framework-documentation/oracle-demo/#create-the-oracle-aea","title":"Create the Oracle AEA","text":"

Fetch the AEA that will deploy and update the oracle contract.

aea fetch fetchai/coin_price_oracle:0.17.6\ncd coin_price_oracle\naea install\n
Alternatively, create from scratch (and customize the data source):

Create the AEA that will deploy the contract.

aea create coin_price_oracle\ncd coin_price_oracle\naea add connection fetchai/http_client:0.24.6\naea add connection fetchai/ledger:0.21.5\naea add connection fetchai/prometheus:0.9.6\naea add skill fetchai/advanced_data_request:0.7.6\naea add skill fetchai/simple_oracle:0.16.5\naea config set --type dict agent.dependencies \\\n'{\n  \"aea-ledger-fetchai\": {\"version\": \"<2.0.0,>=1.0.0\"},\n  \"aea-ledger-ethereum\": {\"version\": \"<2.0.0,>=1.0.0\"}\n}'\naea config set agent.default_connection fetchai/ledger:0.21.5\naea install\n

Set the URL for the data request skill:

aea config set --type str vendor.fetchai.skills.advanced_data_request.models.advanced_data_request_model.args.url \"https://api.coingecko.com/api/v3/simple/price?ids=fetch-ai&vs_currencies=usd\"\n

Specify the name and JSON path of the data to fetch from the API:

aea config set --type list vendor.fetchai.skills.advanced_data_request.models.advanced_data_request_model.args.outputs '[{\"name\": \"price\", \"json_path\": \"fetch-ai.usd\"}]'\n

Set the name of the oracle value in the simple oracle skill:

aea config set vendor.fetchai.skills.simple_oracle.models.strategy.args.oracle_value_name price\n

Then update the agent configuration with the default routing:

aea config set --type dict agent.default_routing \\\n'{\n\"fetchai/contract_api:1.1.7\": \"fetchai/ledger:0.21.5\",\n\"fetchai/http:1.1.7\": \"fetchai/http_client:0.24.6\",\n\"fetchai/ledger_api:1.1.7\": \"fetchai/ledger:0.21.5\"\n}'\n

Update the default ledger.

aea config set agent.default_ledger fetchai\n

Set the following configuration for the oracle skill:

aea config set vendor.fetchai.skills.simple_oracle.models.strategy.args.ledger_id fetchai\naea config set vendor.fetchai.skills.simple_oracle.models.strategy.args.update_function update_oracle_value\n

This demo runs on the fetchai ledger by default. Set the following variable for use in the configuration steps:

LEDGER_ID=fetchai\n
Alternatively, configure the agent to use an ethereum ledger:
LEDGER_ID=ethereum\n

Update the default ledger.

aea config set agent.default_ledger ethereum\n

Set the following configuration for the oracle skill:

aea config set vendor.fetchai.skills.simple_oracle.models.strategy.args.ledger_id ethereum\naea config set vendor.fetchai.skills.simple_oracle.models.strategy.args.update_function updateOracleValue\n

Additionally, create the private key for the oracle AEA. Generate and add a key for use with the ledger:

aea generate-key $LEDGER_ID --add-key\n

If running on a testnet (not including Ganache), generate some wealth for your AEA:

aea generate-wealth $LEDGER_ID\n
"},{"location":"aea-framework-documentation/oracle-demo/#create-the-oracle-client-aea","title":"Create the Oracle Client AEA","text":"

From a new terminal (in the same top-level directory), fetch the AEA that will deploy the oracle client contract and call the function that requests the coin price from the oracle contract.

aea fetch fetchai/coin_price_oracle_client:0.12.6\ncd coin_price_oracle_client\naea install\n
Alternatively, create from scratch:

Create the AEA that will deploy the contract.

aea create coin_price_oracle_client\ncd coin_price_oracle_client\naea add connection fetchai/http_client:0.24.6\naea add connection fetchai/ledger:0.21.5\naea add skill fetchai/simple_oracle_client:0.13.5\naea config set --type dict agent.dependencies \\\n'{\n  \"aea-ledger-fetchai\": {\"version\": \"<2.0.0,>=1.0.0\"},\n  \"aea-ledger-ethereum\": {\"version\": \"<2.0.0,>=1.0.0\"}\n}'\naea config set agent.default_connection fetchai/ledger:0.21.5\naea install\n

Then update the agent configuration with the default routing:

aea config set --type dict agent.default_routing \\\n'{\n\"fetchai/contract_api:1.1.7\": \"fetchai/ledger:0.21.5\",\n\"fetchai/http:1.1.7\": \"fetchai/http_client:0.24.6\",\n\"fetchai/ledger_api:1.1.7\": \"fetchai/ledger:0.21.5\"\n}'\n

Set the default ledger:

aea config set agent.default_ledger fetchai\n
Set the following configuration for the oracle client skill:

aea config set vendor.fetchai.skills.simple_oracle_client.models.strategy.args.ledger_id fetchai\naea config set vendor.fetchai.skills.simple_oracle_client.models.strategy.args.query_function query_oracle_value\n

Similar to above, set a temporary variable LEDGER_ID=fetchai or LEDGER_ID=ethereum.

Follow these steps to configure for an ethereum ledger:

Set the default ledger:

aea config set agent.default_ledger ethereum\n

Set the following configuration for the oracle client skill:

aea config set vendor.fetchai.skills.simple_oracle_client.models.strategy.args.ledger_id ethereum\naea config set vendor.fetchai.skills.simple_oracle_client.models.strategy.args.query_function queryOracleValue\n

Create the private key for the oracle client AEA. Generate and add a key for use on the ledger:

aea generate-key $LEDGER_ID --add-key\n

If running on a testnet (not including Ganache), generate some wealth for your AEA:

aea generate-wealth $LEDGER_ID\n
"},{"location":"aea-framework-documentation/oracle-demo/#configuring-a-ledger","title":"Configuring a Ledger","text":"

The oracle AEAs require either a locally running ledger node or a connection to a remote ledger. By default, they are configured to use the latest fetchai testnet.

Follow these steps to configure local Ethereum test node:

The easiest way to test the oracle agents on an Ethereum-based ledger to set up a local test node using Ganache. This can be done by running the following docker command from the directory you started from (in a new terminal). This command will also fund the accounts of the AEAs:

docker run -p 8545:8545 trufflesuite/ganache-cli:latest --verbose --gasPrice=0 --gasLimit=0x1fffffffffffff --account=\"$(cat coin_price_oracle/ethereum_private_key.txt),1000000000000000000000\" --account=\"$(cat coin_price_oracle_client/ethereum_private_key.txt),1000000000000000000000\"\n

Run the following Python script (with web3 installed) from the top-level directory to deploy a mock Fetch ERC20 contract and give some test FET to the client agent.

import json\nimport os\nfrom web3 import Web3\nFILE_DIR = os.path.dirname(os.path.realpath(__file__))\nCONTRACT_PATH = os.path.join(FILE_DIR, \"coin_price_oracle_client/vendor/fetchai/contracts/fet_erc20/build/FetERC20Mock.json\")\nORACLE_PRIVATE_KEY_PATH = os.path.join(FILE_DIR, \"coin_price_oracle/ethereum_private_key.txt\")\nCLIENT_PRIVATE_KEY_PATH = os.path.join(FILE_DIR, \"coin_price_oracle_client/ethereum_private_key.txt\")\n# Solidity source code\nwith open(CONTRACT_PATH) as file:\ncompiled_sol = json.load(file)\n# web3.py instance\nw3 = Web3(Web3.HTTPProvider('http://127.0.0.1:8545'))\n# Import oracle account from private key and set to default account\nwith open(ORACLE_PRIVATE_KEY_PATH) as file:\nprivate_key = file.read()\noracle_account = w3.eth.account.privateKeyToAccount(private_key)\nw3.eth.defaultAccount = oracle_account.address\n# Import client account from private key\nwith open(CLIENT_PRIVATE_KEY_PATH) as file:\nprivate_key = file.read()\nclient_account = w3.eth.account.privateKeyToAccount(private_key)\n# Deploy mock Fetch ERC20 contract\nFetERC20Mock = w3.eth.contract(abi=compiled_sol['abi'], bytecode=compiled_sol['bytecode'])\n# Submit the transaction that deploys the contract\ntx_hash = FetERC20Mock.constructor(\nname=\"FetERC20Mock\",\nsymbol=\"MFET\",\ninitialSupply=int(1e23),\ndecimals_=18).transact()\n# Wait for the transaction to be mined, and get the transaction receipt\ntx_receipt = w3.eth.waitForTransactionReceipt(tx_hash)\n# Print out the contract address\nprint(\"FetERC20Mock contract deployed at:\", tx_receipt.contractAddress)\n# Get deployed contract\nfet_erc20_mock = w3.eth.contract(address=tx_receipt.contractAddress, abi=compiled_sol['abi'])\n# Transfer some test FET to oracle client account\ntx_hash = fet_erc20_mock.functions.transfer(client_account.address, int(1e20)).transact()\ntx_receipt = w3.eth.waitForTransactionReceipt(tx_hash)\n

Set the ERC20 contract address for the oracle AEA

aea config set vendor.fetchai.skills.simple_oracle.models.strategy.args.erc20_address $ERC20_ADDRESS\n

as well as for the oracle client AEA

aea config set vendor.fetchai.skills.simple_oracle_client.models.strategy.args.erc20_address $ERC20_ADDRESS\n

where ERC20_ADDRESS is in the output of the script above.

"},{"location":"aea-framework-documentation/oracle-demo/#run-the-oracle-aea","title":"Run the Oracle AEA","text":"

Run the oracle agent. This will deploy a contract to the testnet, grant oracle permissions to the AEA's wallet address, and periodically update the contract with the latest price of FET (or whichever coin was specified).

aea run\n

After a few moments, you should see the following notices in the logs:

info: [coin_price_oracle] Oracle contract successfully deployed at address: ...\n...\ninfo: [coin_price_oracle] Oracle role successfully granted!\n...\ninfo: [coin_price_oracle] Oracle value successfully updated!\n

The oracle contract will continue to be updated with the latest retrieved coin price at the default time interval (every 15 seconds).

"},{"location":"aea-framework-documentation/oracle-demo/#set-the-erc20-and-oracle-contract-addresses-for-the-oracle-client-aea","title":"Set the ERC20 and Oracle Contract Addresses for the Oracle Client AEA","text":"
aea config set vendor.fetchai.skills.simple_oracle_client.models.strategy.args.oracle_contract_address $ORACLE_ADDRESS\n

where ORACLE_ADDRESS should be set to the address shown in the oracle AEA logs:

Oracle contract successfully deployed at address: ORACLE_ADDRESS\n
"},{"location":"aea-framework-documentation/oracle-demo/#run-the-oracle-client-aea","title":"Run the Oracle Client AEA","text":"

Run the oracle client agent. This will deploy an oracle client contract to the testnet, approve the contract to spend tokens on behalf of the AEA, and periodically call the contract function that requests the latest price of FET (or whichever coin was specified).

aea run\n

After a few moments, you should see the following notices in the logs:

info: [coin_price_oracle_client] Oracle client contract successfully deployed at address: ...\n...\ninfo: [coin_price_oracle_client] Oracle value successfully requested!\n

The AEA will continue to request the latest coin price at the default time interval (every 15 seconds).

"},{"location":"aea-framework-documentation/orm-integration/","title":"ORM Integration","text":"

This guide demonstrates how to configure an AEA to interact with a database using python-sql objects.

"},{"location":"aea-framework-documentation/orm-integration/#discussion","title":"Discussion","text":"

Object-relational-mapping (ORM) is the idea of being able to write SQL queries, using the object-oriented paradigm of your preferred programming language. The scope of this guide is to demonstrate how you can create an easily configurable AEA that reads data from a database using ORMs.

  • We assume, that you followed the guide for the thermometer-skills.
  • We assume, that we have a database genericdb.db with table name data. This table contains the following columns timestamp and thermometer.
  • We assume, that we have a hardware thermometer sensor that adds the readings in the genericdb database (although you can follow the guide without having access to a sensor).

Since the AEA framework enables us to use third-party libraries hosted on PyPI we can directly reference the external dependencies. The aea install command will install each dependency that the specific AEA needs and which is listed in the skill's YAML file.

"},{"location":"aea-framework-documentation/orm-integration/#communication","title":"Communication","text":"

This diagram shows the communication between the various entities in the case where the thermometer data is successfully sold by the seller AEA to the buyer.

    sequenceDiagram\n        participant Search\n        participant Buyer_AEA\n        participant Seller_AEA\n        participant Blockchain\n\n        activate Buyer_AEA\n        activate Search\n        activate Seller_AEA\n        activate Blockchain\n\n        Seller_AEA->>Search: register_service\n        Buyer_AEA->>Search: search\n        Search-->>Buyer_AEA: list_of_agents\n        Buyer_AEA->>Seller_AEA: call_for_proposal\n        Seller_AEA->>Buyer_AEA: propose\n        Buyer_AEA->>Seller_AEA: accept\n        Seller_AEA->>Buyer_AEA: match_accept\n        Buyer_AEA->>Blockchain: transfer_funds\n        Buyer_AEA->>Seller_AEA: send_transaction_hash\n        Seller_AEA->>Blockchain: check_transaction_status\n        Seller_AEA->>Buyer_AEA: send_data\n\n        deactivate Buyer_AEA\n        deactivate Search\n        deactivate Seller_AEA\n        deactivate Blockchain    
"},{"location":"aea-framework-documentation/orm-integration/#preparation-instructions","title":"Preparation Instructions","text":""},{"location":"aea-framework-documentation/orm-integration/#dependencies","title":"Dependencies","text":"

Follow the Preliminaries and Installation sections from the AEA quick start.

"},{"location":"aea-framework-documentation/orm-integration/#demo-instructions","title":"Demo Instructions","text":"

This demo involves a true ledger transaction on Fetch.ai's testnet network or Ethereum's ropsten. This demo assumes the buyer trusts the seller AEA to send the data upon successful payment.

"},{"location":"aea-framework-documentation/orm-integration/#create-the-seller-aea","title":"Create the Seller AEA","text":"

First, fetch the seller AEA which provides thermometer data:

aea fetch fetchai/thermometer_aea:0.30.5 --alias my_thermometer_aea\ncd my_thermometer_aea\naea install\naea build\n
Alternatively, create from scratch:

The following steps create the seller from scratch:

aea create my_thermometer_aea\ncd my_thermometer_aea\naea add connection fetchai/p2p_libp2p:0.27.5\naea add connection fetchai/soef:0.27.6\naea add connection fetchai/ledger:0.21.5\naea add skill fetchai/thermometer:0.27.6\naea config set --type dict agent.dependencies \\\n'{\n  \"aea-ledger-fetchai\": {\"version\": \"<2.0.0,>=1.0.0\"}\n}'\naea config set agent.default_connection fetchai/p2p_libp2p:0.27.5\naea config set --type dict agent.default_routing \\\n'{\n  \"fetchai/ledger_api:1.1.7\": \"fetchai/ledger:0.21.5\",\n  \"fetchai/oef_search:1.1.7\": \"fetchai/soef:0.27.6\"\n}'\naea install\naea build\n
"},{"location":"aea-framework-documentation/orm-integration/#create-the-buyer-client","title":"Create the Buyer Client","text":"

In another terminal, fetch the buyer AEA:

aea fetch fetchai/thermometer_client:0.32.5 --alias my_thermometer_client\ncd my_thermometer_client\naea install\naea build\n
Alternatively, create from scratch:

The following steps create the car data client from scratch:

aea create my_thermometer_client\ncd my_thermometer_client\naea add connection fetchai/p2p_libp2p:0.27.5\naea add connection fetchai/soef:0.27.6\naea add connection fetchai/ledger:0.21.5\naea add skill fetchai/thermometer_client:0.26.6\naea config set --type dict agent.dependencies \\\n'{\n  \"aea-ledger-fetchai\": {\"version\": \"<2.0.0,>=1.0.0\"}\n}'\naea config set agent.default_connection fetchai/p2p_libp2p:0.27.5\naea config set --type dict agent.default_routing \\\n'{\n  \"fetchai/ledger_api:1.1.7\": \"fetchai/ledger:0.21.5\",\n  \"fetchai/oef_search:1.1.7\": \"fetchai/soef:0.27.6\"\n}'\naea install\naea build\n
"},{"location":"aea-framework-documentation/orm-integration/#add-keys-for-the-seller-aea","title":"Add Keys for the Seller AEA","text":"

First, create the private key for the seller AEA based on the network you want to transact. To generate and add a private-public key pair for Fetch.ai Dorado use:

aea generate-key fetchai\naea add-key fetchai fetchai_private_key.txt\n

Next, create a private key used to secure the AEA's communications:

aea generate-key fetchai fetchai_connection_private_key.txt\naea add-key fetchai fetchai_connection_private_key.txt --connection\n

Finally, certify the key for use by the connections that request that:

aea issue-certificates\n
"},{"location":"aea-framework-documentation/orm-integration/#add-keys-and-generate-wealth-for-the-buyer-aea","title":"Add Keys and Generate Wealth for the Buyer AEA","text":"

The buyer needs to have some wealth to purchase the thermometer data.

First, create the private key for the buyer AEA based on the network you want to transact. To generate and add a private-public key pair for Fetch.ai use:

aea generate-key fetchai\naea add-key fetchai fetchai_private_key.txt\n

Then, create some wealth for the buyer based on the network you want to transact with. On the Fetch.ai Dorado network:

aea generate-wealth fetchai\n

Next, create a private key used to secure the AEA's communications:

aea generate-key fetchai fetchai_connection_private_key.txt\naea add-key fetchai fetchai_connection_private_key.txt --connection\n

Finally, certify the key for use by the connections that request that:

aea issue-certificates\n
"},{"location":"aea-framework-documentation/orm-integration/#update-the-seller-and-buyer-aea-skill-configurations","title":"Update the Seller and Buyer AEA Skill Configurations","text":"

In my_thermometer_aea/vendor/fetchai/skills/thermometer/skill.yaml, replace the data_for_sale with your data:

models:\n...\nstrategy:\nargs:\ncurrency_id: FET\ndata_for_sale:\ntemperature: 26\nhas_data_source: false\nis_ledger_tx: true\nledger_id: fetchai\nlocation:\nlatitude: 51.5194\nlongitude: 0.127\nservice_data:\nkey: seller_service\nvalue: thermometer_data\nservice_id: thermometer_data\nunit_price: 10\nclass_name: Strategy\ndependencies:\nSQLAlchemy: {}\n

The service_data is used to register the service in the SOEF search node and make your agent discoverable.

In my_thermometer_client/vendor/fetchai/skills/thermometer_client/skill.yaml) ensure you have matching data.

models:\n...\nstrategy:\nargs:\ncurrency_id: FET\nis_ledger_tx: true\nledger_id: fetchai\nlocation:\nlatitude: 51.5194\nlongitude: 0.127\nmax_negotiations: 1\nmax_tx_fee: 3550000000000000\nmax_unit_price: 20\nsearch_query:\nconstraint_type: ==\nsearch_key: seller_service\nsearch_value: thermometer_data\nsearch_radius: 5.0\nservice_id: thermometer_data\nclass_name: Strategy\n

After changing the skill configuration files you should run the following command for both agents to install each dependency:

aea install\n
"},{"location":"aea-framework-documentation/orm-integration/#modify-the-sellers-strategy","title":"Modify the Seller's Strategy","text":"

Before being able to modify a package we need to eject it from vendor:

aea eject skill fetchai/thermometer:0.27.6\n

This will move the package to your skills directory and reset the version to 0.1.0 and the author to your author handle.

Open strategy.py (in my_thermometer_aea/skills/thermometer/strategy.py) and make the following modifications:

Import the newly installed sqlalchemy library in your strategy.

import sqlalchemy as db\n

Then modify your strategy's __init__ function to match the following code:

class Strategy(GenericStrategy):\n\"\"\"This class defines a strategy for the agent.\"\"\"\ndef __init__(self, **kwargs) -> None:\n\"\"\"\n        Initialize the strategy of the agent.\n        :param register_as: determines whether the agent registers as seller, buyer or both\n        :param search_for: determines whether the agent searches for sellers, buyers or both\n        :return: None\n        \"\"\"\nself._db_engine = db.create_engine(\"sqlite:///genericdb.db\")\nself._tbl = self.create_database_and_table()\nself.insert_data()\nsuper().__init__(**kwargs)\n

At the end of the file modify the collect_from_data_source function:

    def collect_from_data_source(self) -> Dict[str, str]:\n\"\"\"Implement the logic to collect data.\"\"\"\nconnection = self._db_engine.connect()\nquery = db.select([self._tbl])\nresult_proxy = connection.execute(query)\ndata_points = result_proxy.fetchall()\nreturn {\"data\": json.dumps(list(map(tuple, data_points)))}\n

Also, create two new functions, one that creates a connection with the database, and another that populates the database with some fake data. This is needed in the case you do not have access to an actual thermometer sensor that inserts data in the database.

    def create_database_and_table(self):\n\"\"\"Creates a database and a table to store the data if not exists.\"\"\"\nmetadata = db.MetaData()\ntbl = db.Table(\n\"data\",\nmetadata,\ndb.Column(\"timestamp\", db.Integer()),\ndb.Column(\"temprature\", db.String(255), nullable=False),\n)\nmetadata.create_all(self._db_engine)\nreturn tbl\ndef insert_data(self):\n\"\"\"Insert data in the database.\"\"\"\nconnection = self._db_engine.connect()\nfor _ in range(10):\nquery = db.insert(self._tbl).values(  # nosec\ntimestamp=time.time(), temprature=str(random.randrange(10, 25))\n)\nconnection.execute(query)\n

After modifying the skill we need to fingerprint it:

aea fingerprint skill {YOUR_AUTHOR_HANDLE}/thermometer:0.1.0\n
"},{"location":"aea-framework-documentation/orm-integration/#run-both-aeas","title":"Run Both AEAs","text":"

First, run the thermometer (seller) AEA:

aea run\n

Once you see a message of the form To join its network use multiaddr 'SOME_ADDRESS' take note of this address. (Alternatively, use aea get-multiaddress fetchai -c -i fetchai/p2p_libp2p:0.27.5 -u public_uri to retrieve the address.) This is the entry peer address for the local agent communication network created by the thermometer AEA.

Then, configure the thermometer client (buyer) to connect to this same local ACN by running the following command in the buyer terminal, replacing SOME_ADDRESS with the value you noted above:

aea config set --type dict vendor.fetchai.connections.p2p_libp2p.config \\\n'{\n  \"delegate_uri\": \"127.0.0.1:11001\",\n  \"entry_peers\": [\"SOME_ADDRESS\"],\n  \"local_uri\": \"127.0.0.1:9001\",\n  \"log_file\": \"libp2p_node.log\",\n  \"public_uri\": \"127.0.0.1:9001\"\n}'\n

Then run the thermometer client AEA:

aea run\n

You will see that the AEAs negotiate and then transact using the configured testnet.

"},{"location":"aea-framework-documentation/orm-integration/#delete-the-aeas","title":"Delete the AEAs","text":"

When you're done, stop the agents (CTRL+C), go up a level and delete the AEAs.

cd ..\naea delete my_thermometer_aea\naea delete my_thermometer_client\n
"},{"location":"aea-framework-documentation/p2p-connection/","title":"P2P Connection","text":"

The fetchai/p2p_libp2p:0.27.5 connection allows AEAs to create a peer-to-peer communication network. In particular, the connection creates an overlay network which maps agents' public keys to IP addresses.

"},{"location":"aea-framework-documentation/p2p-connection/#local-demo","title":"Local Demo","text":"

First, make sure you have installed the crypto plugin of the target test-net. E.g. for Fetch.AI:

pip install aea-ledger-fetchai\n
"},{"location":"aea-framework-documentation/p2p-connection/#create-and-run-the-genesis-aea","title":"Create and Run the Genesis AEA","text":"

Create one AEA as follows:

aea create my_genesis_aea\ncd my_genesis_aea\naea add connection fetchai/p2p_libp2p:0.27.5\naea config set agent.default_connection fetchai/p2p_libp2p:0.27.5\naea install\naea build\n

Establish the proof of representation:

aea generate-key fetchai\naea add-key fetchai fetchai_private_key.txt\naea generate-key fetchai fetchai_connection_private_key.txt\naea add-key fetchai fetchai_connection_private_key.txt --connection\naea issue-certificates\n

Run the AEA:

aea run --connections fetchai/p2p_libp2p:0.27.5\n

Once you see a message of the form To join its network use multiaddr 'SOME_ADDRESS' take note of the address. (Alternatively, use aea get-multiaddress fetchai -c -i fetchai/p2p_libp2p:0.27.5 -u public_uri to retrieve the address.) This is the entry peer address for the local agent communication network created by the genesis AEA.

"},{"location":"aea-framework-documentation/p2p-connection/#create-and-run-another-aea","title":"Create and Run Another AEA","text":"

Create a second AEA:

aea create my_other_aea\ncd my_other_aea\naea add connection fetchai/p2p_libp2p:0.27.5\naea config set agent.default_connection fetchai/p2p_libp2p:0.27.5\naea install\naea build\n

Establish the proof of representation:

aea generate-key fetchai\naea add-key fetchai fetchai_private_key.txt\naea generate-key fetchai fetchai_connection_private_key.txt\naea add-key fetchai fetchai_connection_private_key.txt --connection\naea issue-certificates\n

Provide the AEA with the information it needs to find the genesis:

aea config set --type dict vendor.fetchai.connections.p2p_libp2p.config \\\n'{\n  \"delegate_uri\": \"127.0.0.1:11001\",\n  \"entry_peers\": [\"SOME_ADDRESS\"],\n  \"local_uri\": \"127.0.0.1:9001\",\n  \"log_file\": \"libp2p_node.log\",\n  \"public_uri\": \"127.0.0.1:9001\"\n}'\n

Here SOME_ADDRESS needs to be replaced with the list of multi addresses displayed in the log output of the genesis AEA.

Run the AEA:

aea run --connections fetchai/p2p_libp2p:0.27.5\n

You can inspect the libp2p_node.log log files of the AEA to see how they discover each other.

Note

Currently p2p_libp2p connection limits the total message size to 3 MB.

"},{"location":"aea-framework-documentation/p2p-connection/#local-demo-with-skills","title":"Local Demo with Skills","text":"

Explore the demo section for further examples.

"},{"location":"aea-framework-documentation/p2p-connection/#deployed-agent-communication-network","title":"Deployed Agent Communication Network","text":"

You can connect to the deployed public test network by adding one or multiple of the following addresses as the p2p_libp2p connection's entry_peers:

/dns4/acn.fetch.ai/tcp/9000/p2p/16Uiu2HAkw1ypeQYQbRFV5hKUxGRHocwU5ohmVmCnyJNg36tnPFdx\n
/dns4/acn.fetch.ai/tcp/9001/p2p/16Uiu2HAmVWnopQAqq4pniYLw44VRvYxBUoRHqjz1Hh2SoCyjbyRW\n

Specifically, in an AEA's configuration aea-config.yaml add the above addresses for entry_peers as follows:

---\npublic_id: fetchai/p2p_libp2p:0.27.5\ntype: connection\nconfig:\ndelegate_uri: null\nentry_peers: [/dns4/acn.fetch.ai/tcp/9000/p2p/16Uiu2HAkw1ypeQYQbRFV5hKUxGRHocwU5ohmVmCnyJNg36tnPFdx,/dns4/acn.fetch.ai/tcp/9001/p2p/16Uiu2HAmVWnopQAqq4pniYLw44VRvYxBUoRHqjz1Hh2SoCyjbyRW]\npublic_uri: null\nlocal_uri: 127.0.0.1:9001\n

Note, this configuration change must be made for all agents attempting to communicate with each other via the Agent Communication Network. For example, in demos involving two agents, both agents will need the above modifications to their respective aea-config.yaml file. However, remember to use different ports in local_uri. This will allow both agents to default to this communication network without the added overhead of opening ports and specifying hosts on the individual host machines running each agent.

"},{"location":"aea-framework-documentation/p2p-connection/#configuring-the-connectionyaml-entries","title":"Configuring the connection.yaml Entries","text":"

To learn more about how to configure your fetchai/p2p_libp2p:0.27.5 connection consult the README.md file supplied with the connection package.

"},{"location":"aea-framework-documentation/p2p-connection/#running-go-peer-standalone","title":"Running Go Peer Standalone","text":"

You can run a peer node in standalone mode; that is, as a Go process with no dependency on the AEA framework. To facilitate such a deployment, we provide a script run_acn_node_standalone.py and a corresponding Dockerfile.

First, you need to build the node's binary (libp2p_node) either:

  • locally

    svn export https://github.com/fetchai/agents-aea.git/trunk/packages/fetchai/connections/p2p_libp2p\ncd p2p_libp2p\ngo build\nchmod +x libp2p_node\n

    Make sure you satisfy the system requirements.

  • or within a docker image using the provided Dockerfile:

    docker build -t acn_node_standalone -f scripts/acn/Dockerfile .\n

Next, to run the node binary in standalone mode, it requires values for the following entries:

  • AEA_P2P_ID: the node's private key, will be used as its identity
  • AEA_P2P_URI: the local host and port to use by node
  • AEA_P2P_URI_PUBLIC: the URI under which the peer is publicly reachable
  • AEA_P2P_DELEGATE_URI: the URI under which the peer receives delegate connections
  • AEA_P2P_ENTRY_URIS: an optionally supplied list of comma-separated (,) entry multiaddresses for the peer to bootstrap

The script allows different methods to pass these values to the node:

  • As environment variables exported in the format <ENTRY_KEYWORD>=<ENTRY_VALUE> for each entry. Then:

    python3 run_acn_node_standalone.py libp2p_node --config-from-env\n
  • Using an environment file containing the entries and their values in the format <ENTRY_KEYWORD>=<ENTRY_VALUE>, one entry per line. Then:

    python3 run_acn_node_standalone.py libp2p_node --config-from-file <env-file-path>\n

    or

    docker run -v <acn_config_file>:/acn/acn_config -it acn_node_standalone --config-from-file /acn/acn_config\n
  • Using command line arguments:

    python3 run_acn_node_standalone.py libp2p_node --key-file <node_private_key.txt> \\\n--uri <AEA_P2P_URI> --uri-external <AEA_P2P_URI_PUBLIC>  \\\n--uri-delegate <AEA_P2P_DELEGATE_URI> \\\n--entry-peers-maddrs <AEA_P2P_ENTRY_URI_1> <AEA_P2P_ENTRY_URI_2> ...\n

    or

    docker run -v <node_private_key.txt>:/acn/key.txt -it acn_node_standalone --key-file /acn/key.txt \\\n--uri <AEA_P2P_URI> --uri-external <AEA_P2P_URI_PUBLIC>  \\\n--uri-delegate <AEA_P2P_DELEGATE_URI> \\\n--entry-peers-maddrs <AEA_P2P_ENTRY_URI_1> <AEA_P2P_ENTRY_URI_2> ...\n

Note that the script will always save the configuration of the running node as a file under the name .acn_config in the current working directory. This can be handy when you want the exact same configuration for future runs of the node.

"},{"location":"aea-framework-documentation/package-imports/","title":"File Structure","text":"

An agent that is generated using the AEA framework is a modular system with different connections, contracts, protocols and skills.

"},{"location":"aea-framework-documentation/package-imports/#an-aea-projects-file-structure","title":"An AEA Project's File Structure","text":"

The file structure of an AEA is fixed.

The top level directory has the AEA's name. Below is a aea-config.yaml configuration file, then directories containing the connections, contracts, protocols, and skills developed by the developer as part of the given project. The connections, contracts, protocols and skills used from the registry (local or remote - added via aea fetch or aea add) are located in vendor and sorted by author. Build artefacts are placed in the .build/ directory and certificates are placed in the .certs/ directory. Finally, there are files containing the private keys of the AEA.

When we create a new agent with the command aea create my_aea we create the file structure that looks like the following:

aea_name/\n  aea-config.yaml       YAML configuration of the AEA\n  fetchai_private_key.txt   The private key file\n  connections/          Directory containing all the connections developed as part of the given project.\n    connection_1/       First connection\n    ...                 ...\n    connection_n/       nth connection\n  contracts/            Directory containing all the contracts developed as part of the given project.\n    connection_1/       First connection\n    ...                 ...\n    connection_n/       nth connection\n  protocols/            Directory containing all the protocols developed as part of the given project.\n    protocol_1/         First protocol\n    ...                 ...\n    protocol_m/         mth protocol\n  skills/               Directory containing all the skills developed as part of the given project.\n    skill_1/            First skill\n    ...                 ...\n    skill_k/            kth skill\n  vendor/               Directory containing all the added resources from the registry, sorted by author.\n    author_1/           Directory containing all the resources added from author_1\n      connections/      Directory containing all the added connections from author_1\n        ...             ...\n      protocols/        Directory containing all the added protocols from author_1\n        ...             ...\n      skills/           Directory containing all the added skills from author_1\n        ...             ...\n

The developer can create new directories where necessary but the core structure must remain the same.

"},{"location":"aea-framework-documentation/package-imports/#aea-configuration-yaml","title":"AEA Configuration YAML","text":"

The aea-config.yaml is the top level configuration file of an AEA. It defines the global configurations as well as the component/package dependencies of the AEA. In some sense, the AEA can therefore be understood as an orchestrator of components.

For the AEA to use a package, the public_id for the package must be listed in the aea-config.yaml file, e.g.

connections:\n- fetchai/stub:0.21.3\n

The above shows a part of the aea-config.yaml. If you see the connections, you will see that we follow a pattern of author/name_package:version to identify each package, also referred to as public_id. Here the author is the author of the package.

"},{"location":"aea-framework-documentation/package-imports/#vendor-and-package-directories","title":"Vendor and Package Directories","text":"

The vendor folder contains the packages from the registry (local or remote) which have been developed by ourselves, other authors or Fetch.ai and are placed in different namespaces according to the author name.

The packages we develop as part of the given AEA project are in the respective connections/, contracts/, protocols/, and skills/ folders.

In the above configuration example, the package is authored by Fetch.ai and is located inside the vendor/fetchai/connections folder.

"},{"location":"aea-framework-documentation/package-imports/#importing-modules-from-packages","title":"Importing Modules from Packages","text":"

The way we import modules from packages inside the agent is in the form of packages.{author}.{package_type}.{package_name}.{module_name}. So for the above example, the import path is packages.fetchai.connections.stub.{module_name}.

The framework loads the modules from the local agent project and adds them to Python's sys.modules under the respective path.

We use a custom package management approach for the AEAs rather than the default Python one as it provides us with more flexibility, especially when it comes to extension beyond the Python ecosystem.

"},{"location":"aea-framework-documentation/package-imports/#python-dependencies-of-packages","title":"Python Dependencies of Packages","text":"

Python dependencies of packages are specified in their respective configuration files under dependencies. They will be installed when aea install is run on an agent project.

"},{"location":"aea-framework-documentation/package-imports/#create-a-package","title":"Create a Package","text":"

If you want to create a package, you can use the CLI command aea scaffold connection/contract/protocol/skill [name] and this will create the package and put it inside the respective folder based on the command for example if we scaffold skill with the name my_skill it will be located inside the folder skills in the root directory of the agent (my_aea/skills/my_skill).

"},{"location":"aea-framework-documentation/package-imports/#use-published-packages-from-the-registry","title":"Use Published Packages from the Registry","text":"

If you want to use a finished package, you can use a package from the registry.

There or two registries. The remote registry operated by Fetch.ai and a local registry stub. The local registry stub is a directory called packages which contains packages in a nested structure with authors on the top level, followed by the package type, then package name. An example of such a directory is the packages directory located in the AEA repository. The local registry is useful for development.

You can use the CLI to interact with the registry. By default, the CLI points to the remote registry. You can point it to the local registry via the flag --local.

"},{"location":"aea-framework-documentation/package-imports/#package-versioning","title":"Package Versioning","text":"

By default, the AEA can only handle one version per package. That is, a project should never use both some_author/some_package_name:0.1.0 and some_author/some_package_name:0.2.0.

If two AEA packages with the same author and name but different versions are used in the same Python process, then only the code from one of the packages (generally not deterministic) will be available in sys.modules. This can lead to inconsistencies and exceptions at runtime.

"},{"location":"aea-framework-documentation/performance-benchmark/","title":"Performance Benchmark","text":"

Test AEA framework performance.

"},{"location":"aea-framework-documentation/performance-benchmark/#what-is-it","title":"What is it?","text":"

The benchmark module is a set of tools to measure execution time, CPU load and memory usage of the AEA Python code. It produces text reports and draws charts to present the results.

"},{"location":"aea-framework-documentation/performance-benchmark/#how-does-it-work","title":"How does it Work?","text":"

The framework:

  • spawns a dedicated process for each test run to execute the function to test.
  • measures CPU and RAM usage periodically.
  • waits for function exits or terminates them by timeout.
  • repeats test execution multiple times to get more accurate results.
"},{"location":"aea-framework-documentation/performance-benchmark/#how-to-use","title":"How to Use","text":"

Steps to run a test:

  • Write a function you would like to test with all arguments you would like to parametrise, add some doc strings.
  • Split the function into two parts: 'prepare' and 'performance' part. The 'prepare' part will not be included in the measurement.
  • Add BenchmarkControl support, to notify framework to start measurement.
  • Import TestCli class, TestCli().run(function_to_be_tested)
  • Call it from console to get text results.
"},{"location":"aea-framework-documentation/performance-benchmark/#simple-example","title":"Simple Example","text":"

cpuburn - simple test of CPU load depends on idle sleep time. Shows how much CPU consumed during the execution.

import time\nfrom benchmark.framework.benchmark import BenchmarkControl\nfrom benchmark.framework.cli import TestCli\ndef cpu_burn(benchmark: BenchmarkControl, run_time=10, sleep=0.0001) -> None:\n\"\"\"\n    Do nothing, just burn cpu to check cpu load changed on sleep.\n    :param benchmark: benchmark special parameter to communicate with executor\n    :param run_time: time limit to run this function\n    :param sleep: time to sleep in loop\n    :return: None\n    \"\"\"\nbenchmark.start()\nstart_time = time.time()\nwhile True:\ntime.sleep(sleep)\nif time.time() - start_time >= run_time:\nbreak\nif __name__ == \"__main__\":\nTestCli(cpu_burn).run()\n

Run it with python ./benchmark/cases/cpu_burn.py --help to get help about usage.

Usage: cpu_burn.py [OPTIONS] [ARGS]...\n\nDo nothing, just burn cpu to check cpu load changed on sleep.\n\n:param benchmark: benchmark special parameter to communicate with executor\n  :param run_time: time limit to run this function :param sleep: time to sleep in loop\n\n:return: None\n\nARGS is function arguments in format: `run_time,sleep`\ndefault ARGS is `10,0.0001`\nOptions:\n  --timeout FLOAT               Executor timeout in seconds  [default: 10.0]\n--period FLOAT                Period for measurement  [default: 0.1]\n-N, --num-executions INTEGER  Number of runs for each case  [default: 1]\n-P, --plot INTEGER            X axis parameter idx\n  --help                        Show this message and exit.\n

Run it with python ./benchmark/cases/cpu_burn.py to start with default parameters.

Test execution timeout: 10.0\nTest execution measure period: 0.1\nTested function name: cpu_burn\nTested function description:\n    Do nothing, just burn cpu to check cpu load changed on sleep.\n\n:param benchmark: benchmark special parameter to communicate with executor\n    :param run_time: time limit to run this function\n:param sleep: time to sleep in loop\n\n:return: None\n\nTested function argument names: ['run_time', 'sleep']\nTested function argument default values: [10, 0.0001]\n== Report created 2020-04-27 15:14:56.076549 ==\nArguments are `[10, 0.0001]`\nNumber of runs: 1\nNumber of time terminated: 0\nTime passed (seconds): 10.031443119049072 \u00b1 0\ncpu min (%): 0.0 \u00b1 0\ncpu max (%): 10.0 \u00b1 0\ncpu mean (%): 3.4 \u00b1 0\nmem min (kb): 53.98828125 \u00b1 0\nmem max (kb): 53.98828125 \u00b1 0\nmem mean (kb): 53.98828125 \u00b1 0\n

Here you can see test report for default arguments set.

Run with multiple arguments set, multiple repeats and draw a chart on resources python ./benchmark/cases/cpu_burn.py -N 5 -P 1 3,0.00001 3,0.001 3,0.01

Report is:

Test execution timeout: 10.0\nTest execution measure period: 0.1\nTested function name: cpu_burn\nTested function description:\n    Do nothing, just burn cpu to check cpu load changed on sleep.\n\n:param benchmark: benchmark special parameter to communicate with executor\n    :param run_time: time limit to run this function\n:param sleep: time to sleep in loop\n\n:return: None\n\nTested function argument names: ['run_time', 'sleep']\nTested function argument default values: [10, 0.0001]\n== Report created 2020-04-27 15:38:17.849535 ==\nArguments are `(3, 1e-05)`\nNumber of runs: 5\nNumber of time terminated: 0\nTime passed (seconds): 3.0087939262390138 \u00b1 0.0001147521277690166\ncpu min (%): 0.0 \u00b1 0.0\ncpu max (%): 11.0 \u00b1 2.23606797749979\ncpu mean (%): 6.2 \u00b1 0.18257418583505522\nmem min (kb): 54.0265625 \u00b1 0.11180339887498948\nmem max (kb): 54.0265625 \u00b1 0.11180339887498948\nmem mean (kb): 54.0265625 \u00b1 0.11180339887498948\n== Report created 2020-04-27 15:38:32.947308 ==\nArguments are `(3, 0.001)`\nNumber of runs: 5\nNumber of time terminated: 0\nTime passed (seconds): 3.014109659194946 \u00b1 0.0004416575764579524\ncpu min (%): 0.0 \u00b1 0.0\ncpu max (%): 8.0 \u00b1 2.7386127875258306\ncpu mean (%): 1.9986666666666666 \u00b1 0.002981423969999689\nmem min (kb): 53.9890625 \u00b1 0.10431954926750306\nmem max (kb): 53.9890625 \u00b1 0.10431954926750306\nmem mean (kb): 53.9890625 \u00b1 0.10431954926750306\n== Report created 2020-04-27 15:38:48.067511 ==\nArguments are `(3, 0.01)`\nNumber of runs: 5\nNumber of time terminated: 0\nTime passed (seconds): 3.0181806087493896 \u00b1 0.0022409499756841883\ncpu min (%): 0.0 \u00b1 0.0\ncpu max (%): 1.0 \u00b1 2.23606797749979\ncpu mean (%): 0.06666666666666667 \u00b1 0.14907119849998599\nmem min (kb): 53.9078125 \u00b1 0.11487297672320501\nmem max (kb): 53.9078125 \u00b1 0.11487297672320501\nmem mean (kb): 53.9078125 \u00b1 0.11487297672320501\n

Chart is drawn for argument 1: sleep:

The most interesting part is CPU usage, as you can see CPU usage decreases with increasing value of idle sleep. Memory usage and execution time can slightly differ per case execution.

"},{"location":"aea-framework-documentation/performance-benchmark/#requirements-for-tested-function","title":"Requirements for Tested Function","text":"
  • The first function's argument has to be benchmark: BenchmarkControl which is passed by default by the framework.
  • All arguments except the fist one have to set default values.
  • Function doc string is required, it used for help information.
  • benchmark.start() has to be called once in the function body to start measurement. The timeout is counted from this point!
  • All the \"prepare part\" in the function that should not be measured has to be placed before benchmark.start()
  • Code to be measured has to go after benchmark.start()
  • Try to avoid infinitive loops and assume the test should exit after a while.
"},{"location":"aea-framework-documentation/performance-benchmark/#execution-options","title":"Execution Options","text":"
  • To pass an arguments set just provide it as a comma separated string like 10,0.1
  • To pass several argument sets just separate them by white space 10,0.1 20,0.2
  • --timeout FLOAT is test execution timeout in seconds. If the test takes more time, it will be terminated.
  • --period FLOAT is measurement interval in seconds, how often to make CPU and RAM usage measurements.
  • -N, --num-executions INTEGER - how many times to run the same argument set to make result more accurate.
  • -P, --plot INTEGER - Draw a chart, using values in the argument on the X axis, argument positions started with 0, argument benchmark not counted. For example -P 0 will use run_time values, -P 1 will use sleep values, and so on.
"},{"location":"aea-framework-documentation/performance-benchmark/#limitations","title":"Limitations","text":"

Currently, the benchmark framework does not measure resources consumed by subprocess spawned in python code. So try to keep one process solutions during tests.

Asynchronous functions or coroutines are not supported directly. So you have to set up an event loop inside test function and start loop manually.

"},{"location":"aea-framework-documentation/performance-benchmark/#testing-aea-handlers-example","title":"Testing AEA: Handlers Example","text":"

Test react speed on specific messages amount.

def react_speed_in_loop(benchmark: BenchmarkControl, inbox_amount=1000) -> None:\n\"\"\"\n    Test inbox message processing in a loop.\n    :param benchmark: benchmark special parameter to communicate with executor\n    :param inbox_amount: num of inbox messages for every agent\n    :return: None\n    \"\"\"\nskill_definition = {\n\"handlers\": {\"dummy_handler\": DummyHandler}\n}\naea_test_wrapper = AEATestWrapper(\nname=\"dummy agent\",\nskills=[skill_definition],\n)\nfor _ in range(inbox_amount):\naea_test_wrapper.put_inbox(aea_test_wrapper.dummy_envelope())\naea_test_wrapper.set_loop_timeout(0.0)\nbenchmark.start()\naea_test_wrapper.start_loop()\nwhile not aea_test_wrapper.is_inbox_empty():\ntime.sleep(0.1)\naea_test_wrapper.stop_loop()\n

Create AEA wrapper with specified handler:

skill_definition = {\n\"handlers\": {\"dummy_handler\": DummyHandler}\n}\naea_test_wrapper = AEATestWrapper(\nname=\"dummy agent\",\nskills=[skill_definition],\n)\n

Populate inbox with dummy messages:

for _ in range(inbox_amount):\naea_test_wrapper.put_inbox(aea_test_wrapper.dummy_envelope())\n

Set timeout 0, for maximum messages processing speed: aea_test_wrapper.set_loop_timeout(0.0)

Start benchmark: benchmark.start()

Start/stop AEA:

aea_test_wrapper.start()\n...\naea_test_wrapper.stop()\n

Wait till messages present in inbox:

while not aea_test_wrapper.is_inbox_empty():\ntime.sleep(0.1)\n
"},{"location":"aea-framework-documentation/por/","title":"Proof of Representation","text":"

An AEA can use several key pairs. In particular, it can use different keys for securing its communication and for engaging in exchange. In the ACN we make use of this fact. To be able to signal to other agents that the address derived from one key pair is allowed to represent the agent controlling the other key pair, the key pair which is being represented must sign a message to prove that the other key pair is allowed to represent it. The aea issue-certificates command allows to create this association.

The proof of representation feature is used in the context of the fetchai/p2p_libp2p and fetchai/p2p_libp2p_client connection.

In the former connection, the configuration YAML specifies a cert_requests field:

cert_requests:\n- identifier: acn\nledger_id: fetchai\nnot_after: '2023-01-01'\nnot_before: '2022-01-01'\npublic_key: fetchai\nmessage_format: '{public_key}'\nsave_path: .certs/conn_cert.txt\n

The identifier refers to the environment for which the signature is generated, here acn. The ledger_id refers to the key pair to be used from the private_key_paths specified in aea-config.yaml for signing. The not_after and not_before fields specify constraints on the validity of the signature. The public_key can specify either the identifier of the key pair in connection_private_key_paths, of which the public key is signed, or it can contain the to be signed public key in plain text. The save_path specifies the path where the certificate is to be saved at.

In the above example, the connection requests a certificate which is a signature of the fetchai public key in connection_private_key_paths with the fetchai key pair in private_key_paths. The validity of the signature will be constrained to the year 2021 for the environment acn.

"},{"location":"aea-framework-documentation/prometheus/","title":"Prometheus Monitoring","text":"

AEAs can create and update prometheus metrics for remote monitoring by sending messages to the prometheus connection fetchai/prometheus:0.9.6.

To see this working in an agent, fetch and run the coin_price_feed agent and check localhost:9090/metrics to see the latest values of the metrics num_retrievals and num_requests:

aea fetch fetchai/coin_price_feed:0.15.5\ncd coin_price_feed\naea install\naea build\naea run\n

You can then instruct a prometheus server running on the same computing cluster as a deployed agent to scrape these metrics for remote monitoring and visualisation with the Prometheus/Grafana toolset.

To use this connection, add a model prometheus_dialogues to your skill to handle the metrics configuration and messages to the prometheus connection.

Click here for example:
class PrometheusDialogues(Model, BasePrometheusDialogues):\n\"\"\"The dialogues class keeps track of all prometheus dialogues.\"\"\"\ndef __init__(self, **kwargs) -> None:\n\"\"\"\n        Initialize dialogues.\n        :return: None\n        \"\"\"\nself.enabled = kwargs.pop(\"enabled\", False)\nself.metrics = kwargs.pop(\"metrics\", [])\nModel.__init__(self, **kwargs)\ndef role_from_first_message(  # pylint: disable=unused-argument\nmessage: Message, receiver_address: Address\n) -> BaseDialogue.Role:\n\"\"\"Infer the role of the agent from an incoming/outgoing first message\n            :param message: an incoming/outgoing first message\n            :param receiver_address: the address of the receiving agent\n            :return: The role of the agent\n            \"\"\"\nreturn PrometheusDialogue.Role.AGENT\nBasePrometheusDialogues.__init__(\nself,\nself_address=str(self.skill_id),\nrole_from_first_message=role_from_first_message,\n)\n

Then configure your metrics in the skill.yaml file. For example (from the advanced_data_request skill):

models:\nprometheus_dialogues:\nargs:\nenabled: true\nmetrics:\n- name: num_retrievals\ntype: Gauge\ndescription: Number of price quotes retrieved\nlabels: {}\n- name: num_requests\ntype: Gauge\ndescription: Number of price quote requests served\nlabels: {}\nclass_name: PrometheusDialogues\n

Add a metric metric_name of type metric_type {Gauge, Counter, ...} and description description by sending a message with performative ADD_METRIC to the prometheus connection:

def add_prometheus_metric(\nself,\nmetric_name: str,\nmetric_type: str,\ndescription: str,\nlabels: Dict[str, str],\n) -> None:\n\"\"\"\n    Add a prometheus metric.\n    :param metric_name: the name of the metric to add.\n    :param type: the type of the metric.\n    :param description: a description of the metric.\n    :param labels: the metric labels.\n    :return: None\n    \"\"\"\n# context\nprom_dialogues = cast(PrometheusDialogues, self.context.prometheus_dialogues)\n# prometheus update message\nmessage, _ = prom_dialogues.create(\ncounterparty=str(PROM_CONNECTION_ID),\nperformative=PrometheusMessage.Performative.ADD_METRIC,\ntype=metric_type,\ntitle=metric_name,\ndescription=description,\nlabels=labels,\n)\n# send message\nself.context.outbox.put_message(message=message)\n

where PROM_CONNECTION_ID should be imported to your skill as follows:

from packages.fetchai.connections.prometheus.connection import (\nPUBLIC_ID as PROM_CONNECTION_ID,\n)\n

Update metric metric_name with update function update_func {inc, set, observe, ...} and value value by sending a message with performative UPDATE_METRIC to the prometheus connection:

def update_prometheus_metric(\nself, metric_name: str, update_func: str, value: float, labels: Dict[str, str],\n) -> None:\n\"\"\"\n    Update a prometheus metric.\n    :param metric_name: the name of the metric.\n    :param update_func: the name of the update function (e.g. inc, dec, set, ...).\n    :param value: the value to provide to the update function.\n    :param labels: the metric labels.\n    :return: None\n    \"\"\"\n# context\nprom_dialogues = cast(PrometheusDialogues, self.context.prometheus_dialogues)\n# prometheus update message\nmessage, _ = prom_dialogues.create(\ncounterparty=str(PROM_CONNECTION_ID),\nperformative=PrometheusMessage.Performative.UPDATE_METRIC,\ntitle=metric_name,\ncallable=update_func,\nvalue=value,\nlabels=labels,\n)\n# send message\nself.context.outbox.put_message(message=message)\n

Initialize the metrics from the configuration file in the behaviour setup:

def setup(self) -> None:\n\"\"\"Implement the setup of the behaviour\"\"\"\nprom_dialogues = cast(PrometheusDialogues, self.context.prometheus_dialogues)\nif prom_dialogues.enabled:\nfor metric in prom_dialogues.metrics:\nself.context.logger.info(\"Adding Prometheus metric: \" + metric[\"name\"])\nself.add_prometheus_metric(\nmetric[\"name\"], metric[\"type\"], metric[\"description\"], dict(metric[\"labels\"]),\n

Then call the update_prometheus_metric function from the appropriate places. For example, the following code in handlers.py for the advanced_data_request skill updates the number of http requests served:

if self.context.prometheus_dialogues.enabled:\nself.context.behaviours.advanced_data_request_behaviour.update_prometheus_metric(\n\"num_requests\", \"inc\", 1.0, {}\n)\n

Finally, you can add a PrometheusHandler to your skill to process response messages from the prometheus connection.

Click here for example:
class PrometheusHandler(Handler):\n\"\"\"This class handles responses from the prometheus server.\"\"\"\nSUPPORTED_PROTOCOL = PrometheusMessage.protocol_id\ndef __init__(self, **kwargs):\n\"\"\"Initialize the handler.\"\"\"\nsuper().__init__(**kwargs)\nself.handled_message = None\ndef setup(self) -> None:\n\"\"\"Set up the handler.\"\"\"\nif self.context.prometheus_dialogues.enabled:\nself.context.logger.info(\"setting up PrometheusHandler\")\ndef handle(self, message: Message) -> None:\n\"\"\"\n        Implement the reaction to a message.\n        :param message: the message\n        :return: None\n        \"\"\"\nmessage = cast(PrometheusMessage, message)\n# recover dialogue\nprometheus_dialogues = cast(\nPrometheusDialogues, self.context.prometheus_dialogues\n)\nprometheus_dialogue = cast(\nPrometheusDialogue, prometheus_dialogues.update(message)\n)\nif prometheus_dialogue is None:\nself._handle_unidentified_dialogue(message)\nreturn\nself.handled_message = message\nif message.performative == PrometheusMessage.Performative.RESPONSE:\nself.context.logger.debug(\nf\"Prometheus response ({message.code}): {message.message}\"\n)\nelse:\nself.context.logger.debug(\nf\"got unexpected prometheus message: Performative = {PrometheusMessage.Performative}\"\n)\ndef _handle_unidentified_dialogue(self, msg: Message) -> None:\n\"\"\"\n        Handle an unidentified dialogue.\n        :param msg: the unidentified message to be handled\n        :return: None\n        \"\"\"\nself.context.logger.info(\n\"received invalid message={}, unidentified dialogue.\".format(msg)\n)\ndef teardown(self) -> None:\n\"\"\"\n        Teardown the handler.\n        :return: None\n        \"\"\"\n
"},{"location":"aea-framework-documentation/protocol-generator/","title":"Generating Protocols","text":""},{"location":"aea-framework-documentation/protocol-generator/#how-to-run","title":"How to Run","text":"

First make sure you are inside your AEA's folder (see here on how to create a new agent).

Then run

aea generate protocol <path-to-protocol-specification>\n

where <path-to-protocol-specification> is the path to a protocol specification file.

If there are no errors, this command will generate the protocol and place it in your AEA project. The name of the protocol's directory will match the protocol name given in the specification. The author will match the registered author in the CLI. The generator currently produces the following files (assuming the name of the protocol in the specification is sample):

  1. message.py: defines messages valid under the sample protocol
  2. serialisation.py: defines how messages are serialized/deserialized
  3. __init__.py: makes the directory a package
  4. protocol.yaml: contains package information about the sample protocol
  5. sample.proto protocol buffer schema file
  6. sample_pb2.py: the generated protocol buffer implementation
  7. custom_types.py: stub implementations for custom types (created only if the specification contains custom types)
"},{"location":"aea-framework-documentation/protocol-generator/#full-mode-vs-protobuf-only-mode","title":"Full Mode vs Protobuf Only Mode","text":"

Currently, the generator can operate in full mode for Python, creating a complete protocol package (files 1 to 7 above) from a protocol specification. The generator also has a protobuf only mode which only creates the protocol buffer schema and implementation files (files 5 and 6 above). The languages supported in the protobuf only mode and their respective ids are below:

  • go: go
  • c++: cpp
  • java: java
  • c#: csharp
  • ruby: ruby
  • objective-c: objc
  • javascript: js

To use the generator in protobuf only mode for any of the above languages:

aea generate protocol --l <language> <path-to-protocol-specification>\n

where <language> is a language id.

The protocol buffer compiler requires a plugin to generate Go code. Install it with:

Note

Note the protocol buffer compiler protoc that the generator uses requires a plugin to produce go code. Follow this instruction.

"},{"location":"aea-framework-documentation/protocol-generator/#protocol-specification","title":"Protocol Specification","text":"

A protocol can be described in a YAML file. This is called a protocol specification. The following is an example protocol specification:

---\nname: two_party_negotiation\nauthor: fetchai\nversion: 0.1.0\ndescription: An example of a protocol specification that describes a protocol for bilateral negotiation.\nlicense: Apache-2.0\naea_version: '>=1.0.0, <2.0.0'\nspeech_acts:\ncfp:\nquery: ct:Query\npropose:\nprice: pt:float\nproposal: pt:dict[pt:str, pt:str]\nconditions: pt:optional[pt:union[pt:str, pt:dict[pt:str,pt:str], pt:set[pt:str]]]\nresources: pt:list[pt:bytes]\naccept: {}\ndecline: {}\n...\n---\nct:Query: |\nbytes query_bytes = 1;\n...\n---\ninitiation: [cfp]\nreply:\ncfp: [propose, decline]\npropose: [propose, accept, decline]\naccept: []\ndecline: []\ntermination: [accept, decline]\nroles: {buyer, seller}\nend_states: [agreement_reached, agreement_unreached]\nkeep_terminal_state_dialogues: true\n...\n

Each protocol specification must follow the YAML format, and have a minimum of one and a maximum of three YAML documents (each YAML document is enclosed within --- and ...).

"},{"location":"aea-framework-documentation/protocol-generator/#basic-protocol-detail-and-messages-syntax","title":"Basic Protocol Detail and Messages Syntax","text":"

The first YAML document is mandatory in any protocol specification. It contains some basic information about the protocol and describes the syntax of communicative messages allowed under this protocol.

The allowed fields and what they represent are:

  • name: The name of the protocol (written in snake_case)
  • author: The creator of the protocol
  • version: The current version of the protocol
  • license: Licensing information
  • aea_version: The version(s) of the framework that support this protocol. The format is described here.
  • description: A short description of the protocol
  • protocol_specification_id: The id which identifies the protocol for over-the-wire transport. This id is decoupled from the protocol_id ({author}/{name}:{version}) which is tied to the Python implementation.

All of the above fields are mandatory and each is a key/value pair, where both key and value are YAML strings.

Additionally, the first YAML document of a protocol specification must describe the syntax of valid messages according to this protocol. Therefore, it must contain another mandatory speech-acts field which defines the set of performatives valid under this protocol, and a set of contents for each performative.

A performative defines the type of a message (e.g. propose, accept) and has a set of contents (or parameters) of varying types.

The format of the speech-act is as follows: speech-act is a dictionary, where each key is a unique performative (YAML string), and the value is a content dictionary. If a performative does not have any content, then its content dictionary is empty, for instance accept and decline in the specification above.

A content dictionary in turn has key/value pairs, where each key is the name of a content (YAML string) and the value is its type (YAML string). For example, the cfp (short for 'call for proposal') performative has one content whose name is query and whose type is ct:Query.

"},{"location":"aea-framework-documentation/protocol-generator/#types","title":"Types","text":"

The specific types which could be assigned to contents in a protocol specification are described in the table below.

Types are either user defined (i.e. custom types) or primitive:

  • Custom types are prepended with ct: and their format is described using regular expression in the table below.
  • Primitive types are prepended with pt:. There are different categories of primitive types. For example, <PT> such as integers and booleans, <PCT> such as sets and lists, and so on. Primitive types are compositional:
    • For example, consider pt:set[...] under <PCT>, i.e. an unordered collection of elements without duplicates. A pt:set[...] describes the type of its elements (called \"sub-type\") in square brackets. The subtype of a pt:set[...] must be a <PT> (e.g. pt:int, pt:bool).
    • In describing the format of types, / between two subtypes should be treated as \"or\". For example, the subtype of a pt:optional[...] is either a <PT>, <CT>, <PCT>, <PMT> or <MT>.

A multi type denotes an \"or\" separated set of subtypes. For example, a content whose type is specified as pt:union[pt:str, pt:int] should either be pt:int or pt:float.

An optional type pt:optional[...] assigned to a content means the content's existence is optional, but if it is present, its type must match pt:optional[...]'s subtype.

Type Code Format Example In Python Custom types1 <CT> ct:RegExp(^[A-Z][a-zA-Z0-9]*$) ct:DataModel Custom Class Primitive types <PT> pt:bytes pt:bytes bytes pt:int pt:int int pt:float pt:float float pt:bool pt:bool bool pt:str pt:str str Primitive collection types <PCT> pt:set[<PT>] pt:set[pt:str] FrozenSet[str] pt:list[<PT>] pt:list[pt:int] Tuple[int, ...]* Primitive mapping types2 <PMT> pt:dict[<PT>, <PT>] pt:dict[pt:str, pt:bool] Dict[str, bool] Multi types <MT> pt:union[<PT>/<CT>/<PCT>/<PMT>, ..., <PT>/<CT>/<PCT>/<PMT>] pt:union[ct:DataModel, pt:set[pt:str]] Union[DataModel, FrozenSet[str]] Optional types <O> pt:optional[<MT>/<PMT>/<PCT>/<PT>/<CT>] pt:optional[pt:bool] Optional[bool]

* This is how variable length tuples containing elements of the same type are declared in Python; see here.

"},{"location":"aea-framework-documentation/protocol-generator/#protocol-buffer-schema","title":"Protocol Buffer Schema","text":"

Currently, the AEA framework does not officially support describing custom types in a programming language independent format. This means that if a protocol specification includes custom types, the required serialisation logic must be provided manually.

Therefore, if any of the contents declared in speech-acts is of a custom type, the specification must then have a second YAML document, containing the protocol buffer schema code for each custom type.

You can see an example of the second YAML document in the above protocol specification.

"},{"location":"aea-framework-documentation/protocol-generator/#dialogues","title":"Dialogues","text":"

You can optionally specify the structure of dialogues conforming to your protocol in a third YAML document in the specification.

The allowed fields and what they represent are:

  • initiation: The list of initial performatives
  • reply: The reply structure of speech-acts
  • termination: The list of terminal performatives
  • roles: The roles of players participating in a dialogue
  • end_states: The possible outcomes a terminated dialogue.
  • keep_terminal_state_dialogues: whether to keep or drop a terminated dialogue. When a storage backend is configured, the dialogues will be persisted in storage when kept.

All of the above fields are mandatory.

initiation is a YAML list, containing the performatives which can be used to start a dialogue.

reply specifies for every performative, what its valid replies are. If a performative per_1 is a valid reply to another per_2, this means a message with performative per_1 can target a message whose performative is per_2.

reply is a YAML dictionary, where the keys are the performatives (YAML string) defined in speech-acts. For each performative key, its value is a list of performatives which are defined to be a valid reply. For example, valid replies to cfp are propose and decline.

termination is a YAML list, containing the performatives which terminate a dialogue. Once any of these performatives are used in a dialogue, the dialogue is terminated and no other messages may be added to it.

roles is a YAML set, containing the roles players participating in dialogues can take. roles may contain one or two roles, each role being a YAML string. If there are two roles, each participant has a distinguished role in the dialogue (e.g. buyer and seller in the above specification). If there is only one role, then both participants in a dialogue have this same role.

end_states lists the final states a terminated dialogue may have. end_states is a YAML list of strings.

keep_terminal_state_dialogues has a boolean value and specifies whether the terminated dialogues of this protocol are to be kept or discarded.

"},{"location":"aea-framework-documentation/protocol-generator/#design-guidelines","title":"Design Guidelines","text":"
  1. initiation and termination cannot be empty.

  2. Make sure that when defining reply, you include every speech-act you specified under speech_acts. If any of the speech-acts does not have a reply, indicate that with an empty list [] similar to accept and decline in the specification above.

  3. If a speech-act is listed in termination, it must not have any replies in reply. The reason is simple: a terminal speech-act terminates a dialogue and so its reply can never be used.

  4. If a speech-act replies to no other speech-acts, it should be listed in initiation otherwise it could never be used in a dialogue (neither to a start a dialogue with, nor as a reply to another speech-act).

"},{"location":"aea-framework-documentation/protocol-generator/#notes","title":"Notes","text":"
  1. Currently, there is no way to describe custom types in a programming language independent format. This means that if a protocol specification includes custom types, the required implementations must be provided manually. _ Before generating the protocol, the protocol buffer schema code for every custom type must be provided in the protocol specification.
    • Once the generator is called, it produces a custom_types module containing stub implementations for every custom type in the specification. The user must then modify this module and add implementations for every custom type in the specification. This includes implementations of how an object of a custom type can be encoded and decoded using protocol buffer.
    • Note, currently the way custom types are dealt with in the generator is admittedly inconvenient. The reason is, the generator does not know the structure of custom types and how they may be serialized/deserialized. Although this approach works, it is only a temporary solution until further work on a programming language-independent type description language is finished (similar to how the generator is designed to be a programming language-independent protocol description language).
  2. Currently, the first element in pt:dict cannot be a <CT>, pt:float or pt:bytes. This is because of a constraint in protocol buffer version 3 which is the framework's underlying serialisation mechanism. In a future version, we may address this limitation, in which case we will relax this constraint.
  3. In protocol buffer version 3, which is the version used by the generator, there is no way to check whether an optional field (i.e. contents of type pt:optional[...]) has been set or not (see discussion here). In proto3, all optional fields are assigned a default value (e.g. 0 for integers types, false for boolean types, etc). Therefore, given an optional field whose value is the default value, there is no way to know from the optional field itself, whether it is not set, or in fact is set but its value happens to be the default value. Because of this, in the generated protocol schema file (the .proto file), for every optional content there is a second field that declares whether this field is set or not. We will maintain this temporary solution until a cleaner alternative is found.
  4. Be aware that currently, using the generated protocols in python, there might be some rounding errors when serialising and then deserializing values of pt:float contents.
"},{"location":"aea-framework-documentation/protocol-generator/#demo-instructions","title":"Demo Instructions","text":"

First, create a new AEA project:

aea create my_aea\ncd my_aea\n

Second, run the generator on the sample specification:

aea generate protocol ../examples/protocol_specification_ex/sample.yaml\n

This will generate the protocol and place it in your AEA project.

Third, try generating other protocols by first defining a specification, then running the generator.

"},{"location":"aea-framework-documentation/protocol/","title":"Protocols","text":"

Protocols define the structure of agent-to-agent and component-to-component interactions, which in the AEA world, are in the form of communication. To learn more about interactions and interaction protocols, see here.

Protocols in the AEA world provide definitions for:

  • messages defining the structure and syntax of messages;
  • serialization defining how a message is encoded/decoded for transport; and optionally
  • dialogues defining the structure of dialogues formed from exchanging series of messages.

The framework provides a default protocol. This protocol provides a bare-bones implementation for an AEA protocol which includes a DefaultMessage class and associated DefaultSerializer and DefaultDialogue classes.

Additional protocols - i.e. a new type of interaction - can be added as packages or generated with the protocol generator.

We highly recommend you to not attempt writing your protocol manually as they tend to have involved logic; always use existing packages or the protocol generator!

"},{"location":"aea-framework-documentation/protocol/#components-of-a-protocol","title":"Components of a Protocol","text":"

A protocol package contains the following files:

  • __init__.py
  • message.py, which defines message representation
  • serialization.py, which defines the encoding and decoding logic
  • two protobuf related files

It optionally also contains

  • dialogues.py, which defines the structure of dialogues formed from the exchange of a series of messages
  • custom_types.py, which defines custom types

All protocols are for point to point interactions between two agents or agent-like services.

"},{"location":"aea-framework-documentation/protocol/#metadata","title":"Metadata","text":"

Each Message in an interaction protocol has a set of default fields:

  • dialogue_reference: Tuple[str, str], a reference of the dialogue the message is part of. The first part of the tuple is the reference assigned to by the agent who first initiates the dialogue (i.e. sends the first message). The second part of the tuple is the reference assigned to by the other agent. The default value is (\"\", \"\").
  • message_id: int, the identifier of the message in a dialogue. The default value is 1.
  • target: int, the id of the message this message is replying to. The default value is 0.
  • performative: Enum, the purpose/intention of the message.
  • sender: Address, the address of the sender of this message.
  • to: Address, the address of the receiver of this message.

The default values for message_id and target assume the message is the first message in a dialogue. Therefore, the message_id is set to 1 indicating the first message in the dialogue and target is 0 since the first message is the only message that does not reply to any other.

By default, the values of dialogue_reference, message_id, target are set. However, most interactions involve more than one message being sent as part of the interaction and potentially multiple simultaneous interactions utilising the same protocol. In those cases, the dialogue_reference allows different interactions to be identified as such. The message_id and target are used to keep track of messages and their replies. For instance, on receiving of a message with message_id=1 and target=0, the responding agent could respond with another with message_id=2 and target=1 replying to the first message. In particular, target holds the id of the message being replied to. This can be the preceding message, or an older one.

"},{"location":"aea-framework-documentation/protocol/#contents","title":"Contents","text":"

Each message may optionally have any number of contents of varying types.

"},{"location":"aea-framework-documentation/protocol/#dialogue-rules","title":"Dialogue Rules","text":"

Protocols can optionally have a dialogue module. A dialogue, respectively dialogues object, maintains the state of a single, respectively, all dialogues associated with a protocol.

The framework provides a number of helpful classes which implement most of the logic to maintain dialogues, namely the Dialogue and Dialogues base classes.

"},{"location":"aea-framework-documentation/protocol/#custom-protocol","title":"Custom Protocol","text":"

The developer can generate custom protocols with the protocol generator. This lets the developer specify the speech-acts as well as optionally the dialogue structure (e.g. roles of agents participating in a dialogue, the states a dialogue may end in, and the reply structure of the speech-acts in a dialogue).

We highly recommend you do not attempt to write your own protocol code; always use existing packages or the protocol generator!

"},{"location":"aea-framework-documentation/protocol/#fetchaidefault117-protocol","title":"fetchai/default:1.1.7 Protocol","text":"

The fetchai/default:1.1.7 protocol is meant to be implemented by every AEA. It serves AEA to AEA interaction and includes three message performatives:

from enum import Enum\nclass Performative(Enum):\n\"\"\"Performatives for the default protocol.\"\"\"\nBYTES = \"bytes\"\nEND = \"end\"\nERROR = \"error\"\ndef __str__(self):\n\"\"\"Get the string representation.\"\"\"\nreturn self.value\n
  • The DefaultMessage of performative DefaultMessage.Performative.BYTES is used to send payloads of byte strings to other AEAs. An example is:
from packages.fetchai.protocols.default.message import DefaultMessage\nmsg = DefaultMessage(\nperformative=DefaultMessage.Performative.BYTES,\ncontent=b\"This is a bytes payload\",\n)\n
  • The DefaultMessage of performative DefaultMessage.Performative.ERROR is used to notify other AEAs of errors in an interaction, including errors with other protocols, by including an error_code in the payload:
class ErrorCode(Enum):\n\"\"\"This class represents an instance of ErrorCode.\"\"\"\nUNSUPPORTED_PROTOCOL = 0\nDECODING_ERROR = 1\nINVALID_MESSAGE = 2\nUNSUPPORTED_SKILL = 3\nINVALID_DIALOGUE = 4\n

An example is:

msg = DefaultMessage(\nperformative=DefaultMessage.Performative.ERROR,\nerror_code=DefaultMessage.ErrorCode.UNSUPPORTED_PROTOCOL,\nerror_msg=\"This protocol is not supported by this AEA.\",\nerror_data={\"unsupported_msg\": b\"serialized unsupported protocol message\"},\n)\n
  • The DefaultMessage of performative DefaultMessage.Performative.END is used to terminate a default protocol dialogue. An example is:
from packages.fetchai.protocols.default.message import DefaultMessage\nmsg = DefaultMessage(\nperformative=DefaultMessage.Performative.END,\n)\n

Each AEA's fetchai/error:0.18.6 skill utilises the fetchai/default:1.0.0 protocol for error handling.

"},{"location":"aea-framework-documentation/protocol/#fetchaioef_search117-protocol","title":"fetchai/oef_search:1.1.7 Protocol","text":"

The fetchai/oef_search:1.1.7 protocol is used by AEAs to interact with an SOEF search node to register and unregister their own services and search for services registered by other agents.

The fetchai/oef_search:1.1.7 protocol definition includes an OefSearchMessage with the following message types:

class Performative(Enum):\n\"\"\"Performatives for the oef_search protocol.\"\"\"\nREGISTER_SERVICE = \"register_service\"\nUNREGISTER_SERVICE = \"unregister_service\"\nSEARCH_SERVICES = \"search_services\"\nOEF_ERROR = \"oef_error\"\nSEARCH_RESULT = \"search_result\"\nSUCCESS = \"success\"\ndef __str__(self):\n\"\"\"Get string representation.\"\"\"\nreturn self.value\n

We show some example messages below:

  • To register a service, we require a reference to the dialogue in string form (used to keep different dialogues apart), for instance
my_dialogue_reference = \"a_unique_register_service_dialogue_reference\"\n

and a description of the service we would like to register, for instance

from aea.helpers.search.models import Description\nmy_service_data = {\"country\": \"UK\", \"city\": \"Cambridge\"}\nmy_service_description = Description(\nmy_service_data,\ndata_model=my_data_model,\n)\n

where we use, for instance

from aea.helpers.search.generic import GenericDataModel\ndata_model_name = \"location\"\ndata_model = {\n\"attribute_one\": {\n\"name\": \"country\",\n\"type\": \"str\",\n\"is_required\": True,\n},\n\"attribute_two\": {\n\"name\": \"city\",\n\"type\": \"str\",\n\"is_required\": True,\n},\n}\nmy_data_model = GenericDataModel(data_model_name, data_model)\n

We can then create the message to register this service:

msg = OefSearchMessage(\nperformative=OefSearchMessage.Performative.REGISTER_SERVICE,\ndialogue_reference=(my_dialogue_reference, \"\"),\nservice_description=my_service_description,\n)\n
  • To unregister a service, we require a reference to the dialogue in string form, for instance
my_dialogue_reference = \"a_unique_unregister_service_dialogue_reference\"\n

the description of the service we would like to unregister, say my_service_description from above and construct the message:

msg = OefSearchMessage(\nperformative=OefSearchMessage.Performative.UNREGISTER_SERVICE,\ndialogue_reference=(my_dialogue_reference, \"\"),\nservice_description=my_service_description,\n)\n
  • To search a service, we similarly require a reference to the dialogue in string form, and then the query we would like the search node to evaluate, for instance
from aea.helpers.search.models import Constraint, ConstraintType, Query\nquery_data = {\n\"search_term\": \"country\",\n\"search_value\": \"UK\",\n\"constraint_type\": \"==\",\n}\nquery = Query(\n[\nConstraint(\nquery_data[\"search_term\"],\nConstraintType(\nquery_data[\"constraint_type\"],\nquery_data[\"search_value\"],\n),\n)\n],\nmodel=None,\n)\n

We can then create the message to search these services:

oef_msg = OefSearchMessage(\nperformative=OefSearchMessage.Performative.SEARCH_SERVICES,\ndialogue_reference=(my_dialogue_reference, \"\"),\nquery=query,\n)\n
  • The SOEF search node will respond with a message msg of type OefSearchMessage with performative OefSearchMessage.Performative.SEARCH_RESULT. To access the tuple of agents which match the query, simply use msg.agents. In particular, this will return the agent addresses matching the query. The agent address can then be used to send a message to the agent utilising the P2P agent communication network and any protocol other than fetchai/oef_search:1.0.0.

  • If the SOEF search node encounters any errors with the messages you send, it will return an OefSearchMessage of performative OefSearchMessage.Performative.OEF_ERROR and indicate the error operation encountered:

class OefErrorOperation(Enum):\n\"\"\"This class represents an instance of OefErrorOperation.\"\"\"\nREGISTER_SERVICE = 0\nUNREGISTER_SERVICE = 1\nSEARCH_SERVICES = 2\nSEND_MESSAGE = 3\nOTHER = 10000\n
"},{"location":"aea-framework-documentation/protocol/#fetchaifipa117-protocol","title":"fetchai/fipa:1.1.7 Protocol","text":"

This protocol provides classes and functions necessary for communication between AEAs via a variant of the FIPA Agent Communication Language.

The fetchai/fipa:1.1.7 protocol definition includes a FipaMessage with the following performatives:

class Performative(Enum):\n\"\"\"Performatives for the fipa protocol.\"\"\"\nACCEPT = \"accept\"\nACCEPT_W_INFORM = \"accept_w_inform\"\nCFP = \"cfp\"\nDECLINE = \"decline\"\nEND = \"end\"\nINFORM = \"inform\"\nMATCH_ACCEPT = \"match_accept\"\nMATCH_ACCEPT_W_INFORM = \"match_accept_w_inform\"\nPROPOSE = \"propose\"\ndef __str__(self):\n\"\"\"Get the string representation.\"\"\"\nreturn self.value\n

FipaMessages are constructed with a performative, dialogue_reference, message_id, and target as well as the kwargs specific to each message performative.

def __init__(\nself,\nperformative: Performative,\ndialogue_reference: Tuple[str, str] = (\"\", \"\"),\nmessage_id: int = 1,\ntarget: int = 0,\n**kwargs,\n)\n

The fetchai/fipa:1.1.7 protocol also defines a FipaDialogue class which specifies the valid reply structure and provides other helper methods to maintain dialogues.

For examples of the usage of the fetchai/fipa:1.1.7 protocol check out the generic skills step by step guide.

"},{"location":"aea-framework-documentation/protocol/#fipa-dialogue","title":"Fipa Dialogue","text":"

Below, we give an example of a dialogue between two agents. In practice; both dialogues would be maintained in the respective agent.

We first create concrete implementations of FipaDialogue and FipaDialogues for the buyer and seller:

from aea.common import Address\nfrom aea.helpers.search.models import Constraint, ConstraintType, Description, Query\nfrom aea.mail.base import Envelope\nfrom aea.protocols.base import Message\nfrom aea.protocols.dialogue.base import Dialogue as BaseDialogue\nfrom aea.protocols.dialogue.base import DialogueLabel\nfrom packages.fetchai.protocols.fipa.dialogues import FipaDialogue, FipaDialogues\nfrom packages.fetchai.protocols.fipa.message import FipaMessage\nclass BuyerDialogue(FipaDialogue):\n\"\"\"The dialogue class maintains state of a dialogue and manages it.\"\"\"\ndef __init__(\nself,\ndialogue_label: DialogueLabel,\nself_address: Address,\nrole: BaseDialogue.Role,\nmessage_class: Type[FipaMessage] = FipaMessage,\n) -> None:\n\"\"\"\n        Initialize a dialogue.\n        :param dialogue_label: the identifier of the dialogue\n        :param self_address: the address of the entity for whom this dialogue is maintained\n        :param role: the role of the agent this dialogue is maintained for\n        :return: None\n        \"\"\"\nFipaDialogue.__init__(\nself,\ndialogue_label=dialogue_label,\nself_address=self_address,\nrole=role,\nmessage_class=message_class,\n)\nself.proposal = None  # type: Optional[Description]\nclass BuyerDialogues(FipaDialogues):\n\"\"\"The dialogues class keeps track of all dialogues.\"\"\"\ndef __init__(self, self_address: Address) -> None:\n\"\"\"\n        Initialize dialogues.\n        :return: None\n        \"\"\"\ndef role_from_first_message(\nmessage: Message, receiver_address: Address\n) -> BaseDialogue.Role:\n\"\"\"Infer the role of the agent from an incoming/outgoing first message\n            :param message: an incoming/outgoing first message\n            :param receiver_address: the address of the receiving agent\n            :return: The role of the agent\n            \"\"\"\nreturn BaseFipaDialogue.Role.BUYER\nFipaDialogues.__init__(\nself,\nself_address=self_address,\nrole_from_first_message=role_from_first_message,\ndialogue_class=FipaDialogue,\n)\nclass SellerDialogue(FipaDialogue):\n\"\"\"The dialogue class maintains state of a dialogue and manages it.\"\"\"\ndef __init__(\nself,\ndialogue_label: DialogueLabel,\nself_address: Address,\nrole: BaseDialogue.Role,\nmessage_class: Type[FipaMessage] = FipaMessage,\n) -> None:\n\"\"\"\n        Initialize a dialogue.\n        :param dialogue_label: the identifier of the dialogue\n        :param self_address: the address of the entity for whom this dialogue is maintained\n        :param role: the role of the agent this dialogue is maintained for\n        :return: None\n        \"\"\"\nFipaDialogue.__init__(\nself,\ndialogue_label=dialogue_label,\nself_address=self_address,\nrole=role,\nmessage_class=message_class,\n)\nself.proposal = None  # type: Optional[Description]\nclass SellerDialogues(FipaDialogues):\n\"\"\"The dialogues class keeps track of all dialogues.\"\"\"\ndef __init__(self, self_address: Address) -> None:\n\"\"\"\n        Initialize dialogues.\n        :return: None\n        \"\"\"\ndef role_from_first_message(\nmessage: Message, receiver_address: Address\n) -> BaseDialogue.Role:\n\"\"\"Infer the role of the agent from an incoming/outgoing first message\n            :param message: an incoming/outgoing first message\n            :param receiver_address: the address of the receiving agent\n            :return: The role of the agent\n            \"\"\"\nreturn FipaDialogue.Role.SELLER\nFipaDialogues.__init__(\nself,\nself_address=self_address,\nrole_from_first_message=role_from_first_message,\ndialogue_class=FipaDialogue,\n)\n

Next, we can imitate a dialogue between the buyer and the seller. We first instantiate the dialogues models:

buyer_address = \"buyer_address_stub\"\nseller_address = \"seller_address_stub\"\nbuyer_dialogues = BuyerDialogues(buyer_address)\nseller_dialogues = SellerDialogues(seller_address)\n

First, the buyer creates a message destined for the seller and updates the dialogues:

cfp_msg = FipaMessage(\nmessage_id=1,\ndialogue_reference=buyer_dialogues.new_self_initiated_dialogue_reference(),\ntarget=0,\nperformative=FipaMessage.Performative.CFP,\nquery=Query([Constraint(\"something\", ConstraintType(\">\", 1))]),\n)\ncfp_msg.counterparty = seller_addr\n# Extends the outgoing list of messages.\nbuyer_dialogue = buyer_dialogues.update(cfp_msg)\n

If the message has been correctly constructed, the buyer_dialogue will be returned, otherwise it will be None.

In a skill, the message could now be sent:

# In a skill we would do:\n# self.context.outbox.put_message(message=cfp_msg)\n

However, here we simply continue with the seller:

# change the incoming message field & counterparty\ncfp_msg.is_incoming = True\ncfp_msg.counterparty = buyer_address\n

In the skill, the above two lines will be done by the framework; you can simply receive the message in the handler.

We update the seller's dialogues model next to generate a new dialogue:

# Creates a new dialogue for the seller side based on the income message.\nseller_dialogue = seller_dialogues.update(cfp_msg)\n

Next, the seller can generate a proposal:

# Generate a proposal message to send to the buyer.\nproposal = Description({\"foo1\": 1, \"bar1\": 2})\nmessage_id = cfp_msg.message_id + 1\ntarget = cfp_msg.message_id\nproposal_msg = FipaMessage(\nmessage_id=message_id,\ndialogue_reference=seller_dialogue.dialogue_label.dialogue_reference,\ntarget=target,\nperformative=FipaMessage.Performative.PROPOSE,\nproposal=proposal,\n)\nproposal_msg.counterparty = cfp_msg.counterparty\n# Then we update the dialogue\nseller_dialogue.update(proposal_msg)\n

In a skill, the message could now be sent:

# In a skill we would do:\n# self.context.outbox.put_message(message=proposal_msg)\n

The dialogue can continue like this.

To retrieve a dialogue for a given message, we can do the following:

retrieved_dialogue = seller_dialogues.get_dialogue(cfp_msg)\n
"},{"location":"aea-framework-documentation/query-language/","title":"The Query Language","text":"

We recommend reading Defining a Data Model before reading this section.

Along with the Data Model language, the AEA framework offers the possibility to specify queries defined over data models.

The aea.helpers.search module implements the API that allows you to build queries.

In one sentence, a Query is a set of constraints, defined over a data model. The outcome is a set of description (that is, instances of Description) matching with the query. That is, all the description whose attributes satisfy the constraints in the query.

In the next sections, we describe how to build queries.

"},{"location":"aea-framework-documentation/query-language/#constraints","title":"Constraints","text":"

A Constraint is associated with an attribute name and imposes restrictions on the domain of that attribute. That is, it imposes some limitations on the values the attribute can assume.

We have different types of constraints:

  • relation constraints:

  • the author of the book must be Stephen King

  • the publication year must be greater than 1990

  • set constraints:

  • the genre must fall into the following set of genres: Horror, Science fiction, Non-fiction.

  • range constraints:

  • the average rating must be between 3.5 and 4.5

  • distance constraints:

  • the nearest bookshop must be within a distance from a given location.

The class that implements the constraint concept is Constraint In the following, we show how to define them.

"},{"location":"aea-framework-documentation/query-language/#relation","title":"Relation","text":"

There are several ConstraintTypes that allows you to impose specific values for the attributes.

The types of relation constraints are:

  • Equal: ==
  • Not Equal: !=
  • Less than: <
  • Less than or Equal: <=
  • Greater than: >
  • Greater than or Equal: >=

Examples: using the attributes we used before:

from aea.helpers.search.models import Constraint, ConstraintType\n# all the books whose author is Stephen King\nConstraint(\"author\", ConstraintType(\"==\", \"Stephen King\"))\n# all the books that are not of the genre Horror\nConstraint(\"genre\", ConstraintType(\"!=\", \"Horror\"))\n# all the books published before 1990\nConstraint(\"year\", ConstraintType(\"<\", 1990))\n# the same of before, but including 1990\nConstraint(\"year\", ConstraintType(\"<=\", 1990))\n# all the books with rating greater than 4.0\nConstraint(\"average_rating\", ConstraintType(\">\", 4.0))\n# all the books published after 2000, included\nConstraint(\"year\", ConstraintType(\">=\", 2000))\n
"},{"location":"aea-framework-documentation/query-language/#set","title":"Set","text":"

The set is a constraint type that allows you to restrict the values of the attribute in a specific set.

There are two kind of set constraints:

  • In (a set of values): in
  • Not in (a set of values): not_in

Examples:

from aea.helpers.search.models import Constraint, ConstraintType\n# all the books whose genre is one of `Horror`, `Science fiction`, `Non-fiction`\nConstraint(\"genre\", ConstraintType(\"in\", (\"horror\", \"science fiction\", \"non-fiction\")))\n# all the books that have not been published neither in 1990, nor in 1995, nor in 2000\nConstraint(\"year\", ConstraintType(\"not_in\", (1990, 1995, 2000)))\n
"},{"location":"aea-framework-documentation/query-language/#range","title":"Range","text":"

The range is a constraint type that allows you to restrict the values of the attribute in a given range.

Examples:

from aea.helpers.search.models import Constraint, ConstraintType\n# all the books whose title is between 'A' and 'B' (alphanumeric order)\nConstraint(\"title\", ConstraintType(\"within\", (\"A\", \"B\")))\n# all the books that have been published between 1960 and 1970\nConstraint(\"genre\", ConstraintType(\"within\", (1960, 1970)))\n
"},{"location":"aea-framework-documentation/query-language/#distance","title":"Distance","text":"

The distance is a constraint type that allows you to put a limit on a Location attribute type. More specifically, you can set a maximum distance from a given location (the centre), such that will be considered only the instances whose location attribute value is within a distance from the centre.

Examples:

from aea.helpers.search.models import Constraint, ConstraintType, Description, Location\n# define a location of interest, e.g. the Tour Eiffel\ntour_eiffel = Location(48.8581064, 2.29447)\n# find all the locations close to the Tour Eiffel within 1 km\nclose_to_tour_eiffel = Constraint(\"position\", ConstraintType(\"distance\", (tour_eiffel, 1.0)))\n# Le Jules Verne, a famous restaurant close to the Tour Eiffel, satisfies the constraint.\nle_jules_verne_restaurant = Location(48.8579675, 2.2951849)\nclose_to_tour_eiffel.check(Description({\"position\": le_jules_verne_restaurant}))  # gives `True`\n# The Colosseum does not satisfy the constraint (farther than 1 km from the Tour Eiffel).\ncolosseum = Location(41.8902102, 12.4922309)\nclose_to_tour_eiffel.check(Description({\"position\": colosseum}))  # gives `False`\n
"},{"location":"aea-framework-documentation/query-language/#constraint-expressions","title":"Constraint Expressions","text":"

The constraints mentioned above can be combined with the common logical operators (i.e. and, or and not), yielding more complex expression.

In particular, we can specify any conjunction/disjunction/negations of the previous constraints or composite ConstraintExpressions, e.g.:

  • books that belong to Horror and has been published after 2000, but not published by Stephen King.
  • books whose author is either J. K. Rowling or J. R. R. Tolkien

The classes that implement these operators are Not, And and Or.

"},{"location":"aea-framework-documentation/query-language/#not","title":"Not","text":"

The Not is a constraint expression that allows you to specify a negation of a constraint expression. The Not constraint is satisfied whenever its subexpression is not satisfied.

Example:

from aea.helpers.search.models import Constraint, ConstraintType, Not\n# all the books whose year of publication is not between 1990 and 2000\nNot(Constraint(\"year\", ConstraintType(\"within\", (1990, 2000))))\n
"},{"location":"aea-framework-documentation/query-language/#and","title":"And","text":"

The And is a constraint type that allows you to specify a conjunction of constraints over an attribute. That is, the And constraint is satisfied whenever all the subexpressions that constitute the and are satisfied.

Notice: the number of subexpressions must be at least 2.

Example:

from aea.helpers.search.models import Constraint, ConstraintType, And\n# all the books whose title is between 'I' and 'J' (alphanumeric order) but not equal to 'It'\nAnd([Constraint(\"title\", ConstraintType(\"within\", (\"I\", \"J\"))), Constraint(\"title\", ConstraintType(\"!=\", \"It\"))])\n
"},{"location":"aea-framework-documentation/query-language/#or","title":"Or","text":"

The class Or is a constraint type that allows you to specify a disjunction of constraints. That is, the Or constraint is satisfied whenever at least one of the constraints that constitute the or is satisfied.

Notice: the number of subexpressions must be at least 2.

Example:

from aea.helpers.search.models import Constraint, ConstraintType, Or\n# all the books that have been published either before the year 1960 or after the year 1970\nOr([Constraint(\"year\", ConstraintType(\"<\", 1960)), Constraint(\"year\", ConstraintType(\">\", 1970))])\n
"},{"location":"aea-framework-documentation/query-language/#queries","title":"Queries","text":"

A query is simply a list of constraint expressions, interpreted as a conjunction (that is, a matching description with the query must satisfy every constraint expression.)

Examples:

from aea.helpers.search.models import Query, Constraint, ConstraintType\n# query all the books written by Stephen King published after 1990, and available as an e-book:\nQuery([\nConstraint(\"author\", ConstraintType(\"==\", \"Stephen King\")),\nConstraint(\"year\", ConstraintType(\">=\", 1990)),\nConstraint(\"ebook_available\", ConstraintType(\"==\", True))\n], book_model)\n

Where book_model is the DataModel object. However, the data model is an optional parameter, but to avoid ambiguity is recommended to include it.

"},{"location":"aea-framework-documentation/query-language/#the-check-method","title":"The check Method","text":"

The Query class supports a way to check whether a Description matches with the query. This method is called Query.check.

Examples:

from aea.helpers.search.models import Query, Constraint, ConstraintType\nfrom aea.helpers.search.models import Description\nq = Query([\nConstraint(\"author\", ConstraintType(\"==\", \"Stephen King\")),\nConstraint(\"year\", ConstraintType(\">=\", 1990)),\nConstraint(\"ebook_available\", ConstraintType(\"==\", True))\n])\n# With a query, you can check that a `Description` object satisfies the constraints.\nq.check(Description({\"author\": \"Stephen King\", \"year\": 1991, \"ebook_available\": True}))  # True\nq.check(Description({\"author\": \"George Orwell\", \"year\": 1948, \"ebook_available\": False})) # False\n
"},{"location":"aea-framework-documentation/query-language/#validity","title":"Validity","text":"

A Query object must satisfy some conditions in order to be instantiated.

  • The list of constraints expressions can't be empty; must have at least one constraint expression.
  • If the data model is specified:

    • For every constraint expression that constitute the query, check if they are valid with respect to the data model.

A ConstraintExpr c (that is, one of And, Or, Not, Constraint) is valid with respect to a DataModel if:

  • If c is an instance of And, Or or Not, then every subexpression of c must be valid (with respect to the data model);
  • If c is an instance of Constraint, then:

    • if the constraint type is one of <, <=, >, >=, the value in the constructor must be one of str, int or float.
    • if the constraint type is a within, then the types in the range must be one of int, str, float or Location.
    • if the constraint type is a distance, then the only valid type is Location.
    • if the constraint type is a in, then the types supported are str, int, float, bool, Location. Notice though that a set of bool is trivial, so you may find yourself more comfortable by using other alternatives.
    • for the other constraint types, i.e. == and !=, the value can be one of the allowed types for Attribute, that is str, int, float, bool, Location.
  • Moreover, when c is a Constraint, the attribute must have a consistent type with respect to the data model. E.g. consider a Constraint like:

Constraint(\"foo\", ConstraintType(\"==\", True))\n

Consider a DataModel where there is an Attribute \"foo\" of type str. Then the constraint is not compatible with the mentioned data model, because the constraint expect an equality comparison with a boolean True, instead of a str.

"},{"location":"aea-framework-documentation/questions-and-answers/","title":"Q&A","text":"What is an AEA?

AEA stands for \"Autonomous Economic Agent\". An AEA can represent an individual, organisation or object and looks after its owner's interests. AEAs act independently of constant user input and autonomously execute actions to achieve their prescribed goals. Their purpose is to create economic value for their owners.

How do AEAs talk to each other when they do not know each other?

For an Autonomous Economic Agent (AEA) to talk to other AEAs, it first needs to find them. Once it does, it should ensure that they both use the same protocol for communication, and if so, they then have to send messages to each other.

The AEA framework, together with some of the services it provides, address all three problems. You can read more about search and discovery here, protocols here, and the Agent Communication Network (ACN) here.

How does an AEA use blockchain?

The AEA framework enables agents to interact with blockchains to settle transactions. Currently, the framework has native support for three different networks: Fetch.ai, Ethereum and Cosmos.

You can read more about the framework's integration with the different blockchains here and gain a high level overview here.

How does one install third party libraries?

The framework supports the use of third-party libraries hosted on PyPI. You can directly reference the external dependencies of an AEA package (e.g. skill) in its configuration file. From inside an AEA's project directory, the install command can be used to install all the dependencies of the AEA which are listed in the configuration files belonging to any of its packages.

How does one connect to a database?

You have two options to connect to a database: using the built-in storage solution or using a custom ORM (object-relational mapping) library and backend.

The use of the built-in storage is explained here. For a detailed example of how to use an ORM, follow the ORM guide.

How does one connect a frontend?

There are multiple options. The most obvious is using an HTTP server connection and creating a client that communicates with this connection.

You can find a more detailed discussion here.

Is the AEA framework ideal for agent-based modelling?

The goal of agent-based modelling (ABM) is to study the unknown (often complex) behaviour of systems comprised of agents with known (much simpler) behaviour. ABM is a popular technique for studying biological and social systems. Despite some similarities between ABM and the AEA framework, the two have fundamentally different goals. ABM's goal is not the design of agents or solving specific practical or engineering problems. Although it would be potentially possible, it would likely be inefficient to use the AEA framework for that kind of problems.

You can find more details on the application areas of the AEA framework here.

When a new AEA is created, is the vendor folder populated with some default packages?

All AEA projects by default hold the fetchai/default:1.1.7, fetchai/state_update:1.1.7 and fetchai/signing:1.1.7 protocols. These (as all other packages installed from the registry) are placed in the vendor folder.

You can find more details about the file structure here.

Is there a standardization for private key files?

Currently, the private keys are stored in .txt files. This is temporary and will be improved soon.

How to use the same protocol in different skills?

The details of envelope/message routing by the AEA framework are discussed in this guide.

Why does the AEA framework use its own package registry?

AEA packages could be described as personalized plugins for the AEA runtime. They are not like a library - they have no direct use outside the context of the framework - and therefore are not suitable for distribution via PyPI.

"},{"location":"aea-framework-documentation/quickstart/","title":"AEA Quick Start","text":"

If you want to create Autonomous Economic Agents (AEAs) that can act independently of constant user input and autonomously execute actions to achieve their objective, you can use the AEA framework.

This example will take you through a simple AEA to familiarise you with the basics of the framework.

"},{"location":"aea-framework-documentation/quickstart/#echo-skill-demo","title":"Echo Skill Demo","text":"

This is a simple demo that introduces you to the main components of an AEA.

The fastest way to have your first AEA is to fetch one that already exists!

aea fetch fetchai/my_first_aea:0.28.5\ncd my_first_aea\n

To learn more about the folder structure of an AEA project read on here.

Alternatively: step by step install:

Create a new AEA

First, create a new AEA project and enter it.

aea create my_first_aea\ncd my_first_aea\n

Add the stub connection

Second, add the stub connection to the project.

aea add connection fetchai/stub:0.21.3\n

Add the echo skill

Third, add the echo skill to the project.

aea add skill fetchai/echo:0.20.6\n

This copies the fetchai/echo:0.20.6 skill code containing the \"behaviours\", and \"handlers\" into the project, ready to run. The identifier of the skill fetchai/echo:0.20.6 consists of the name of the author of the skill, followed by the skill name and its version.

"},{"location":"aea-framework-documentation/quickstart/#echo-skill","title":"Echo Skill","text":"

Just like humans, AEAs can have skills to achieve their tasks. As an agent developer, you can create skills to add to your own AEAs. You can also choose to publish your skills so others add them to their AEAs. More details on skills can be found on this page .

The above agent has an echo skill, fetched from the registry, which simply echoes any messages it receives back to its sender.

"},{"location":"aea-framework-documentation/quickstart/#communication-via-envelopes-and-messages","title":"Communication via Envelopes and Messages","text":"

AEAs use envelopes containing messages for communication. To learn more, check out the next section.

"},{"location":"aea-framework-documentation/quickstart/#stub-connection","title":"Stub Connection","text":"

Besides skills, AEAs may have one or more connections enabling them to interface with entities in the outside world. For example, an HTTP client connection allows an AEA to communicate with HTTP servers. To read more about connections see this page.

In this demo, we use the stub connection (fetchai/stub0.15.0) to send envelopes to and receive envelopes from the AEA.

A stub connection provides an I/O reader and writer. It uses two files for communication: one for incoming envelopes and the other for outgoing envelopes.

The AEA waits for a new envelope posted to the file my_first_aea/input_file, and adds a response to the file my_first_aea/output_file.

The format of each envelope is the following:

TO,SENDER,PROTOCOL_ID,ENCODED_MESSAGE,\n

For example:

recipient_aea,sender_aea,fetchai/default:1.0.0,\\x08\\x01\\x12\\x011*\\x07\\n\\x05hello,\n
"},{"location":"aea-framework-documentation/quickstart/#install-aea-dependencies","title":"Install AEA Dependencies","text":"
aea install\n
"},{"location":"aea-framework-documentation/quickstart/#add-and-create-a-private-key","title":"Add and Create a Private Key","text":"

All AEAs need a private key to run. Add one now:

aea generate-key fetchai\naea add-key fetchai\n
"},{"location":"aea-framework-documentation/quickstart/#run-the-aea","title":"Run the AEA","text":"

Run the AEA.

aea run\n

You will see the echo skill running in the terminal window (an output similar to the one below).

    _     _____     _\n   / \\   | ____|   / \\\n/ _ \\  |  _|    / _ \\\n/ ___ \\ | |___  / ___ \\\n/_/   \\_\\|_____|/_/   \\_\\\nv1.1.1\n\nStarting AEA 'my_first_aea' in 'async' mode ...\ninfo: Echo Handler: setup method called.\ninfo: Echo Behaviour: setup method called.\ninfo: [my_first_aea]: Start processing messages...\ninfo: Echo Behaviour: act method called.\ninfo: Echo Behaviour: act method called.\ninfo: Echo Behaviour: act method called.\n...\n

The framework first calls the setup methods in the skill's Handler and Behaviour classes in that order; after which it repeatedly calls the act method of Behaviour class. This is the main agent loop in action.

"},{"location":"aea-framework-documentation/quickstart/#add-a-message-to-the-input-file","title":"Add a Message to the Input File","text":"

You can send the AEA a message wrapped in an envelope using the CLI's interact command.

From a different terminal and same directory (ensure you are in the same virtual environment: pipenv shell):

cd my_first_aea\naea interact\n

You can now send messages to this AEA via an interactive tool by typing anything into the prompt and hitting enter twice (once to send the message and once more to check for a response).

Let us send hello to this AEA (type hello and press enter twice). In the original terminal, you will see the Echo Handler dealing with this envelope and its contained message. You should see an output similar to the one below but with a different dialogue_reference.

info: Echo Behaviour: act method called.\ninfo: Echo Handler: message=Message(dialogue_reference=('1', '') message_id=1 target=0 performative=bytes content=b'hello'), sender=my_first_aea_interact\ninfo: Echo Behaviour: act method called.\ninfo: Echo Behaviour: act method called.\n
Manual approach:

Optionally, from a different terminal and same directory (i.e. the my_first_aea project), you can send the AEA a message wrapped in an envelope via the input file.

echo 'my_first_aea,sender_aea,fetchai/default:1.0.0,\\x12\\x10\\x08\\x01\\x12\\x011*\\t*\\x07\\n\\x05hello,' >> input_file\n

You will see the Echo Handler dealing with the envelope and responding with the same message to the output_file, and also decoding the Base64 encrypted message in this case.

info: Echo Behaviour: act method called.\nEcho Handler: message=Message(sender=sender_aea,to=my_first_aea,content=b'hello',dialogue_reference=('1', ''),message_id=1,performative=bytes,target=0), sender=sender_aea\ninfo: Echo Behaviour: act method called.\ninfo: Echo Behaviour: act method called.\n

Note, due to the dialogue reference having to be incremented, you can only send the above envelope once! This approach does not work in conjunction with the aea interact command.

"},{"location":"aea-framework-documentation/quickstart/#stop-the-aea","title":"Stop the AEA","text":"

You can stop an AEA by pressing CTRL C.

Once you do, you should see the AEA being interrupted and then calling the teardown() methods:

info: Echo Behaviour: act method called.\ninfo: Echo Behaviour: act method called.\n^C my_first_aea interrupted!\nmy_first_aea stopping ...\ninfo: Echo Handler: teardown method called.\ninfo: Echo Behaviour: teardown method called.\n
"},{"location":"aea-framework-documentation/quickstart/#write-a-test-for-the-aea","title":"Write a Test for the AEA","text":"

We can write an end-to-end test for the AEA utilising helper classes provided by the framework.

Writing tests:

The following test class replicates the preceding demo and tests its correct behaviour. The AEATestCase classes are a tool for AEA developers to write useful end-to-end tests of their AEAs.

First, get the packages directory from the AEA repository (execute from the working directory which contains the my_first_aea folder):

svn export https://github.com/fetchai/agents-aea.git/trunk/packages\n

Then write the test:

import signal\nimport time\nfrom aea.common import Address\nfrom aea.mail.base import Envelope\nfrom aea.protocols.base import Message\nfrom aea.protocols.dialogue.base import Dialogue\nfrom packages.fetchai.protocols.default.dialogues import DefaultDialogue, DefaultDialogues\nfrom packages.fetchai.protocols.default.message import DefaultMessage\nfrom packages.fetchai.protocols.default.serialization import DefaultSerializer\nfrom aea.test_tools.test_cases import AEATestCase\nclass TestEchoSkill(AEATestCase):\n\"\"\"Test that echo skill works.\"\"\"\ndef test_echo(self):\n\"\"\"Run the echo skill sequence.\"\"\"\nprocess = self.run_agent()\nis_running = self.is_running(process)\nassert is_running, \"AEA not running within timeout!\"\n# add sending and receiving envelope from input/output files\nsender_aea = \"sender_aea\"\ndef role_from_first_message(\nmessage: Message, receiver_address: Address\n) -> Dialogue.Role:\nreturn DefaultDialogue.Role.AGENT\ndialogues = DefaultDialogues(sender_aea, role_from_first_message)\nmessage_content = b\"hello\"\nmessage = DefaultMessage(\nperformative=DefaultMessage.Performative.BYTES,\ndialogue_reference=dialogues.new_self_initiated_dialogue_reference(),\ncontent=message_content,\n)\nsent_envelope = Envelope(\nto=self.agent_name,\nsender=sender_aea,\nprotocol_id=message.protocol_id,\nmessage=DefaultSerializer().encode(message),\n)\nself.send_envelope_to_agent(sent_envelope, self.agent_name)\ntime.sleep(2.0)\nreceived_envelope = self.read_envelope_from_agent(self.agent_name)\nassert sent_envelope.to == received_envelope.sender\nassert sent_envelope.sender == received_envelope.to\nassert sent_envelope.protocol_id == received_envelope.protocol_id\nreceived_message = DefaultMessage.serializer.decode(received_envelope.message)\nassert message.content == received_message.content\ncheck_strings = (\n\"Echo Handler: setup method called.\",\n\"Echo Behaviour: setup method called.\",\n\"Echo Behaviour: act method called.\",\n\"content={}\".format(message_content),\n)\nmissing_strings = self.missing_from_output(process, check_strings)\nassert (\nmissing_strings == []\n), \"Strings {} didn't appear in agent output.\".format(missing_strings)\nassert (\nself.is_successfully_terminated()\n), \"Echo agent wasn't successfully terminated.\"\n

Place the above code into a file test.py in your AEA project directory (the same level as the aea-config.yaml file).

To run, execute the following:

pytest test.py\n
"},{"location":"aea-framework-documentation/quickstart/#delete-the-aea","title":"Delete the AEA","text":"

Delete the AEA from the parent directory (cd .. to go to the parent directory).

aea delete my_first_aea\n
"},{"location":"aea-framework-documentation/quickstart/#next-steps","title":"Next Steps","text":"

To gain an understanding of the core components of the framework, please continue to the next page:

  • Core components - Part 1

For more demos, use cases or step-by-step guides, please check the following:

  • Generic skill use case
  • Weather skill demo
  • Generic step by step guide
"},{"location":"aea-framework-documentation/raspberry-set-up/","title":"Build an AEA on a Raspberry Pi","text":"

This guide explains how to run an AEA inside a Raspberry Pi.

"},{"location":"aea-framework-documentation/raspberry-set-up/#prerequisites","title":"Prerequisites","text":"
  • Raspberry Pi 4 (You can also use Raspberry Pi3 b or Raspberry Pi3 b+)
  • Internet connection (preferably wireless to minimise the number of wires connecting into your device)
"},{"location":"aea-framework-documentation/raspberry-set-up/#preparing-the-raspberry-pi","title":"Preparing the Raspberry Pi","text":"

The easiest and recommended way to get started is to download and unzip our custom AEA Raspberry Pi Image, which includes the AEA installation as well as the most common dependencies.

However, you can also do the installation manually, and if you have a new Raspberry Pi, you can boot the system using the included SD card and skip the next section.

"},{"location":"aea-framework-documentation/raspberry-set-up/#raspberry-pi-imager","title":"Raspberry Pi Imager","text":"

Raspberry Pi Imager is a way to write to an SD card for easy installation on a Raspberry Pi.

First download the tool from this link.

Then follow this guide to set up your SD card. When you get to the step of choosing an operating system, select the downloaded and unzipped AEA Raspberry Pi Image (AEA_RPI.IMG), or for a manual installation, select the latest Raspberry Pi OS.

Once you have set up your SD card, plug it into your Raspberry Pi, connect the power and boot up.

"},{"location":"aea-framework-documentation/raspberry-set-up/#booting-up-with-the-aea-raspberry-pi-image","title":"Booting up with the AEA Raspberry Pi Image","text":"

After booting up, you may be prompted to log in as the aea user and the password is fetch. Next, navigate to settings menu to set up your internet connection. Your Raspberry Pi is now ready to run an AEA! You can find some preloaded demos in the folder ~/aea/demos. To run these demos, navigate to one of the sub-folders and enter aea run.

"},{"location":"aea-framework-documentation/raspberry-set-up/#booting-up-with-the-raspberry-pi-os-for-manual-installation","title":"Booting up with the Raspberry Pi OS for Manual Installation","text":"

When you first boot your Raspberry Pi, you will be prompted to enter a password for the Raspberry Pi and your Wi-Fi password so the device can access the internet. You may also be given the option to update the operating system and software. We recommend that you let the system update. Once finished you will be prompted to restart.

Even if your Raspberry Pi updated itself, we recommend that you make sure it is completely up-to-date using the terminal. Open a Terminal window (your Raspberry Pi might restart a few times during this process):

sudo apt update -y sudo apt-get update\nsudo apt-get dist-upgrade 
"},{"location":"aea-framework-documentation/raspberry-set-up/#install-common-dependencies","title":"Install Common Dependencies","text":"
sudo apt install cmake golang -y\n
"},{"location":"aea-framework-documentation/raspberry-set-up/#install-less-common-dependencies-optional","title":"Install Less Common Dependencies (optional)","text":"

For some of the more advanced AEAs that make use of SciPy, such as the Car Park Detector, you will need some additional dependencies.

Install additional dependencies with the enclosed steps:

Install additional dependencies

sudo apt install gfortran libatlas-base-dev libopenblas-dev -y\n

Increase the swap space for the SciPy installation:

sudo /bin/dd if=/dev/zero of=/var/swap.1 bs=1M count=1024\nsudo /sbin/mkswap /var/swap.1\nsudo chmod 600 /var/swap.1\nsudo /sbin/swapon /var/swap.1\n

Install NumPy and scikit-image (including SciPy)

pip install numpy --upgrade\npip install scikit-image\n

Revert to default swap space

sudo swapoff /var/swap.1\nsudo rm /var/swap.1\n
"},{"location":"aea-framework-documentation/raspberry-set-up/#install-the-aea-framework","title":"Install the AEA Framework","text":"

Add to the local PATH environment variable (this will happen automatically the next time you log in):

export PATH=\"$HOME/.local/bin:$PATH\"\n

Finally, install the AEA framework from PyPI:

pip install aea[all]\n

Check to make sure installation was successful:

aea --version\n

Your Raspberry Pi is now ready to run an AEA!

"},{"location":"aea-framework-documentation/runtime-cost/","title":"Profiling","text":""},{"location":"aea-framework-documentation/runtime-cost/#measuring-runtime-cost","title":"Measuring Runtime Cost","text":"

It is important to emphasise the fact that the AEA is a framework, so ultimately its running cost will highly depend on the number and type of components which are being run as part of a given AEA. The other cost factor is determined by the cost of running the core framework itself and how fast and efficient the framework is in interconnecting the components.

These observations can provide guidance on what to report as part of the cost of running an AEA.

Here is a list of suggestion on how to measure the cost of running an AEA:

  • the cost of running the framework itself: by running a minimal agent with an idle loop (the default one) with no connections, skills or protocols and measuring memory usage and CPU consumption as a baseline.
  • the cost of interconnecting components: by running an agent with a basic skill (e.g. fetchai/echo) and measuring memory usage and CPU consumption relative to number of messages exchanged as well as bandwidth.
  • the cost of basic components: dialogues memory relative to number of messages, SOEF connection baseline memory usage, P2P connection baseline memory usage, smart contract baseline memory usage

The aea run --profiling SECONDS command can be used to report measures in all of the above scenarios.

"},{"location":"aea-framework-documentation/scaffolding/","title":"Scaffolding Packages","text":""},{"location":"aea-framework-documentation/scaffolding/#scaffold-generator","title":"Scaffold Generator","text":"

The scaffold generator builds out the directory structure required when adding new skills, protocols, contracts and connections to the AEA.

For example, create a new AEA project (add the author flag using your own author handle if this is your first project using the aea package).

aea create my_aea --author \"fetchai\"\ncd my_aea\n

Then, enter into your project directory and scaffold your project skill, protocol, or connection.

"},{"location":"aea-framework-documentation/scaffolding/#scaffold-a-skill","title":"Scaffold a Skill","text":"
aea scaffold skill my_skill\n
"},{"location":"aea-framework-documentation/scaffolding/#scaffold-a-protocol","title":"Scaffold a Protocol","text":"
aea scaffold protocol my_protocol\n
"},{"location":"aea-framework-documentation/scaffolding/#scaffold-a-contract","title":"Scaffold a Contract","text":"
aea scaffold contract my_contract\n
"},{"location":"aea-framework-documentation/scaffolding/#scaffold-a-connection","title":"Scaffold a Connection","text":"
aea scaffold connection my_connection\n

After running the above commands, you are able to develop your own skill, protocol, contract and connection.

Once you have made changes to your scaffolded packages, make sure you update the fingerprint of the package:

aea fingerprint [package_name] [public_id]\n

Then you are ready to run the AEA.

"},{"location":"aea-framework-documentation/security/","title":"Security","text":"

The AEA framework takes every care to follow best practice around security.

The following advice will help you when writing your own code:

  • Many potential common security vulnerabilities can be caught by static code analysis. We recommend you use safety, pylint and bandit to analyse your code.

  • Don't use relative import paths, these can lead to malicious code being executed.

  • Try to avoid using the subprocess module. If needed, make sure you sanitise commands passed to subprocess.

  • Try to avoid using the pickle module. Pickle should never be used for agent-to-agent communication protocols.

  • By design, the framework prevents skill code from accessing private keys directly, as they are not reachable from the skill execution context through attribute getters. However, if the flag -p or the option --password are not used when generating private keys for an AEA project via the aea CLI tool, the private keys will be stored in plaintext. This allows the skills to access them via interaction with the OS file system. We recommend to always specify a password to encrypt private keys by using the flag argument.

"},{"location":"aea-framework-documentation/setup/","title":"Setting up","text":"

Once you successfully install the AEA framework, you can set it up for agent development.

"},{"location":"aea-framework-documentation/setup/#specify-author-handle","title":"Specify Author Handle","text":"

You need an author handle before being able to develop agents or agent components. This handle is used in the author field of any agent or component you create.

AEAs and their components can be developed by anyone and pushed to the AEA registry for others to use. To publish packages to the registry, you also need to register your author handle.

"},{"location":"aea-framework-documentation/setup/#pick-author-handle-and-register","title":"Pick Author Handle and Register","text":"

If you are intending to use the registry:

aea init --register\n

This will let you pick a new author handle and register it at the same time.

"},{"location":"aea-framework-documentation/setup/#pick-author-handle-only","title":"Pick Author Handle Only","text":"

If you are unsure whether you will need a registry account, or intending not to use it, simply pick a new author handle:

aea init\n
"},{"location":"aea-framework-documentation/setup/#register-author-handle","title":"Register Author Handle","text":"

To register an already created author handle with the AEA registry:

aea register\n

Note

The author handle is your unique author (or developer) name in the AEA ecosystem.

"},{"location":"aea-framework-documentation/simple-oef-usage/","title":"SOEF Connection","text":"

You can use the SOEF in the agent framework by using the SOEF connection as a package in your agent project.

"},{"location":"aea-framework-documentation/simple-oef-usage/#add-the-soef-package","title":"Add the SOEF Package","text":"

Check out the CLI guide on details how to add a connection. You will want to add the fetchai/soef:0.27.6 connection package.

"},{"location":"aea-framework-documentation/simple-oef-usage/#register-your-agent-and-its-services","title":"Register your Agent and its Services","text":""},{"location":"aea-framework-documentation/simple-oef-usage/#register-agent-location","title":"Register Agent Location","text":"

To register your agent's location, you have to send a message in the fetchai/oef_search:1.0.0 protocol to the SOEF connection.

First, define a data model for location data:

from aea.helpers.search.models import Attribute, DataModel, Location\nAGENT_LOCATION_MODEL = DataModel(\n\"location_agent\",\n[Attribute(\"location\", Location, True, \"The location where the agent is.\")],\n\"A data model to describe location of an agent.\",\n)\n

It is important to use this exact data model, as the SOEF connection can only process specific data models.

Second, create a location object:

from aea.helpers.search.models import Location\nagent_location = Location(52.2057092, 2.1183431)\n

Third, construct a service description instance with location and data model:

from aea.helpers.search.models import Description\nservice_instance = {\"location\": agent_location}\nservice_description = Description(\nservice_instance, data_model=AGENT_LOCATION_MODEL\n)\n

Finally, construct a message and send it:

from packages.fetchai.protocols.oef_search.message import OefSearchMessage\nmessage = OefSearchMessage(\nperformative=OefSearchMessage.Performative.REGISTER_SERVICE,\nservice_description=service_description,\n)\n

In case everything is registered OK, you will not receive any message back.

If something goes wrong you will receive an error message with performative OefSearchMessage.Performative.OEF_ERROR.

"},{"location":"aea-framework-documentation/simple-oef-usage/#register-personality-pieces","title":"Register Personality Pieces","text":"

To register personality pieces, you have to use a specific data model:

from aea.helpers.search.models import Attribute, DataModel, Location\nAGENT_PERSONALITY_MODEL = DataModel(\n\"personality_agent\",\n[\nAttribute(\"piece\", str, True, \"The personality piece key.\"),\nAttribute(\"value\", str, True, \"The personality piece value.\"),\n],\n\"A data model to describe the personality of an agent.\",\n)\n

An example follows:

service_instance = {\"piece\": \"genus\", \"value\": \"service\"}\nservice_description = Description(\nservice_instance, data_model=AGENT_PERSONALITY_MODEL\n)\n
"},{"location":"aea-framework-documentation/simple-oef-usage/#register-services","title":"Register Services","text":"

To set some service key and value you have to use a specific data model:

SET_SERVICE_KEY_MODEL = DataModel(\n\"set_service_key\",\n[\nAttribute(\"key\", str, True, \"Service key name.\"),\nAttribute(\"value\", str, True, \"Service key value.\"),\n],\n\"A data model to set service key.\",\n)\n

An example follows:

service_instance = {\"key\": \"test\", \"value\": \"test\"}\nservice_description = Description(\nservice_instance, data_model=SET_SERVICE_KEY_MODEL\n)\n
"},{"location":"aea-framework-documentation/simple-oef-usage/#remove-service-key","title":"Remove Service Key","text":"

To remove service key have to use a specific data model:

REMOVE_SERVICE_KEY_MODEL = DataModel(\n\"remove_service_key\",\n[Attribute(\"key\", str, True, \"Service key name.\")],\n\"A data model to remove service key.\",\n)\n

An example follows:

service_instance = {\"key\": \"test\"}\nservice_description = Description(\nservice_instance, data_model=REMOVE_SERVICE_KEY_MODEL\n)\n

Note

Currently, the soef does not allow for multiple registrations to be combined into a single command.

"},{"location":"aea-framework-documentation/simple-oef-usage/#perform-a-search","title":"Perform a Search","text":"

To perform a search for services registered you have to define a search query consisting of constraints. The location constraints is required, personality pieces or services keys constraints are optional.

An example follows:

from aea.helpers.search.models import (\nConstraint,\nConstraintType,\nLocation,\nQuery,\n)\nradius = 0.1\nclose_to_my_service = Constraint(\n\"location\", ConstraintType(\"distance\", (agent_location, radius))\n)\npersonality_filters = [\nConstraint(\"genus\", ConstraintType(\"==\", \"vehicle\")),\nConstraint(\n\"classification\", ConstraintType(\"==\", \"mobility.railway.train\")\n),\n]\nservice_key_filters = [\nConstraint(\"test\", ConstraintType(\"==\", \"test\")),\n]\ncloseness_query = Query(\n[close_to_my_service] + personality_filters + service_key_filters\n)\nmessage = OefSearchMessage(\nperformative=OefSearchMessage.Performative.SEARCH_SERVICES,\nquery=closeness_query,\n)\n

In case of error, you will receive a message with OefSearchMessage.Performative.OEF_ERROR. In case of successful search you will receive a message with performative OefSearchMessage.Performative.SEARCH_RESULT and the list of matched agents addresses.

"},{"location":"aea-framework-documentation/simple-oef-usage/#generic-command","title":"Generic Command","text":"

To send a generic command request to the SOEF use the following (here on the example of setting a declared name):

import urllib\nAGENT_GENERIC_COMMAND_MODEL = DataModel(\n\"generic_command\",\n[\nAttribute(\"command\", str, True, \"Command name to execute.\"),\nAttribute(\"parameters\", str, False, \"Url encoded parameters string.\"),\n],\n\"A data model to describe the generic soef command.\",\n)\ndeclared_name = \"new_declared_name\"\nservice_description = Description(\n{\n\"command\": \"set_declared_name\",\n\"parameters\": urllib.parse.urlencode({\"name\": declared_name}),\n},\ndata_model=AGENT_GENERIC_COMMAND_MODEL,\n)\nmessage = OefSearchMessage(\nperformative=OefSearchMessage.Performative.REGISTER_SERVICE,\nservice_description=service_description,\n)\n
"},{"location":"aea-framework-documentation/simple-oef/","title":"Simple-OEF: Agent Search and Discovery","text":"

The full documentation is available here.

"},{"location":"aea-framework-documentation/skill-guide/","title":"Build your First Skill - Search & Discovery","text":"

This guide will take you through the development of your first skill. It will teach you, how to connect the AEA to the digital world, register the AEA and search for other AEAs.

Although one can imagine scenarios where a single AEA pursues its goals in isolation without interacting with other AEAs, there is no doubt that by working together, AEAs can achieve much more. To do so, an AEA must be seen and found by other AEAs so that they can trade and do other useful things. Fetch.ai\u2019s search-and-discovery mechanism, the simple OEF (or SOEF, for short) lets your agents register, be discovered, and find other agents. You can then negotiate using the AEA framework\u2019s peer-to-peer network (ACN) and trade. This guide covers getting your AEA connected to the SOEF, and describing your AEA to make itself visible.

Registering your AEA with the SOEF involves setting a name, a genus (a high-level description of what the agent represents, e.g. vehicle, building or service), a classification (for example infrastructure.railway.train) and other descriptors to further fine-tune the kind of service your AEA offers (for example, the agent's position, whether it buys or sells, and other descriptive items).

The more you describe your AEA, the easier it is for others to find it using specific filters.

"},{"location":"aea-framework-documentation/skill-guide/#dependencies-required","title":"Dependencies (Required)","text":"

Follow the Preliminaries and Installation sections from the AEA quick start.

"},{"location":"aea-framework-documentation/skill-guide/#step-1-setup","title":"Step 1: Setup","text":"

We will first create an AEA and add a scaffold skill, which we call my_search.

aea create my_aea && cd my_aea\naea scaffold skill my_search\n

In the following steps, we replace the scaffolded Behaviour and Handler in my_aea/skills/my_search with our implementation. We will build a simple skill which lets the AEA send a search query to the SOEF search node and process the resulting response.

"},{"location":"aea-framework-documentation/skill-guide/#step-2-develop-a-behaviour","title":"Step 2: Develop a Behaviour","text":"

A Behaviour class contains the business logic specific to actions initiated by the AEA rather than reactions to other events.

In this example, we implement a simple search behaviour. Each time, act() gets called by the main agent loop, we will send a search request to the SOEF search node via the P2P communication network.

from typing import cast\nfrom aea.helpers.search.models import Constraint, ConstraintType, Location, Query\nfrom aea.skills.behaviours import TickerBehaviour\nfrom packages.fetchai.protocols.oef_search.message import OefSearchMessage\nfrom packages.fetchai.skills.my_search.dialogues import OefSearchDialogues\nDEFAULT_LOCATION = {\"longitude\": 0.1270, \"latitude\": 51.5194}\nDEFAULT_SEARCH_QUERY = {\n\"search_key\": \"seller_service\",\n\"search_value\": \"generic_service\",\n\"constraint_type\": \"==\",\n}\nDEFAULT_SEARCH_RADIUS = 5.0\nclass MySearchBehaviour(TickerBehaviour):\n\"\"\"This class provides a simple search behaviour.\"\"\"\ndef __init__(self, **kwargs):\n\"\"\"Initialize the search behaviour.\"\"\"\nsearch_query = kwargs.pop(\"search_query\", DEFAULT_SEARCH_QUERY)\nlocation = kwargs.pop(\"location\", DEFAULT_LOCATION)\nagent_location = Location(latitude=location[\"latitude\"], longitude=location[\"longitude\"])\nradius = kwargs.pop(\"search_radius\", DEFAULT_SEARCH_RADIUS)\nclose_to_my_service = Constraint(\n\"location\", ConstraintType(\"distance\", (agent_location, radius))\n)\nservice_key_filter = Constraint(\nsearch_query[\"search_key\"],\nConstraintType(\nsearch_query[\"constraint_type\"], search_query[\"search_value\"],\n),\n)\nself.query = Query([close_to_my_service, service_key_filter])\nsuper().__init__(**kwargs)\nself.sent_search_count = 0\ndef setup(self) -> None:\n\"\"\"\n        Implement the setup.\n        :return: None\n        \"\"\"\nself.context.logger.info(\n\"setting up MySearchBehaviour\"\n)\ndef act(self) -> None:\n\"\"\"\n        Implement the act.\n        :return: None\n        \"\"\"\nself.sent_search_count += 1\noef_search_dialogues = cast(\nOefSearchDialogues, self.context.oef_search_dialogues\n)\nself.context.logger.info(\n\"sending search request to OEF search node, search_count={}\".format(\nself.sent_search_count\n)\n)\nsearch_request, _ = oef_search_dialogues.create(\ncounterparty=self.context.search_service_address,\nperformative=OefSearchMessage.Performative.SEARCH_SERVICES,\nquery=self.query,\n)\nself.context.outbox.put_message(message=search_request)\ndef teardown(self) -> None:\n\"\"\"\n        Implement the task teardown.\n        :return: None\n        \"\"\"\nself.context.logger.info(\n\"tearing down MySearchBehaviour\"\n)\n

Searches are proactive and, as such, well placed in a Behaviour. Specifically, we subclass the TickerBehaviour as it allows us to repeatedly search at a defined tick interval.

We place this code in my_aea/skills/my_search/behaviours.py. Ensure you replace the fetchai author in this line from packages.fetchai.skills.my_search.dialogues import OefSearchDialogues with your author handle (run aea init to set or check the author name).

Note

The import paths to agent packages, for example packages.fetchai.skills.my_search.dialogues above, are not actual paths. Package files always reside in your AEA's folder, either under a specific package directory (e.g. connection, protocol, skill) if the package is custom-built, or under vendor if it is pulled from the registry. These paths are virtual and created automatically when an AEA is run. See this page for more details.

"},{"location":"aea-framework-documentation/skill-guide/#step-3-develop-a-handler","title":"Step 3: Develop a Handler","text":"

So far, we have tasked the AEA with sending search requests to the SOEF search node. However, we have no way of handling the responses sent to the AEA by the SOEF search node at the moment. The AEA would simply respond to the SOEF search node via the default error skill which sends all unrecognised envelopes back to the sender.

Let us now implement a Handler to deal with the incoming search responses.

from typing import Optional, cast\nfrom aea.protocols.base import Message\nfrom aea.skills.base import Handler\nfrom packages.fetchai.protocols.oef_search.message import OefSearchMessage\nfrom packages.fetchai.skills.my_search.dialogues import (\nOefSearchDialogue,\nOefSearchDialogues,\n)\nclass MySearchHandler(Handler):\n\"\"\"This class provides a simple search handler.\"\"\"\nSUPPORTED_PROTOCOL = OefSearchMessage.protocol_id\ndef __init__(self, **kwargs):\n\"\"\"Initialize the handler.\"\"\"\nsuper().__init__(**kwargs)\nself.received_search_count = 0\ndef setup(self) -> None:\n\"\"\"Set up the handler.\"\"\"\nself.context.logger.info(\n\"setting up MySearchHandler\"\n)\ndef handle(self, message: Message) -> None:\n\"\"\"\n        Implement the reaction to a message.\n        :param message: the message\n        :return: None\n        \"\"\"\noef_search_msg = cast(OefSearchMessage, message)\n# recover dialogue\noef_search_dialogues = cast(\nOefSearchDialogues, self.context.oef_search_dialogues\n)\noef_search_dialogue = cast(\nOptional[OefSearchDialogue], oef_search_dialogues.update(oef_search_msg)\n)\nif oef_search_dialogue is None:\nself._handle_unidentified_dialogue(oef_search_msg)\nreturn\n# handle message\nif oef_search_msg.performative is OefSearchMessage.Performative.OEF_ERROR:\nself._handle_error(oef_search_msg, oef_search_dialogue)\nelif oef_search_msg.performative is OefSearchMessage.Performative.SEARCH_RESULT:\nself._handle_search(oef_search_msg, oef_search_dialogue)\nelse:\nself._handle_invalid(oef_search_msg, oef_search_dialogue)\ndef _handle_unidentified_dialogue(self, oef_search_msg: OefSearchMessage) -> None:\n\"\"\"\n        Handle an unidentified dialogue.\n        :param msg: the message\n        \"\"\"\nself.context.logger.info(\n\"received invalid oef_search message={}, unidentified dialogue.\".format(\noef_search_msg\n)\n)\ndef _handle_error(\nself, oef_search_msg: OefSearchMessage, oef_search_dialogue: OefSearchDialogue\n) -> None:\n\"\"\"\n        Handle an oef search message.\n        :param oef_search_msg: the oef search message\n        :param oef_search_dialogue: the dialogue\n        :return: None\n        \"\"\"\nself.context.logger.info(\n\"received oef_search error message={} in dialogue={}.\".format(\noef_search_msg, oef_search_dialogue\n)\n)\ndef _handle_search(\nself, oef_search_msg: OefSearchMessage, oef_search_dialogue: OefSearchDialogue\n) -> None:\n\"\"\"\n        Handle the search response.\n        :param agents: the agents returned by the search\n        :return: None\n        \"\"\"\nself.received_search_count += 1\nnb_agents_found = len(oef_search_msg.agents)\nself.context.logger.info(\n\"found number of agents={}, received search count={}\".format(\nnb_agents_found, self.received_search_count\n)\n)\nself.context.logger.info(\n\"number of search requests sent={} vs. number of search responses received={}\".format(\nself.context.behaviours.my_search_behaviour.sent_search_count,\nself.received_search_count,\n)\n)\ndef _handle_invalid(\nself, oef_search_msg: OefSearchMessage, oef_search_dialogue: OefSearchDialogue\n) -> None:\n\"\"\"\n        Handle an oef search message.\n        :param oef_search_msg: the oef search message\n        :param oef_search_dialogue: the dialogue\n        :return: None\n        \"\"\"\nself.context.logger.warning(\n\"cannot handle oef_search message of performative={} in dialogue={}.\".format(\noef_search_msg.performative, oef_search_dialogue,\n)\n)\ndef teardown(self) -> None:\n\"\"\"\n        Teardown the handler.\n        :return: None\n        \"\"\"\nself.context.logger.info(\n\"tearing down MySearchHandler\"\n)\n

We create a handler which is registered for the oef_search protocol. Whenever it receives a search result, we log the number of agents returned by the search - the agents matching the search query - and update the counter of received searches.

We also implement a trivial check on the difference between the amount of search requests sent and responses received.

Note, how the handler simply reacts to incoming events (i.e. messages). It could initiate further actions, however, they are still reactions to the upstream search event.

Also note, how we have access to other objects in the skill via self.context, the SkillContext.

We place this code in my_aea/skills/my_search/handlers.py. Ensure you replace the fetchai author in this line from packages.fetchai.skills.my_search.dialogues import ( with your author handle (run aea init to set or check the author name).

"},{"location":"aea-framework-documentation/skill-guide/#step-4-add-dialogues-model","title":"Step 4: Add Dialogues Model","text":"

We have implemented a behaviour and a handler. We now implement a Model, in particular we implement the Dialogue and Dialogues classes. These ensure that the message flow satisfies the fetchai/oef_search:1.0.0 protocol and keep track of the individual messages being sent and received.

from aea.protocols.base import Message\nfrom aea.protocols.dialogue.base import Dialogue as BaseDialogue\nfrom aea.skills.base import Address, Model\nfrom packages.fetchai.protocols.oef_search.dialogues import (\nOefSearchDialogue as BaseOefSearchDialogue,\n)\nfrom packages.fetchai.protocols.oef_search.dialogues import (\nOefSearchDialogues as BaseOefSearchDialogues,\n)\nOefSearchDialogue = BaseOefSearchDialogue\nclass OefSearchDialogues(Model, BaseOefSearchDialogues):\n\"\"\"This class keeps track of all oef_search dialogues.\"\"\"\ndef __init__(self, **kwargs) -> None:\n\"\"\"\n        Initialize dialogues.\n        :param agent_address: the address of the agent for whom dialogues are maintained\n        :return: None\n        \"\"\"\nModel.__init__(self, **kwargs)\ndef role_from_first_message(  # pylint: disable=unused-argument\nmessage: Message, receiver_address: Address\n) -> BaseDialogue.Role:\n\"\"\"Infer the role of the agent from an incoming/outgoing first message\n            :param message: an incoming/outgoing first message\n            :param receiver_address: the address of the receiving agent\n            :return: The role of the agent\n            \"\"\"\nreturn BaseOefSearchDialogue.Role.AGENT\nBaseOefSearchDialogues.__init__(\nself,\nself_address=str(self.skill_id),\nrole_from_first_message=role_from_first_message,\n)\n

We add this code in the file my_aea/skills/my_search/my_model.py, replacing its original content. We then rename my_aea/skills/my_search/my_model.py to my_aea/skills/my_search/dialogues.py.

"},{"location":"aea-framework-documentation/skill-guide/#step-5-create-the-configuration-file","title":"Step 5: Create the Configuration File","text":"

Based on our skill components above, we create the following configuration file.

name: my_search\nauthor: fetchai\nversion: 0.1.0\ntype: skill\ndescription: A simple search skill utilising the SOEF search node.\nlicense: Apache-2.0\naea_version: '>=1.0.0, <2.0.0'\nfingerprint: {}\nfingerprint_ignore_patterns: []\nconnections: []\ncontracts: []\nprotocols:\n- fetchai/oef_search:1.1.7\nskills: []\nbehaviours:\nmy_search_behaviour:\nargs:\nlocation:\nlatitude: 51.5194\nlongitude: 0.127\nsearch_query:\nconstraint_type: ==\nsearch_key: seller_service\nsearch_value: generic_service\nsearch_radius: 5.0\ntick_interval: 5\nclass_name: MySearchBehaviour\nhandlers:\nmy_search_handler:\nargs: {}\nclass_name: MySearchHandler\nmodels:\noef_search_dialogues:\nargs: {}\nclass_name: OefSearchDialogues\ndependencies:\naea-ledger-fetchai:\nversion: <2.0.0,>=1.0.0\nis_abstract: false\n

Ensure, you replace the author field with your author name! (Run aea init to set or check the author name.)

Importantly, the keys my_search_behaviour and my_search_handler are used in the above handler to access these skill components at runtime via the context. We also set the tick_interval of the TickerBehaviour to 5 seconds.

We place this code in my_aea/skills/my_search/skill.yaml.

Similarly, we replace my_aea/skills/my_search/__init__.py as follows:

# -*- coding: utf-8 -*-\n# ------------------------------------------------------------------------------\n#\n#   Copyright 2018-2019 Fetch.AI Limited\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#       http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n#\n# ------------------------------------------------------------------------------\n\"\"\"This module contains the implementation of the error skill.\"\"\"\nfrom aea.configurations.base import PublicId\nPUBLIC_ID = PublicId.from_str(\"fetchai/my_search:0.1.0\")\n

Again, ensure the author field matches your own.

"},{"location":"aea-framework-documentation/skill-guide/#step-6-update-fingerprint","title":"Step 6: Update Fingerprint","text":"

To run an AEA with new or modified code, you need to update the fingerprint of the new/modified components. In this case, we need to fingerprint our skill:

aea fingerprint skill fetchai/my_search:0.1.0\n

Ensure, you use the correct author name to reference your skill (here we use fetchai as the author.)

"},{"location":"aea-framework-documentation/skill-guide/#step-7-add-the-oef-protocol-and-connection","title":"Step 7: Add the OEF Protocol and Connection","text":"

Our AEA does not have the OEF protocol yet so let's add it.

aea add protocol fetchai/oef_search:1.1.7\n

This adds the protocol to our AEA and makes it available on the path packages.fetchai.protocols....

At this point we need to add the SOEF and P2P connections to allow the AEA to communicate with the SOEF node and other AEAs, install the AEA's dependencies, and configure the AEA:

aea add connection fetchai/soef:0.27.6\naea add connection fetchai/p2p_libp2p:0.27.5\naea install\naea build\naea config set agent.default_connection fetchai/p2p_libp2p:0.27.5\naea config set --type dict agent.default_routing \\\n'{\n  \"fetchai/oef_search:1.1.7\": \"fetchai/soef:0.27.6\"\n}'\n

The last command will ensure that search requests are processed by the correct connection.

"},{"location":"aea-framework-documentation/skill-guide/#step-8-run-a-service-provider-aea","title":"Step 8: Run a Service Provider AEA","text":"

In order for this AEA to find another AEA when searching, the second AEA (let's call it the service provider AEA) must exist and have been registered with the SOEF.

From a different terminal window, we fetch a finished service provider AEA and install its Python dependencies:

aea fetch fetchai/simple_service_registration:0.32.5 && cd simple_service_registration && aea install && aea build\n

This AEA will simply register a location service on the SOEF search node so we can search for it.

We first create the private key for the service provider AEA based on the network you want to transact. To generate and add a private-public key pair for Fetch.ai Dorado use:

aea generate-key fetchai\naea add-key fetchai fetchai_private_key.txt\n

Next, create a private key used to secure the AEA's communications:

aea generate-key fetchai fetchai_connection_private_key.txt\naea add-key fetchai fetchai_connection_private_key.txt --connection\n

Finally, certify the key for use by the connections that request that:

aea issue-certificates\n

Then we run the AEA:

aea run\n

Once you see a message of the form To join its network use multiaddr: ['SOME_ADDRESS'] take note of the address. (Alternatively, use aea get-multiaddress fetchai -c -i fetchai/p2p_libp2p:0.27.5 -u public_uri to retrieve the address.) This is the entry peer address for the local agent communication network created by the simple_service_registration (service provider) AEA.

Click here to see full code and guide for this AEA:

We use a TickerBehaviour to update the service registration at regular intervals. The following code is placed in behaviours.py.

from typing import Any, Optional, cast\nfrom aea.helpers.search.models import Description\nfrom aea.skills.behaviours import TickerBehaviour\nfrom packages.fetchai.protocols.oef_search.message import OefSearchMessage\nfrom packages.fetchai.skills.simple_service_registration.dialogues import (\nOefSearchDialogues,\n)\nfrom packages.fetchai.skills.simple_service_registration.strategy import Strategy\nDEFAULT_MAX_SOEF_REGISTRATION_RETRIES = 5\nDEFAULT_SERVICES_INTERVAL = 30.0\nclass ServiceRegistrationBehaviour(TickerBehaviour):\n\"\"\"This class implements a behaviour.\"\"\"\ndef __init__(self, **kwargs: Any) -> None:\n\"\"\"Initialise the behaviour.\"\"\"\nservices_interval = kwargs.pop(\n\"services_interval\", DEFAULT_SERVICES_INTERVAL\n)  # type: int\nself._max_soef_registration_retries = kwargs.pop(\n\"max_soef_registration_retries\", DEFAULT_MAX_SOEF_REGISTRATION_RETRIES\n)  # type: int\nsuper().__init__(tick_interval=services_interval, **kwargs)\nself.failed_registration_msg = None  # type: Optional[OefSearchMessage]\nself._nb_retries = 0\ndef setup(self) -> None:\n\"\"\"\n        Implement the setup.\n        :return: None\n        \"\"\"\nself._register_agent()\ndef act(self) -> None:\n\"\"\"\n        Implement the act.\n        :return: None\n        \"\"\"\nself._retry_failed_registration()\ndef teardown(self) -> None:\n\"\"\"\n        Implement the task teardown.\n        :return: None\n        \"\"\"\nself._unregister_service()\nself._unregister_agent()\ndef _retry_failed_registration(self) -> None:\n\"\"\"\n        Retry a failed registration.\n        :return: None\n        \"\"\"\nif self.failed_registration_msg is not None:\nself._nb_retries += 1\nif self._nb_retries > self._max_soef_registration_retries:\nself.context.is_active = False\nreturn\noef_search_dialogues = cast(\nOefSearchDialogues, self.context.oef_search_dialogues\n)\noef_search_msg, _ = oef_search_dialogues.create(\ncounterparty=self.failed_registration_msg.to,\nperformative=self.failed_registration_msg.performative,\nservice_description=self.failed_registration_msg.service_description,\n)\nself.context.outbox.put_message(message=oef_search_msg)\nself.context.logger.info(\nf\"Retrying registration on SOEF. Retry {self._nb_retries} out of {self._max_soef_registration_retries}.\"\n)\nself.failed_registration_msg = None\ndef _register(self, description: Description, logger_msg: str) -> None:\n\"\"\"\n        Register something on the SOEF.\n        :param description: the description of what is being registered\n        :param logger_msg: the logger message to print after the registration\n        :return: None\n        \"\"\"\noef_search_dialogues = cast(\nOefSearchDialogues, self.context.oef_search_dialogues\n)\noef_search_msg, _ = oef_search_dialogues.create(\ncounterparty=self.context.search_service_address,\nperformative=OefSearchMessage.Performative.REGISTER_SERVICE,\nservice_description=description,\n)\nself.context.outbox.put_message(message=oef_search_msg)\nself.context.logger.info(logger_msg)\ndef _register_agent(self) -> None:\n\"\"\"\n        Register the agent's location.\n        :return: None\n        \"\"\"\nstrategy = cast(Strategy, self.context.strategy)\ndescription = strategy.get_location_description()\nself._register(description, \"registering agent on SOEF.\")\ndef register_service(self) -> None:\n\"\"\"\n        Register the agent's service.\n        :return: None\n        \"\"\"\nstrategy = cast(Strategy, self.context.strategy)\ndescription = strategy.get_register_service_description()\nself._register(description, \"registering agent's service on the SOEF.\")\ndef register_genus(self) -> None:\n\"\"\"\n        Register the agent's personality genus.\n        :return: None\n        \"\"\"\nstrategy = cast(Strategy, self.context.strategy)\ndescription = strategy.get_register_personality_description()\nself._register(\ndescription, \"registering agent's personality genus on the SOEF.\"\n)\ndef register_classification(self) -> None:\n\"\"\"\n        Register the agent's personality classification.\n        :return: None\n        \"\"\"\nstrategy = cast(Strategy, self.context.strategy)\ndescription = strategy.get_register_classification_description()\nself._register(\ndescription, \"registering agent's personality classification on the SOEF.\"\n)\ndef _unregister_service(self) -> None:\n\"\"\"\n        Unregister service from the SOEF.\n        :return: None\n        \"\"\"\nstrategy = cast(Strategy, self.context.strategy)\ndescription = strategy.get_unregister_service_description()\noef_search_dialogues = cast(\nOefSearchDialogues, self.context.oef_search_dialogues\n)\noef_search_msg, _ = oef_search_dialogues.create(\ncounterparty=self.context.search_service_address,\nperformative=OefSearchMessage.Performative.UNREGISTER_SERVICE,\nservice_description=description,\n)\nself.context.outbox.put_message(message=oef_search_msg)\nself.context.logger.info(\"unregistering service from SOEF.\")\ndef _unregister_agent(self) -> None:\n\"\"\"\n        Unregister agent from the SOEF.\n        :return: None\n        \"\"\"\nstrategy = cast(Strategy, self.context.strategy)\ndescription = strategy.get_location_description()\noef_search_dialogues = cast(\nOefSearchDialogues, self.context.oef_search_dialogues\n)\noef_search_msg, _ = oef_search_dialogues.create(\ncounterparty=self.context.search_service_address,\nperformative=OefSearchMessage.Performative.UNREGISTER_SERVICE,\nservice_description=description,\n)\nself.context.outbox.put_message(message=oef_search_msg)\nself.context.logger.info(\"unregistering agent from SOEF.\")\n

We create a Model type strategy class and place it in strategy.py. We use a generic data model to register the service. As part of the registration we register a location and a key pair describing our service.

from typing import Any\nfrom aea.exceptions import enforce\nfrom aea.helpers.search.generic import (\nAGENT_LOCATION_MODEL,\nAGENT_PERSONALITY_MODEL,\nAGENT_REMOVE_SERVICE_MODEL,\nAGENT_SET_SERVICE_MODEL,\n)\nfrom aea.helpers.search.models import Description, Location\nfrom aea.skills.base import Model\nDEFAULT_LOCATION = {\"longitude\": 0.1270, \"latitude\": 51.5194}\nDEFAULT_SERVICE_DATA = {\"key\": \"seller_service\", \"value\": \"generic_service\"}\nDEFAULT_PERSONALITY_DATA = {\"piece\": \"genus\", \"value\": \"data\"}\nDEFAULT_CLASSIFICATION = {\"piece\": \"classification\", \"value\": \"seller\"}\nclass Strategy(Model):\n\"\"\"This class defines a strategy for the agent.\"\"\"\ndef __init__(self, **kwargs: Any) -> None:\n\"\"\"\n        Initialize the strategy of the agent.\n        :return: None\n        \"\"\"\nlocation = kwargs.pop(\"location\", DEFAULT_LOCATION)\nself._agent_location = {\n\"location\": Location(\nlatitude=location[\"latitude\"], longitude=location[\"longitude\"]\n)\n}\nself._set_personality_data = kwargs.pop(\n\"personality_data\", DEFAULT_PERSONALITY_DATA\n)\nenforce(\nlen(self._set_personality_data) == 2\nand \"piece\" in self._set_personality_data\nand \"value\" in self._set_personality_data,\n\"personality_data must contain keys `key` and `value`\",\n)\nself._set_classification = kwargs.pop(\"classification\", DEFAULT_CLASSIFICATION)\nenforce(\nlen(self._set_classification) == 2\nand \"piece\" in self._set_classification\nand \"value\" in self._set_classification,\n\"classification must contain keys `key` and `value`\",\n)\nself._set_service_data = kwargs.pop(\"service_data\", DEFAULT_SERVICE_DATA)\nenforce(\nlen(self._set_service_data) == 2\nand \"key\" in self._set_service_data\nand \"value\" in self._set_service_data,\n\"service_data must contain keys `key` and `value`\",\n)\nself._remove_service_data = {\"key\": self._set_service_data[\"key\"]}\nsuper().__init__(**kwargs)\ndef get_location_description(self) -> Description:\n\"\"\"\n        Get the location description.\n        :return: a description of the agent's location\n        \"\"\"\ndescription = Description(\nself._agent_location, data_model=AGENT_LOCATION_MODEL,\n)\nreturn description\ndef get_register_service_description(self) -> Description:\n\"\"\"\n        Get the register service description.\n        :return: a description of the offered services\n        \"\"\"\ndescription = Description(\nself._set_service_data, data_model=AGENT_SET_SERVICE_MODEL,\n)\nreturn description\ndef get_register_personality_description(self) -> Description:\n\"\"\"\n        Get the register personality description.\n        :return: a description of the personality\n        \"\"\"\ndescription = Description(\nself._set_personality_data, data_model=AGENT_PERSONALITY_MODEL,\n)\nreturn description\ndef get_register_classification_description(self) -> Description:\n\"\"\"\n        Get the register classification description.\n        :return: a description of the classification\n        \"\"\"\ndescription = Description(\nself._set_classification, data_model=AGENT_PERSONALITY_MODEL,\n)\nreturn description\ndef get_unregister_service_description(self) -> Description:\n\"\"\"\n        Get the unregister service description.\n        :return: a description of the to be removed service\n        \"\"\"\ndescription = Description(\nself._remove_service_data, data_model=AGENT_REMOVE_SERVICE_MODEL,\n)\nreturn description\n

We create a Model type dialogue class and place it in dialogues.py. These classes ensure that the message flow satisfies the fetchai/oef_search:1.0.0 protocol and keep track of the individual messages being sent and received.

from typing import Any\nfrom aea.protocols.base import Address, Message\nfrom aea.protocols.dialogue.base import Dialogue as BaseDialogue\nfrom aea.skills.base import Model\nfrom packages.fetchai.protocols.oef_search.dialogues import (\nOefSearchDialogue as BaseOefSearchDialogue,\n)\nfrom packages.fetchai.protocols.oef_search.dialogues import (\nOefSearchDialogues as BaseOefSearchDialogues,\n)\nOefSearchDialogue = BaseOefSearchDialogue\nclass OefSearchDialogues(Model, BaseOefSearchDialogues):\n\"\"\"This class keeps track of all oef_search dialogues.\"\"\"\ndef __init__(self, **kwargs: Any) -> None:\n\"\"\"\n        Initialize dialogues.\n        :param agent_address: the address of the agent for whom dialogues are maintained\n        :return: None\n        \"\"\"\nModel.__init__(self, **kwargs)\ndef role_from_first_message(  # pylint: disable=unused-argument\nmessage: Message, receiver_address: Address\n) -> BaseDialogue.Role:\n\"\"\"Infer the role of the agent from an incoming/outgoing first message\n            :param message: an incoming/outgoing first message\n            :param receiver_address: the address of the receiving agent\n            :return: The role of the agent\n            \"\"\"\nreturn BaseOefSearchDialogue.Role.AGENT\nBaseOefSearchDialogues.__init__(\nself,\nself_address=str(self.skill_id),\nrole_from_first_message=role_from_first_message,\n)\n

Finally, we have a handler, placed in handlers.py. The handler deals with handling any error messages which might occur during service registration:

from typing import Optional, cast\nfrom aea.configurations.base import PublicId\nfrom aea.protocols.base import Message\nfrom aea.skills.base import Handler\nfrom packages.fetchai.protocols.oef_search.message import OefSearchMessage\nfrom packages.fetchai.skills.simple_service_registration.behaviours import (\nServiceRegistrationBehaviour,\n)\nfrom packages.fetchai.skills.simple_service_registration.dialogues import (\nOefSearchDialogue,\nOefSearchDialogues,\n)\nclass OefSearchHandler(Handler):\n\"\"\"This class implements an OEF search handler.\"\"\"\nSUPPORTED_PROTOCOL = OefSearchMessage.protocol_id  # type: Optional[PublicId]\ndef setup(self) -> None:\n\"\"\"Call to setup the handler.\"\"\"\ndef handle(self, message: Message) -> None:\n\"\"\"\n        Implement the reaction to a message.\n        :param message: the message\n        :return: None\n        \"\"\"\noef_search_msg = cast(OefSearchMessage, message)\n# recover dialogue\noef_search_dialogues = cast(\nOefSearchDialogues, self.context.oef_search_dialogues\n)\noef_search_dialogue = cast(\nOptional[OefSearchDialogue], oef_search_dialogues.update(oef_search_msg)\n)\nif oef_search_dialogue is None:\nself._handle_unidentified_dialogue(oef_search_msg)\nreturn\n# handle message\nif oef_search_msg.performative == OefSearchMessage.Performative.SUCCESS:\nself._handle_success(oef_search_msg, oef_search_dialogue)\nelif oef_search_msg.performative == OefSearchMessage.Performative.OEF_ERROR:\nself._handle_error(oef_search_msg, oef_search_dialogue)\nelse:\nself._handle_invalid(oef_search_msg, oef_search_dialogue)\ndef teardown(self) -> None:\n\"\"\"\n        Implement the handler teardown.\n        :return: None\n        \"\"\"\ndef _handle_unidentified_dialogue(self, oef_search_msg: OefSearchMessage) -> None:\n\"\"\"\n        Handle an unidentified dialogue.\n        :param msg: the message\n        \"\"\"\nself.context.logger.info(\n\"received invalid oef_search message={}, unidentified dialogue.\".format(\noef_search_msg\n)\n)\ndef _handle_success(\nself,\noef_search_success_msg: OefSearchMessage,\noef_search_dialogue: OefSearchDialogue,\n) -> None:\n\"\"\"\n        Handle an oef search message.\n        :param oef_search_success_msg: the oef search message\n        :param oef_search_dialogue: the dialogue\n        :return: None\n        \"\"\"\nself.context.logger.info(\n\"received oef_search success message={} in dialogue={}.\".format(\noef_search_success_msg, oef_search_dialogue\n)\n)\ntarget_message = cast(\nOefSearchMessage,\noef_search_dialogue.get_message_by_id(oef_search_success_msg.target),\n)\nif (\ntarget_message.performative\n== OefSearchMessage.Performative.REGISTER_SERVICE\n):\ndescription = target_message.service_description\ndata_model_name = description.data_model.name\nregistration_behaviour = cast(\nServiceRegistrationBehaviour, self.context.behaviours.service,\n)\nif \"location_agent\" in data_model_name:\nregistration_behaviour.register_service()\nelif \"set_service_key\" in data_model_name:\nregistration_behaviour.register_genus()\nelif (\n\"personality_agent\" in data_model_name\nand description.values[\"piece\"] == \"genus\"\n):\nregistration_behaviour.register_classification()\nelif (\n\"personality_agent\" in data_model_name\nand description.values[\"piece\"] == \"classification\"\n):\nself.context.logger.info(\n\"the agent, with its genus and classification, and its service are successfully registered on the SOEF.\"\n)\nelse:\nself.context.logger.warning(\nf\"received soef SUCCESS message as a reply to the following unexpected message: {target_message}\"\n)\ndef _handle_error(\nself,\noef_search_error_msg: OefSearchMessage,\noef_search_dialogue: OefSearchDialogue,\n) -> None:\n\"\"\"\n        Handle an oef search message.\n        :param oef_search_error_msg: the oef search message\n        :param oef_search_dialogue: the dialogue\n        :return: None\n        \"\"\"\nself.context.logger.info(\n\"received oef_search error message={} in dialogue={}.\".format(\noef_search_error_msg, oef_search_dialogue\n)\n)\ntarget_message = cast(\nOefSearchMessage,\noef_search_dialogue.get_message_by_id(oef_search_error_msg.target),\n)\nif (\ntarget_message.performative\n== OefSearchMessage.Performative.REGISTER_SERVICE\n):\nregistration_behaviour = cast(\nServiceRegistrationBehaviour, self.context.behaviours.service,\n)\nregistration_behaviour.failed_registration_msg = target_message\ndef _handle_invalid(\nself, oef_search_msg: OefSearchMessage, oef_search_dialogue: OefSearchDialogue\n) -> None:\n\"\"\"\n        Handle an oef search message.\n        :param oef_search_msg: the oef search message\n        :param oef_search_dialogue: the dialogue\n        :return: None\n        \"\"\"\nself.context.logger.warning(\n\"cannot handle oef_search message of performative={} in dialogue={}.\".format(\noef_search_msg.performative, oef_search_dialogue,\n)\n)\n

The associated skill.yaml is:

name: simple_service_registration\nauthor: fetchai\nversion: 0.20.0\ntype: skill\ndescription: The simple service registration skills is a skill to register a service.\nlicense: Apache-2.0\naea_version: '>=1.0.0, <2.0.0'\nfingerprint:\nREADME.md: QmUgCcR7sDBQeeCBRKwDT7tPBTi3t4zSibyEqR3xdQUKmh\n__init__.py: QmZd48HmYDr7FMxNaVeGfWRvVtieEdEV78hd7h7roTceP2\nbehaviours.py: QmQHf6QL5aBtLJ34D2tdcbjJLbzom9gaA3HWgRn3rWyigM\ndialogues.py: QmTT9dvFhWt6qvxjwBfMFDTrgEtgWbvgANYafyRg2BXwcR\nhandlers.py: QmZqPt8toGbJgTT6NZBLxjkusrQCZ8GmUEwcmqZ1sd7DpG\nstrategy.py: QmVXfQpk4cjDw576H2ELE12tEiN5brPkwvffvcTeMbsugA\nfingerprint_ignore_patterns: []\nconnections: []\ncontracts: []\nprotocols:\n- fetchai/oef_search:1.1.7\nskills: []\nbehaviours:\nservice:\nargs:\nmax_soef_registration_retries: 5\nservices_interval: 30\nclass_name: ServiceRegistrationBehaviour\nhandlers:\noef_search:\nargs: {}\nclass_name: OefSearchHandler\nmodels:\noef_search_dialogues:\nargs: {}\nclass_name: OefSearchDialogues\nstrategy:\nargs:\nclassification:\npiece: classification\nvalue: seller\nlocation:\nlatitude: 51.5194\nlongitude: 0.127\npersonality_data:\npiece: genus\nvalue: data\nservice_data:\nkey: seller_service\nvalue: generic_service\nclass_name: Strategy\ndependencies: {}\nis_abstract: false\n
"},{"location":"aea-framework-documentation/skill-guide/#step-9-run-the-search-aea","title":"Step 9: Run the Search AEA","text":"

First, create the private key for the search AEA based on the network you want to transact. To generate and add a private-public key pair for Fetch.ai Dorado use:

aea generate-key fetchai\naea add-key fetchai fetchai_private_key.txt\n

Next, create a private key used to secure the AEA's communications:

aea generate-key fetchai fetchai_connection_private_key.txt\naea add-key fetchai fetchai_connection_private_key.txt --connection\n

Finally, certify the key for use by the connections that request that:

aea issue-certificates\n

Then, in the search AEA, run this command (replace SOME_ADDRESS with the correct value as described above):

aea config set --type dict vendor.fetchai.connections.p2p_libp2p.config \\\n'{\n  \"delegate_uri\": \"127.0.0.1:11001\",\n  \"entry_peers\": [\"/dns4/127.0.0.1/tcp/9000/p2p/16Uiu2HAm1uJpFsqSgHStJdtTBPpDme1fo8uFEvvY182D2y89jQuj\"],\n  \"local_uri\": \"127.0.0.1:9001\",\n  \"log_file\": \"libp2p_node.log\",\n  \"public_uri\": \"127.0.0.1:9001\"\n}'\n

This allows the search AEA to connect to the same local agent communication network as the service registration AEA.

We can then launch our AEA.

aea run\n

We can see that the AEA sends search requests to the SOEF search node and receives search responses from the SOEF search node. The search response returns one or more agents (the service provider and potentially other agents which match the query).

We stop the AEA with CTRL + C.

"},{"location":"aea-framework-documentation/skill-guide/#next-steps","title":"Next Steps","text":""},{"location":"aea-framework-documentation/skill-guide/#recommended","title":"Recommended","text":"

We recommend you continue with the next step in the 'Getting Started' series:

  • Core components (Part 2)
"},{"location":"aea-framework-documentation/skill-guide/#relevant-deep-dives","title":"Relevant Deep-Dives","text":"

This guide goes through a more elaborate scenario than the one on this page, where after finding each other, the two AEAs negotiate and trade via a ledger.

"},{"location":"aea-framework-documentation/skill-testing/","title":"Testing Skills","text":"

In this guide, we describe some of the tools the framework offers for testing skills.

"},{"location":"aea-framework-documentation/skill-testing/#the-baseskilltestcase-class","title":"The BaseSkillTestCase Class","text":"

The framework offers a BaseSkillTestCase class which you can subclass and write your test cases with.

Let us assume you want to test the my_behaviour behaviour of a CustomSkill skill you have developed.

You can create a TestMyBehaviour class which inherits BaseSkillTestCase as below:

import asyncio\nfrom asyncio import Queue\nfrom pathlib import Path\nfrom types import SimpleNamespace\nfrom typing import cast\nfrom aea.configurations.constants import DEFAULT_LEDGER\nfrom aea.context.base import AgentContext\nfrom aea.crypto.ledger_apis import DEFAULT_CURRENCY_DENOMINATIONS\nfrom aea.identity.base import Identity\nfrom aea.multiplexer import AsyncMultiplexer, OutBox, Multiplexer\nfrom aea.skills.tasks import TaskManager\nfrom aea.test_tools.test_skill import BaseSkillTestCase\nclass TestMyBehaviour(BaseSkillTestCase):\n\"\"\"Test my_behaviours of the custom skill.\"\"\"\npath_to_skill = Path(\"path_to_this_skill\")\n
"},{"location":"aea-framework-documentation/skill-testing/#specifying-skill-path","title":"Specifying Skill Path","text":"

You must then specify the path to your skill directory via path_to_skill to allow the skill to be loaded and tested. This must be the directory in which skill.yaml of your skill resides.

"},{"location":"aea-framework-documentation/skill-testing/#setting-up-each-test","title":"Setting up Each Test","text":"

You can add a setup() class method to set the environment up for each of your tests. This code will be executed before every test method. If you do include this method, you must call the setup() method of the BaseSkillTestCase class via super().setup().

@classmethod\ndef setup(cls):\n\"\"\"Setup the test class.\"\"\"\nsuper().setup()\ncls.my_behaviour = cast(\nMyBehaviour, cls._skill.skill_context.behaviours.my_behaviour\n)\n

In the above, we make the my_behaviour behaviour object accessible for every test.

"},{"location":"aea-framework-documentation/skill-testing/#skill-and-skill-context","title":"Skill and Skill Context","text":"

The skill object itself is exposed via a property. So you can access the skill object by self.skill and by extension all of its attributes. This crucially includes the complete skill_context. This means that for example, every component of the skill (e.g. behaviours, handlers, models) can be accessed via the skill context.

In the above code snippet, my_behavior is accessed and exposed as a class attribute. Note accessing the skill context is slightly different in the above because it is a class method. If this was a test method, you could access the behaviour via self.skill.skill_context.behaviours.my_behaviour.

"},{"location":"aea-framework-documentation/skill-testing/#dummy-agent-context","title":"Dummy Agent Context","text":"

The loaded skill is also fed a dummy agent_context complete with an identity, outbox, decision_maker_queue and so on, to allow the skill to be properly loaded and have access to everything it requires to function. The agent_context object fed to the skill is shown below:

_multiplexer = AsyncMultiplexer()\n_multiplexer._out_queue = (asyncio.Queue())\nagent_context = AgentContext(\nidentity=Identity(\"test_agent_name\", \"test_agent_address\", \"test_agent_public_key\"),\nconnection_status=_multiplexer.connection_status,\noutbox=OutBox(cast(Multiplexer, cls._multiplexer)),\ndecision_maker_message_queue=Queue(),\ndecision_maker_handler_context=SimpleNamespace(),\ntask_manager=TaskManager(),\ndefault_ledger_id=DEFAULT_LEDGER,\ncurrency_denominations={},\ndefault_connection=None,\ndefault_routing={},\nsearch_service_address=\"dummy_search_service_address\",\ndecision_maker_address=\"dummy_decision_maker_address\",\ndata_dir=\".\"\n)\n
"},{"location":"aea-framework-documentation/skill-testing/#some-useful-skill-attributes","title":"Some Useful Skill Attributes","text":"

Some of the useful objects you can access in your test class for the loaded skill are below:

  • self.skill.skill_context.agent_address: this is the agent identity the skill uses and is set to \"test_agent_address\".
  • self.skill.skill_context.search_service_address: this is the address of the search service and is set to \"dummy_search_service_address\".
  • self.skill.skill_context.skill_id: this is the id of the skill.
  • self.skill.skill_context.decision_maker_address: this is the address of the decision maker and is set to \"dummy_decision_maker_address\".
"},{"location":"aea-framework-documentation/skill-testing/#some-useful-baseskilltestcase-methods","title":"Some Useful BaseSkillTestCase Methods","text":"

There are a number of methods that BaseSkillTestCase offers to make testing skills easier. Some of these are mentioned below. For the rest, consult the API for BaseSkillTestCase:

  • self.get_quantity_in_outbox(): gives you the number of messages which are in the outbox. After running a part of the skill which is expected to send messages, you can use this method to assert the correct number of messages are indeed sent.
  • self.get_message_from_outbox(): gives you the last message in the outbox. Together with the above, you can use this method to grab the last message sent by the skill code you tested and check this is indeed the expected message.
  • self.message_has_attributes(actual_message: Message, message_type: Type[Message], **kwargs,): you can use this method in tandem with the above method to check that a message has the attributes you expect it to have. You have to supply it with the actual message (e.g. using self.get_message_from_outbox()), specify its expected type (e.g. FipaMessage), and any other attribute you expect the message to have (e.g. message_id is 1) may be provided via keyword arguments.
  • self.build_incoming_message: this is an especially useful method to test handlers. Since handlers handle incoming messages, you can create an incoming message using this method to feed it to the handler and test its execution.
"},{"location":"aea-framework-documentation/skill-testing/#checking-logger-output","title":"Checking Logger Output","text":"

You can check the output of your skill's logger by mocking it using unittest.mock before executing a part of your skill as such:

import logging\nfrom unittest import mock\nwith mock.patch.object(self.my_behaviour.context.logger, \"log\") as mock_logger:\nself.my_behaviour.act()\nmock_logger.assert_any_call(logging.INFO, \"some_logger_message\")\n

In the above, we mock the logger before running my_behaviour's act() method and check that the string \"some_logger_message\" is indeed passed to the logger.

"},{"location":"aea-framework-documentation/skill-testing/#next-steps","title":"Next Steps","text":"

You can consult the fetchai/generic_buyer and fetchai/generic_seller skills and their associated tests here to study how BaseSkillTestCase can help you in testing your skills.

You can also refer to the API to study the different methods BaseSkillTestCase makes available to make testing your skills easier.

"},{"location":"aea-framework-documentation/skill/","title":"Skills","text":"

Skills are the core focus of the framework's extensibility as they implement business logic to deliver economic value for the AEA. They are self-contained capabilities that AEAs can dynamically take on board, in order to expand their effectiveness in different situations.

A skill encapsulates implementations of the three abstract base classes Handler, Behaviour, Model, and is closely related with the abstract base class Task:

  • Handler: each skill has zero, one or more Handler objects, each responsible for the registered messaging protocol. Handlers implement AEAs' reactive behaviour. If the AEA understands the protocol referenced in a received Envelope, the Handler reacts appropriately to the corresponding message. Each Handler is responsible for only one protocol. A Handler is also capable of dealing with internal messages (see next section).
  • Behaviour: zero, one or more Behaviours encapsulate actions which further the AEAs goal and are initiated by internals of the AEA, rather than external events. Behaviours implement AEAs' pro-activeness. The framework provides a number of abstract base classes implementing different types of behaviours (e.g. cyclic/one-shot/finite-state-machine/etc.).
  • Model: zero, one or more Models that inherit from the Model class. Models encapsulate custom objects which are made accessible to any part of a skill via the SkillContext.
  • Task: zero, one or more Tasks encapsulate background work internal to the AEA. Task differs from the other three in that it is not a part of skills, but Tasks are declared in or from skills if a packaging approach for AEA creation is used.

A skill can read (parts of) the state of the AEA (as summarised in the AgentContext), and propose actions to the AEA according to its specific logic. As such, more than one skill could exist per protocol, competing with each other in suggesting to the AEA the best course of actions to take. In technical terms this means skills are horizontally arranged.

For instance, an AEA who is trading goods, could subscribe to more than one skill, where each skill corresponds to a different trading strategy. The skills could then read the preference and ownership state of the AEA, and independently suggest profitable transactions.

The framework places no limits on the complexity of skills. They can implement simple (e.g. if-this-then-that) or complex (e.g. a deep learning model or reinforcement learning agent).

The framework provides one default skill, called error. Additional skills can be added as packages.

"},{"location":"aea-framework-documentation/skill/#independence-of-skills","title":"Independence of Skills","text":"

Skills are horizontally layered, that is they run independently of each other. They also cannot access each other's state.

Two skills can communicate with each other in two ways. The skill context provides access via self.context.shared_state to a key-value store which allows skills to share state. A skill can also define as a callback another skill in a message to the decision maker.

"},{"location":"aea-framework-documentation/skill/#context","title":"Context","text":"

The skill has a SkillContext object which is shared by all Handler, Behaviour, and Model objects. The skill context also has a link to the AgentContext. The AgentContext provides read access to AEA specific information like the public key and address of the AEA, its preferences and ownership state. It also provides access to the OutBox.

This means it is possible to, at any point, grab the context and have access to the code in other parts of the skill and the AEA.

For example, in the ErrorHandler(Handler) class, the code often grabs a reference to its context and by doing so can access initialised and running framework objects such as an OutBox for putting messages into.

self.context.outbox.put_message(message=reply)\n

Moreover, you can read/write to the agent context namespace by accessing the attribute SkillContext.namespace.

Importantly, however, a skill does not have access to the context of another skill or protected AEA components like the DecisionMaker.

"},{"location":"aea-framework-documentation/skill/#what-to-code","title":"What to Code","text":"

Each of the skill classes has three methods that must be implemented. All of them include a setup() and teardown() method which the developer must implement.

Then there is a specific method that the framework requires for each class.

"},{"location":"aea-framework-documentation/skill/#handlerspy","title":"handlers.py","text":"

There can be none, one or more Handler class per skill.

Handler classes can receive Message objects of one protocol type only. However, Handler classes can send Envelope objects of any type of protocol they require.

  • handle(self, message: Message): is where the skill receives a Message of the specified protocol and decides what to do with it.

A handler can be registered in one way:

  • By declaring it in the skill configuration file skill.yaml (see below).

It is possible to register new handlers dynamically by enqueuing new Handler instances in the queue context.new_handlers, e.g. in a skill component we can write:

self.context.new_handlers.put(MyHandler(name=\"my_handler\", skill_context=self.context))\n
"},{"location":"aea-framework-documentation/skill/#behaviourspy","title":"behaviours.py","text":"

Conceptually, a Behaviour class contains the business logic specific to initial actions initiated by the AEA rather than reactions to other events.

There can be one or more Behaviour classes per skill. The developer must create a subclass from the abstract class Behaviour to create a new Behaviour.

  • act(self): is how the framework calls the Behaviour code.

A behaviour can be registered in two ways:

  • By declaring it in the skill configuration file skill.yaml (see below)
  • In any part of the code of the skill, by enqueuing new Behaviour instances in the queue context.new_behaviours. In that case, setupis not called by the framework, as the behaviour will be added after the AEA setup is complete.

The framework supports different types of behaviours:

  • OneShotBehaviour: this behaviour is executed only once.
  • TickerBehaviour: the act() method is called every tick_interval. E.g. if the TickerBehaviour subclass is instantiated

There is another category of behaviours, called CompositeBehaviour:

  • SequenceBehaviour: a sequence of Behaviour classes, executed one after the other.
  • FSMBehaviour: a state machine of State behaviours. A state is in charge of scheduling the next state.

If your behaviour fits one of the above, we suggest subclassing your behaviour class with that behaviour class. Otherwise, you can always subclass the general-purpose Behaviour class.

Follows an example of a custom behaviour:

from aea.skills.behaviours import OneShotBehaviour\nclass HelloWorldBehaviour(OneShotBehaviour):\ndef setup(self):\n\"\"\"This method is called once, when the behaviour gets loaded.\"\"\"\ndef act(self):\n\"\"\"This methods is called in every iteration of the agent main loop.\"\"\"\nprint(\"Hello, World!\")\ndef teardown(self):\n\"\"\"This method is called once, when the behaviour is teared down.\"\"\"\n

If we want to register this behaviour dynamically, in any part of the skill code (i.e. wherever the skill context is available), we can write:

self.context.new_behaviours.put(HelloWorldBehaviour(name=\"hello_world\", skill_context=self.context))\n

Or, equivalently to the previous two code blocks:

def hello():\nprint(\"Hello, World!\")\nself.context.new_behaviours.put(OneShotBehaviour(act=hello, name=\"hello_world\", skill_context=self.context))\n

The callable passed to the act parameter is equivalent to the implementation of the act method described above.

The framework is then in charge of registering the behaviour and scheduling it for execution.

"},{"location":"aea-framework-documentation/skill/#taskspy","title":"tasks.py","text":"

Conceptually, a Task is where the developer codes any internal tasks the AEA requires.

There can be one or more Task classes per skill. The developer subclasses abstract class Task to create a new Task.

  • execute(self): is how the framework calls a Task.

The Task class implements the functor pattern. An instance of the Task class can be invoked as if it were an ordinary function. Once completed, it will store the result in the property result. Raises error if the task has not been executed yet, or an error occurred during computation.

We suggest using the task_manager, accessible through the skill context, to manage long-running tasks. The task manager uses multiprocessing to schedule tasks, so be aware that the changes on the task object will not be updated.

Here's an example:

In tasks.py:

from aea.skills.tasks import Task\ndef nth_prime_number(n: int) -> int:\n\"\"\"A naive algorithm to find the n_th prime number.\"\"\"\nassert n > 0\nprimes = [2]\nnum = 3\nwhile len(primes) < n:\nfor p in primes:\nif num % p == 0:\nbreak\nelse:\nprimes.append(num)\nnum += 2\nreturn primes[-1]\nclass LongTask(Task):\ndef setup(self):\n\"\"\"Set the task up before execution.\"\"\"\ndef execute(self, n: int):\nreturn nth_prime_number(n)\ndef teardown(self):\n\"\"\"Clean the task up after execution.\"\"\"\n

In behaviours.py:

from aea.skills.behaviours import TickerBehaviour\nfrom packages.my_author_name.skills.my_skill.tasks import LongTask\nclass MyBehaviour(TickerBehaviour):\ndef setup(self):\n\"\"\"Setup behaviour.\"\"\"\nmy_task = LongTask()\ntask_id = self.context.task_manager.enqueue_task(my_task, args=(10000, ))\nself.async_result = self.context.task_manager.get_task_result(task_id)  # type: multiprocessing.pool.AsyncResult\ndef act(self):\n\"\"\"Act implementation.\"\"\"\nif self.async_result.ready() is False:\nprint(\"The task is not finished yet.\")\nelse:\ncompleted_task = self.async_result.get()  # type: LongTask\nprint(\"The result is:\", completed_task.result)\n# Stop the skill\nself.context.is_active = False\ndef teardown(self):\n\"\"\"Teardown behaviour.\"\"\"\n
"},{"location":"aea-framework-documentation/skill/#models","title":"Models","text":"

The developer might want to add other classes on the context level which are shared equally across the Handler, Behaviour and Task classes. To this end, the developer can subclass an abstract Model. These models are made available on the context level upon initialization of the AEA.

Say, the developer has a class called SomeModel

class SomeModel(Model):\n...\n

Then, an instance of this class is available on the context level like so:

some_model = self.context.some_model\n
"},{"location":"aea-framework-documentation/skill/#skill-configuration","title":"Skill Configuration","text":"

Each skill has a skill.yaml configuration file which lists all Behaviour, Handler, and Task objects pertaining to the skill.

It also details the protocol types used in the skill and points to shared modules, i.e. modules of type Model, which allow custom classes within the skill to be accessible in the skill context.

name: echo\nauthors: fetchai\nversion: 0.1.0\nlicense: Apache-2.0\nbehaviours:\necho:\nclass_name: EchoBehaviour\nargs:\ntick_interval: 1.0\nhandlers:\necho:\nclass_name: EchoHandler\nargs:\nfoo: bar\nmodels: {}\ndependencies: {}\nprotocols:\n- fetchai/default:1.1.7\n
"},{"location":"aea-framework-documentation/skill/#error-skill","title":"Error Skill","text":"

All AEAs have a default error skill that contains error handling code for a number of scenarios:

  • Received envelopes with unsupported protocols
  • Received envelopes with unsupported skills (i.e. protocols for which no handler is registered)
  • Envelopes with decoding errors
  • Invalid messages with respect to the registered protocol

The error skill relies on the fetchai/default:1.0.0 protocol which provides error codes for the above.

"},{"location":"aea-framework-documentation/skill/#custom-error-handler","title":"Custom Error Handler","text":"

The framework implements a default ErrorHandler. You can implement your own and mount it. The easiest way to do this is to run the following command to scaffold a custom ErrorHandler:

aea scaffold error-handler\n

Now you will see a file called error_handler.py in the AEA project root. You can then implement your own custom logic to process messages.

"},{"location":"aea-framework-documentation/standalone-transaction/","title":"Create Stand-Alone Transaction","text":"

In this guide, we will generate some wealth for the Fetch.ai testnet and create a standalone transaction. After the completion of the transaction, we get the transaction digest. With this we can search for the transaction on the block explorer

This guide requires the aea-ledger-fetchai plug-in installed in your Python environment:

pip install aea-ledger-fetchai\n

First, import the python and application specific libraries and set the static variables.

import logging\nfrom aea_ledger_fetchai import FetchAICrypto\nfrom aea.crypto.helpers import create_private_key, try_generate_testnet_wealth\nfrom aea.crypto.ledger_apis import LedgerApis\nfrom aea.crypto.wallet import Wallet\nlogger = logging.getLogger(\"aea\")\nlogging.basicConfig(level=logging.INFO)\nFETCHAI_PRIVATE_KEY_FILE_1 = \"fetchai_private_key_1.txt\"\nFETCHAI_PRIVATE_KEY_FILE_2 = \"fetchai_private_key_2.txt\"\n
"},{"location":"aea-framework-documentation/standalone-transaction/#create-the-private-keys","title":"Create the Private Keys","text":"
    # Create a private keys\ncreate_private_key(\nFetchAICrypto.identifier, private_key_file=FETCHAI_PRIVATE_KEY_FILE_1\n)\ncreate_private_key(\nFetchAICrypto.identifier, private_key_file=FETCHAI_PRIVATE_KEY_FILE_2\n)\n
"},{"location":"aea-framework-documentation/standalone-transaction/#create-the-wallets","title":"Create the Wallets","text":"

Once we created the private keys we need to generate the wallets.

    # Set up the wallets\nwallet_1 = Wallet({FetchAICrypto.identifier: FETCHAI_PRIVATE_KEY_FILE_1})\nwallet_2 = Wallet({FetchAICrypto.identifier: FETCHAI_PRIVATE_KEY_FILE_2})\n
"},{"location":"aea-framework-documentation/standalone-transaction/#generate-wealth","title":"Generate Wealth","text":"

Since we want to send funds from wallet_1 to wallet_2, we need to generate some wealth for the wallet_1. We can do this with the following code

    # Generate some wealth\ntry_generate_testnet_wealth(\nFetchAICrypto.identifier, wallet_1.addresses[FetchAICrypto.identifier]\n)\n
"},{"location":"aea-framework-documentation/standalone-transaction/#send-transaction","title":"Send Transaction","text":"

Finally, we create a transaction that sends the funds to the wallet_2

    # Create the transaction and send it to the ledger.\ntx_nonce = LedgerApis.generate_tx_nonce(\nFetchAICrypto.identifier,\nwallet_2.addresses.get(FetchAICrypto.identifier),\nwallet_1.addresses.get(FetchAICrypto.identifier),\n)\ntransaction = LedgerApis.get_transfer_transaction(\nidentifier=FetchAICrypto.identifier,\nsender_address=wallet_1.addresses.get(FetchAICrypto.identifier),\ndestination_address=wallet_2.addresses.get(FetchAICrypto.identifier),\namount=1,\ntx_fee=1,\ntx_nonce=tx_nonce,\n)\nsigned_transaction = wallet_1.sign_transaction(\nFetchAICrypto.identifier, transaction\n)\ntransaction_digest = LedgerApis.send_signed_transaction(\nFetchAICrypto.identifier, signed_transaction\n)\nlogger.info(\"Transaction complete.\")\nlogger.info(\"The transaction digest is {}\".format(transaction_digest))\n
Stand-alone transaction full code:
import logging\nfrom aea_ledger_fetchai import FetchAICrypto\nfrom aea.crypto.helpers import create_private_key, try_generate_testnet_wealth\nfrom aea.crypto.ledger_apis import LedgerApis\nfrom aea.crypto.wallet import Wallet\nlogger = logging.getLogger(\"aea\")\nlogging.basicConfig(level=logging.INFO)\nFETCHAI_PRIVATE_KEY_FILE_1 = \"fetchai_private_key_1.txt\"\nFETCHAI_PRIVATE_KEY_FILE_2 = \"fetchai_private_key_2.txt\"\ndef run():\n\"\"\"Run demo.\"\"\"\n# Create a private keys\ncreate_private_key(\nFetchAICrypto.identifier, private_key_file=FETCHAI_PRIVATE_KEY_FILE_1\n)\ncreate_private_key(\nFetchAICrypto.identifier, private_key_file=FETCHAI_PRIVATE_KEY_FILE_2\n)\n# Set up the wallets\nwallet_1 = Wallet({FetchAICrypto.identifier: FETCHAI_PRIVATE_KEY_FILE_1})\nwallet_2 = Wallet({FetchAICrypto.identifier: FETCHAI_PRIVATE_KEY_FILE_2})\n# Generate some wealth\ntry_generate_testnet_wealth(\nFetchAICrypto.identifier, wallet_1.addresses[FetchAICrypto.identifier]\n)\nlogger.info(\n\"Sending amount to {}\".format(wallet_2.addresses.get(FetchAICrypto.identifier))\n)\n# Create the transaction and send it to the ledger.\ntx_nonce = LedgerApis.generate_tx_nonce(\nFetchAICrypto.identifier,\nwallet_2.addresses.get(FetchAICrypto.identifier),\nwallet_1.addresses.get(FetchAICrypto.identifier),\n)\ntransaction = LedgerApis.get_transfer_transaction(\nidentifier=FetchAICrypto.identifier,\nsender_address=wallet_1.addresses.get(FetchAICrypto.identifier),\ndestination_address=wallet_2.addresses.get(FetchAICrypto.identifier),\namount=1,\ntx_fee=1,\ntx_nonce=tx_nonce,\n)\nsigned_transaction = wallet_1.sign_transaction(\nFetchAICrypto.identifier, transaction\n)\ntransaction_digest = LedgerApis.send_signed_transaction(\nFetchAICrypto.identifier, signed_transaction\n)\nlogger.info(\"Transaction complete.\")\nlogger.info(\"The transaction digest is {}\".format(transaction_digest))\nif __name__ == \"__main__\":\nrun()\n
"},{"location":"aea-framework-documentation/step-one/","title":"Ways to Build an AEA","text":"

There are a number of ways to build an AEA:

  • To start with, we recommended you build an AEA project step-by-step with the CLI tool as demonstrated in the quick start guide and described here.
  • Using the CLI aea fetch command, pull in an already built project and run as is or extend it to your needs.
  • The last option is to build an AEA programmatically as described here.

Sometimes, an AEA is more than is required for the task at hand. In particular, an AEA is much more than just an agent. In those cases, we suggest you have a look at the following two guides:

  • the AEA vs Agents guide shows the difference between an agent and an AEA in code,
  • the Use multiplexer standalone guide shows how to use the multiplexer on its own to receive and send envelopes.
"},{"location":"aea-framework-documentation/tac-skills-contract/","title":"TAC Skills Ledger-Based","text":"

The AEA TAC - trading agent competition - skills demonstrate an interaction between multiple AEAs in a game.

There are two types of AEAs:

  • The tac_controller which coordinates the game.
  • The tac_participant AEAs which compete in the game. The tac_participant AEAs trade tokens with each other to maximize their utility.
"},{"location":"aea-framework-documentation/tac-skills-contract/#discussion","title":"Discussion","text":"

This demo shows how agents negotiate autonomously with each other while they pursue their goals by participating in the Trading Agents Competition (TAC). The demo can be run against Fetchai or Ethereum ledger. Transactions are validated on an ERC1155 smart contract on the Fetchai Dorado or a local Ganache Ethereum testnet.

In the following video we discuss the framework and TAC in more detail:

"},{"location":"aea-framework-documentation/tac-skills-contract/#communication","title":"Communication","text":"

There are two types of interactions:

  • between the controller and participants (game management communication)
  • between the participants (negotiations)
"},{"location":"aea-framework-documentation/tac-skills-contract/#registration-communication","title":"Registration Communication","text":"

This diagram shows the communication between the various entities during the registration phase.

    sequenceDiagram\n        participant Agent_2\n        participant Agent_1\n        participant Search\n        participant Controller\n\n        activate Search\n        activate Controller\n\n        Controller->>Search: register_service\n        activate Agent_1\n        Agent_1->>Search: search\n        Search-->>Agent_1: controller\n        Agent_1->>Controller: register\n        activate Agent_2\n        Agent_2->>Search: search\n        Search-->>Agent_2: controller\n        Agent_2->>Controller: register\n        Controller->>Agent_1: game_data\n        Controller->>Agent_2: game_data\n\n        deactivate Agent_1\n        deactivate Agent_2\n        deactivate Search\n        deactivate Controller
"},{"location":"aea-framework-documentation/tac-skills-contract/#transaction-communication","title":"Transaction Communication","text":"

This diagram shows the communication between two AEAs and a controller. In this case, we have a Seller_Agent which is set up as a seller (and registers itself as such with the controller during the registration phase). We also have the Searching_Agent which is set up to search for sellers.

    sequenceDiagram\n        participant Buyer_Agent\n        participant Seller_Agent\n        participant Search\n        participant Controller\n\n        activate Buyer_Agent\n        activate Seller_Agent\n        activate Search\n        activate Controller\n\n        Seller_Agent->>Search: register_service\n        Buyer_Agent->>Search: search\n        Search-->>Buyer_Agent: list_of_agents\n        Buyer_Agent->>Seller_Agent: call_for_proposal\n        Seller_Agent->>Buyer_Agent: proposal\n        Buyer_Agent->>Seller_Agent: accept\n        Seller_Agent->>Buyer_Agent: match_accept\n        Seller_Agent->>Controller: transaction\n        Controller->>Controller: transaction_execution\n        Controller->>Seller_Agent: confirm_transaction\n        Controller->>Buyer_Agent: confirm_transaction\n\n        deactivate Buyer_Agent\n        deactivate Seller_Agent\n        deactivate Search\n        deactivate Controller

In the above case, the proposal received contains a set of goods to sell and an associated price. The buyer AEA needs to determine if this is a good deal for them, and if so, it accepts.

There is an equivalent diagram for seller AEAs set up to search for buyers and their interaction with AEAs which are registered as buyers. In that scenario, the proposal will instead be a list of goods that the buyer wishes to buy and the price it is willing to pay for them.

"},{"location":"aea-framework-documentation/tac-skills-contract/#preparation-instructions","title":"Preparation Instructions","text":""},{"location":"aea-framework-documentation/tac-skills-contract/#dependencies","title":"Dependencies","text":"

Follow the Preliminaries and Installation sections from the AEA quick start.

"},{"location":"aea-framework-documentation/tac-skills-contract/#demo-instructions-fetchai","title":"Demo Instructions (Fetchai)","text":"

Follow this instruction to run TAC against the fetch.ai Dorado testnet.

"},{"location":"aea-framework-documentation/tac-skills-contract/#fetch-tac-controller-aea","title":"Fetch TAC Controller AEA","text":"

In the root directory, fetch the controller AEA:

aea fetch fetchai/tac_controller_contract:0.32.5\ncd tac_controller_contract\naea install\naea build\n
Alternatively, create from scratch:

The following steps create the controller from scratch:

aea create tac_controller_contract\ncd tac_controller_contract\naea add connection fetchai/p2p_libp2p:0.27.5\naea add connection fetchai/soef:0.27.6\naea add connection fetchai/ledger:0.21.5\naea add skill fetchai/tac_control_contract:0.27.6\naea config set --type dict agent.dependencies \\\n'{\n  \"aea-ledger-fetchai\": {\"version\": \"<2.0.0,>=1.0.0\"},\n  \"aea-ledger-ethereum\": {\"version\": \"<2.0.0,>=1.0.0\"}\n}'\naea config set agent.default_connection fetchai/p2p_libp2p:0.27.5\naea config set agent.default_ledger fetchai\naea config set vendor.fetchai.connections.soef.config.chain_identifier fetchai_v2_misc\naea config set --type bool vendor.fetchai.skills.tac_control.is_abstract true\naea config set --type dict agent.default_routing \\\n'{\n  \"fetchai/contract_api:1.1.7\": \"fetchai/ledger:0.21.5\",\n  \"fetchai/ledger_api:1.1.7\": \"fetchai/ledger:0.21.5\",\n  \"fetchai/oef_search:1.1.7\": \"fetchai/soef:0.27.6\"\n}'\naea config set --type list vendor.fetchai.connections.p2p_libp2p.cert_requests \\\n'[{\"identifier\": \"acn\", \"ledger_id\": \"fetchai\", \"not_after\": \"2023-01-01\", \"not_before\": \"2022-01-01\", \"public_key\": \"fetchai\", \"save_path\": \".certs/conn_cert.txt\"}]'\naea install\naea build\n
"},{"location":"aea-framework-documentation/tac-skills-contract/#fetch-the-tac-participant-aeas","title":"Fetch the TAC Participant AEAs","text":"

In separate terminals, in the root directory, fetch at least two participants:

aea fetch fetchai/tac_participant_contract:0.22.5 --alias tac_participant_one\ncd tac_participant_one\naea install\naea build\ncd ..\naea fetch fetchai/tac_participant_contract:0.22.5 --alias tac_participant_two\ncd tac_participant_two\naea install\naea build\n
Alternatively, create from scratch:

In a separate terminal, in the root directory, create at least two tac participant AEAs:

aea create tac_participant_one\naea create tac_participant_two\n

Build participant one:

cd tac_participant_one\naea add connection fetchai/p2p_libp2p:0.27.5\naea add connection fetchai/soef:0.27.6\naea add connection fetchai/ledger:0.21.5\naea add skill fetchai/tac_participation:0.25.6\naea add skill fetchai/tac_negotiation:0.29.6\naea config set --type dict agent.dependencies \\\n'{\n  \"aea-ledger-fetchai\": {\"version\": \"<2.0.0,>=1.0.0\"},\n  \"aea-ledger-ethereum\": {\"version\": \"<2.0.0,>=1.0.0\"}\n}'\naea config set agent.default_connection fetchai/p2p_libp2p:0.27.5\naea config set agent.default_ledger fetchai\naea config set vendor.fetchai.connections.soef.config.chain_identifier fetchai_v2_misc\naea config set vendor.fetchai.skills.tac_participation.models.game.args.is_using_contract 'True' --type bool\naea config set vendor.fetchai.skills.tac_negotiation.models.strategy.args.is_contract_tx 'True' --type bool\naea config set --type dict agent.default_routing \\\n'{\n  \"fetchai/contract_api:1.1.7\": \"fetchai/ledger:0.21.5\",\n  \"fetchai/ledger_api:1.1.7\": \"fetchai/ledger:0.21.5\",\n  \"fetchai/oef_search:1.1.7\": \"fetchai/soef:0.27.6\"\n}'\naea config set --type dict agent.decision_maker_handler \\\n'{\n  \"dotted_path\": \"aea.decision_maker.gop:DecisionMakerHandler\",\n  \"file_path\": null\n}'\naea config set --type list vendor.fetchai.connections.p2p_libp2p.cert_requests \\\n'''[{\"identifier\": \"acn\", \"ledger_id\": \"fetchai\", \"message_format\": \"'{public_key}'\", \"not_after\": \"2023-01-01\", \"not_before\": \"2022-01-01\", \"public_key\": \"fetchai\", \"save_path\": \".certs/conn_cert.txt\"}]'''\naea install\naea build\n

Then, build participant two:

cd tac_participant_two\naea add connection fetchai/p2p_libp2p:0.27.5\naea add connection fetchai/soef:0.27.6\naea add connection fetchai/ledger:0.21.5\naea add skill fetchai/tac_participation:0.25.6\naea add skill fetchai/tac_negotiation:0.29.6\naea config set --type dict agent.dependencies \\\n'{\n  \"aea-ledger-fetchai\": {\"version\": \"<2.0.0,>=1.0.0\"},\n  \"aea-ledger-ethereum\": {\"version\": \"<2.0.0,>=1.0.0\"}\n}'\naea config set agent.default_connection fetchai/p2p_libp2p:0.27.5\naea config set agent.default_ledger fetchai\naea config set vendor.fetchai.connections.soef.config.chain_identifier fetchai_v2_misc\naea config set vendor.fetchai.skills.tac_participation.models.game.args.is_using_contract 'True' --type bool\naea config set vendor.fetchai.skills.tac_negotiation.models.strategy.args.is_contract_tx 'True' --type bool\naea config set --type dict agent.default_routing \\\n'{\n  \"fetchai/contract_api:1.1.7\": \"fetchai/ledger:0.21.5\",\n  \"fetchai/ledger_api:1.1.7\": \"fetchai/ledger:0.21.5\",\n  \"fetchai/oef_search:1.1.7\": \"fetchai/soef:0.27.6\"\n}'\naea config set --type dict agent.decision_maker_handler \\\n'{\n  \"dotted_path\": \"aea.decision_maker.gop:DecisionMakerHandler\",\n  \"file_path\": null\n}'\naea config set --type list vendor.fetchai.connections.p2p_libp2p.cert_requests \\\n'''[{\"identifier\": \"acn\", \"ledger_id\": \"fetchai\", \"message_format\": \"'{public_key}'\", \"not_after\": \"2023-01-01\", \"not_before\": \"2022-01-01\", \"public_key\": \"fetchai\", \"save_path\": \".certs/conn_cert.txt\"}]'''\naea install\naea build\n
"},{"location":"aea-framework-documentation/tac-skills-contract/#add-keys-for-all-aeas","title":"Add Keys for All AEAs","text":"

For every AEA in the competition (controller and participants):

First generate and add a private key:

aea generate-key fetchai\naea add-key fetchai fetchai_private_key.txt\n

Then create and add a separate private key for secure communication:

aea generate-key fetchai fetchai_connection_private_key.txt\naea add-key fetchai fetchai_connection_private_key.txt --connection\n

Finally, certify the key for use by the connections that request that:

aea issue-certificates\n
"},{"location":"aea-framework-documentation/tac-skills-contract/#update-the-game-parameters-in-the-controller","title":"Update the Game Parameters in the Controller","text":"

In the tac controller project, get and set the registration start time (set it to at least 5 minutes in the future):

aea config get vendor.fetchai.skills.tac_control_contract.models.parameters.args.registration_start_time\naea config set vendor.fetchai.skills.tac_control_contract.models.parameters.args.registration_start_time '01 01 2020  00:01'\n

To set the registration time, you may find handy the following command:

aea config set vendor.fetchai.skills.tac_control_contract.models.parameters.args.registration_start_time \"$(date -d \"5 minutes\" +'%d %m %Y %H:%M')\"\n
"},{"location":"aea-framework-documentation/tac-skills-contract/#update-the-connection-parameters","title":"Update the Connection Parameters","text":"

Update the connection parameters of the TAC participants to allow them to connect to the same local agent communication network as the TAC controller.

First, retrieve controller's local ACN address by running the following in the controller agent's project terminal:

aea get-multiaddress fetchai -c -i fetchai/p2p_libp2p:0.27.5 -u public_uri\n

Then, in participant one, run this command (replace SOME_ADDRESS with the value you retrieved above):

aea config set --type dict vendor.fetchai.connections.p2p_libp2p.config \\\n'{\n  \"delegate_uri\": \"127.0.0.1:11001\",\n  \"entry_peers\": [\"SOME_ADDRESS\"],\n  \"local_uri\": \"127.0.0.1:9001\",\n  \"log_file\": \"libp2p_node.log\",\n  \"public_uri\": \"127.0.0.1:9001\"\n}'\n

Do the same in participant two (beware of the different port numbers):

aea config set --type dict vendor.fetchai.connections.p2p_libp2p.config \\\n'{\n  \"delegate_uri\": \"127.0.0.1:11002\",\n  \"entry_peers\": [\"SOME_ADDRESS\"],\n  \"local_uri\": \"127.0.0.1:9002\",\n  \"log_file\": \"libp2p_node.log\",\n  \"public_uri\": \"127.0.0.1:9002\"\n}'\n
"},{"location":"aea-framework-documentation/tac-skills-contract/#fund-agents-accounts","title":"Fund Agents' Accounts","text":"

Retrieve the address of each agent (in each terminal):

aea get-address fetchai\n

Go to the Dorado block explorer and request some test tokens via Get Funds.

To check the wealth of an AEA, use:

aea get-wealth fetchai\n
"},{"location":"aea-framework-documentation/tac-skills-contract/#run-the-aeas","title":"Run the AEAs","text":"

First, launch the tac_contract_controller then the participants by executing the following from their respective terminals:

aea run\n

The CLI tool supports launching several agents at once. For example, assuming you followed the tutorial, you can launch both TAC participant agents as follows from the root directory (ensure you run the controller agent first as above):

aea launch tac_participant_one tac_participant_two\n

You may want to try --multithreaded option in order to run the agents in the same process.

"},{"location":"aea-framework-documentation/tac-skills-contract/#cleaning-up","title":"Cleaning up","text":"

When you're finished, delete your AEAs:

aea delete tac_controller_contract\naea delete tac_participant_one\naea delete tac_participant_two\n
"},{"location":"aea-framework-documentation/tac-skills-contract/#demo-instructions-ethereum","title":"Demo Instructions (Ethereum)","text":"

Follow this instruction to run TAC against a local Ganache Ethereum test-net.

"},{"location":"aea-framework-documentation/tac-skills-contract/#create-tac-controller-aea","title":"Create TAC Controller AEA","text":"

In the root directory, fetch the controller AEA:

aea fetch fetchai/tac_controller_contract:0.32.5\ncd tac_controller_contract\naea install\naea build\n
Alternatively, create from scratch:

The following steps create the controller from scratch:

aea create tac_controller_contract\ncd tac_controller_contract\naea add connection fetchai/p2p_libp2p:0.27.5\naea add connection fetchai/soef:0.27.6\naea add connection fetchai/ledger:0.21.5\naea add skill fetchai/tac_control_contract:0.27.6\naea config set --type dict agent.dependencies \\\n'{\n  \"aea-ledger-fetchai\": {\"version\": \"<2.0.0,>=1.0.0\"},\n  \"aea-ledger-ethereum\": {\"version\": \"<2.0.0,>=1.0.0\"}\n}'\naea config set agent.default_connection fetchai/p2p_libp2p:0.27.5\naea config set agent.default_ledger ethereum\naea config set vendor.fetchai.connections.soef.config.chain_identifier ethereum\naea config set --type bool vendor.fetchai.skills.tac_control.is_abstract true\naea config set --type dict agent.default_routing \\\n'{\n  \"fetchai/contract_api:1.1.7\": \"fetchai/ledger:0.21.5\",\n  \"fetchai/ledger_api:1.1.7\": \"fetchai/ledger:0.21.5\",\n  \"fetchai/oef_search:1.1.7\": \"fetchai/soef:0.27.6\"\n}'\naea config set --type list vendor.fetchai.connections.p2p_libp2p.cert_requests \\\n'[{\"identifier\": \"acn\", \"ledger_id\": \"ethereum\", \"not_after\": \"2023-01-01\", \"not_before\": \"2022-01-01\", \"public_key\": \"fetchai\", \"save_path\": \".certs/conn_cert.txt\"}]'\naea install\naea build\n
"},{"location":"aea-framework-documentation/tac-skills-contract/#fetch-the-tac-participant-aeas_1","title":"Fetch the TAC Participant AEAs","text":"

In separate terminals, in the root directory, fetch at least two participants:

aea fetch fetchai/tac_participant_contract:0.22.5 --alias tac_participant_one\ncd tac_participant_one\naea install\naea build\ncd ..\naea fetch fetchai/tac_participant_contract:0.22.5 --alias tac_participant_two\ncd tac_participant_two\naea install\naea build\n
Alternatively, create from scratch:

In a separate terminal, in the root directory, create at least two tac participant AEAs:

aea create tac_participant_one\naea create tac_participant_two\n

Build participant one:

cd tac_participant_one\naea add connection fetchai/p2p_libp2p:0.27.5\naea add connection fetchai/soef:0.27.6\naea add connection fetchai/ledger:0.21.5\naea add skill fetchai/tac_participation:0.25.6\naea add skill fetchai/tac_negotiation:0.29.6\naea config set --type dict agent.dependencies \\\n'{\n  \"aea-ledger-fetchai\": {\"version\": \"<2.0.0,>=1.0.0\"},\n  \"aea-ledger-ethereum\": {\"version\": \"<2.0.0,>=1.0.0\"}\n}'\naea config set agent.default_connection fetchai/p2p_libp2p:0.27.5\naea config set agent.default_ledger ethereum\naea config set vendor.fetchai.connections.soef.config.chain_identifier ethereum\naea config set vendor.fetchai.skills.tac_participation.models.game.args.is_using_contract 'True' --type bool\naea config set vendor.fetchai.skills.tac_negotiation.models.strategy.args.is_contract_tx 'True' --type bool\naea config set --type dict agent.default_routing \\\n'{\n  \"fetchai/contract_api:1.1.7\": \"fetchai/ledger:0.21.5\",\n  \"fetchai/ledger_api:1.1.7\": \"fetchai/ledger:0.21.5\",\n  \"fetchai/oef_search:1.1.7\": \"fetchai/soef:0.27.6\"\n}'\naea config set --type dict agent.decision_maker_handler \\\n'{\n  \"dotted_path\": \"aea.decision_maker.gop:DecisionMakerHandler\",\n  \"file_path\": null\n}'\naea config set --type list vendor.fetchai.connections.p2p_libp2p.cert_requests \\\n'''[{\"identifier\": \"acn\", \"ledger_id\": \"ethereum\", \"message_format\": \"'{public_key}'\", \"not_after\": \"2023-01-01\", \"not_before\": \"2022-01-01\", \"public_key\": \"fetchai\", \"save_path\": \".certs/conn_cert.txt\"}]'''\naea install\naea build\n

Then, build participant two:

cd tac_participant_two\naea add connection fetchai/p2p_libp2p:0.27.5\naea add connection fetchai/soef:0.27.6\naea add connection fetchai/ledger:0.21.5\naea add skill fetchai/tac_participation:0.25.6\naea add skill fetchai/tac_negotiation:0.29.6\naea config set --type dict agent.dependencies \\\n'{\n  \"aea-ledger-fetchai\": {\"version\": \"<2.0.0,>=1.0.0\"},\n  \"aea-ledger-ethereum\": {\"version\": \"<2.0.0,>=1.0.0\"}\n}'\naea config set agent.default_connection fetchai/p2p_libp2p:0.27.5\naea config set agent.default_ledger ethereum\naea config set vendor.fetchai.connections.soef.config.chain_identifier ethereum\naea config set vendor.fetchai.skills.tac_participation.models.game.args.is_using_contract 'True' --type bool\naea config set vendor.fetchai.skills.tac_negotiation.models.strategy.args.is_contract_tx 'True' --type bool\naea config set --type dict agent.default_routing \\\n'{\n  \"fetchai/contract_api:1.1.7\": \"fetchai/ledger:0.21.5\",\n  \"fetchai/ledger_api:1.1.7\": \"fetchai/ledger:0.21.5\",\n  \"fetchai/oef_search:1.1.7\": \"fetchai/soef:0.27.6\"\n}'\naea config set --type dict agent.decision_maker_handler \\\n'{\n  \"dotted_path\": \"aea.decision_maker.gop:DecisionMakerHandler\",\n  \"file_path\": null\n}'\naea config set --type list vendor.fetchai.connections.p2p_libp2p.cert_requests \\\n'''[{\"identifier\": \"acn\", \"ledger_id\": \"ethereum\", \"message_format\": \"'{public_key}'\", \"not_after\": \"2023-01-01\", \"not_before\": \"2022-01-01\", \"public_key\": \"fetchai\", \"save_path\": \".certs/conn_cert.txt\"}]'''\naea install\naea build\n
"},{"location":"aea-framework-documentation/tac-skills-contract/#configure-the-agents-to-use-ethereum","title":"Configure the Agents to Use Ethereum","text":"

Run the following in every AEA's terminal:

aea config set agent.default_ledger ethereum\njson=$(printf '[{\"identifier\": \"acn\", \"ledger_id\": \"ethereum\", \"not_after\": \"2023-01-01\", \"not_before\": \"2022-01-01\", \"public_key\": \"fetchai\", \"message_format\": \"{public_key}\", \"save_path\": \".certs/conn_cert.txt\"}]')\naea config set --type list vendor.fetchai.connections.p2p_libp2p.cert_requests \"$json\"\naea config set vendor.fetchai.connections.soef.config.chain_identifier ethereum\n
"},{"location":"aea-framework-documentation/tac-skills-contract/#add-keys-for-all-aeas_1","title":"Add Keys for All AEAs","text":"

For every AEA in the competition (controller and participants):

First generate and add a private key:

aea generate-key ethereum\naea add-key ethereum ethereum_private_key.txt\n

Then create and add a separate private key for secure communication:

aea generate-key fetchai fetchai_connection_private_key.txt\naea add-key fetchai fetchai_connection_private_key.txt --connection\n

Finally, certify the key for use by the connections that request that:

aea issue-certificates\n
"},{"location":"aea-framework-documentation/tac-skills-contract/#update-the-game-parameters-in-the-controller_1","title":"Update the Game Parameters in the Controller","text":"

In the tac controller project, get and set the registration start time (set it to at least 5 minutes in the future):

aea config get vendor.fetchai.skills.tac_control_contract.models.parameters.args.registration_start_time\naea config set vendor.fetchai.skills.tac_control_contract.models.parameters.args.registration_start_time '01 01 2020  00:01'\n

To set the registration time, you may find handy the following command:

aea config set vendor.fetchai.skills.tac_control_contract.models.parameters.args.registration_start_time \"$(date -d \"5 minutes\" +'%d %m %Y %H:%M')\"\n
"},{"location":"aea-framework-documentation/tac-skills-contract/#update-the-connection-parameters_1","title":"Update the Connection Parameters","text":"

Update the connection parameters of the TAC participants to allow them to connect to the same local agent communication network as the TAC controller.

First, retrieve controller's local ACN address by running the following in the controller agent's project terminal:

aea get-multiaddress fetchai -c -i fetchai/p2p_libp2p:0.27.5 -u public_uri\n

Then, in participant one, run this command (replace SOME_ADDRESS with the value you retrieved above):

aea config set --type dict vendor.fetchai.connections.p2p_libp2p.config \\\n'{\n  \"delegate_uri\": \"127.0.0.1:11001\",\n  \"entry_peers\": [\"SOME_ADDRESS\"],\n  \"local_uri\": \"127.0.0.1:9001\",\n  \"log_file\": \"libp2p_node.log\",\n  \"public_uri\": \"127.0.0.1:9001\"\n}'\n

Do the same in participant two (beware of the different port numbers):

aea config set --type dict vendor.fetchai.connections.p2p_libp2p.config \\\n'{\n  \"delegate_uri\": \"127.0.0.1:11002\",\n  \"entry_peers\": [\"SOME_ADDRESS\"],\n  \"local_uri\": \"127.0.0.1:9002\",\n  \"log_file\": \"libp2p_node.log\",\n  \"public_uri\": \"127.0.0.1:9002\"\n}'\n
"},{"location":"aea-framework-documentation/tac-skills-contract/#fund-agents-accounts_1","title":"Fund Agents' Accounts","text":"

Run a local Ganache Ethereum test-net with funds for the addresses of the three AEAs in this demo:

docker run -p 8545:8545 trufflesuite/ganache-cli:latest --verbose --gasPrice=0 --gasLimit=0x1fffffffffffff --account=\"$(cat tac_controller_contract/ethereum_private_key.txt),1000000000000000000000\" --account=\"$(cat tac_participant_one/ethereum_private_key.txt),1000000000000000000000\" --account=\"$(cat tac_participant_two/ethereum_private_key.txt),1000000000000000000000\"\n

To check the wealth of an AEA, use:

aea get-wealth ethereum\n

You should get 1000000000000000000000.

"},{"location":"aea-framework-documentation/tac-skills-contract/#run-the-aeas_1","title":"Run the AEAs","text":"

First, launch the tac_contract_controller then the participants by executing the following from their respective terminals:

aea run\n

The CLI tool supports launching several agents at once. For example, assuming you followed the tutorial, you can launch both TAC participant agents as follows from the root directory (ensure you run the controller agent first as above):

aea launch tac_participant_one tac_participant_two\n

You may want to try --multithreaded option in order to run the agents in the same process.

"},{"location":"aea-framework-documentation/tac-skills-contract/#cleaning-up_1","title":"Cleaning up","text":"

When you're finished, delete your AEAs:

aea delete tac_controller_contract\naea delete tac_participant_one\naea delete tac_participant_two\n
"},{"location":"aea-framework-documentation/tac-skills/","title":"TAC Skills","text":"

The AEA TAC - trading agent competition - skills demonstrate an interaction between multiple AEAs in a game.

There are two types of AEAs:

  • The tac_controller which coordinates the game.
  • The tac_participant AEAs which compete in the game. The tac_participant AEAs trade tokens with each other to maximize their utility.
"},{"location":"aea-framework-documentation/tac-skills/#discussion","title":"Discussion","text":"

The scope of this specific demo is to demonstrate how the agents negotiate autonomously with each other while they pursue their goals by playing a game of TAC. Another AEA has the role of the controller, responsible for calculating the revenue for each participant and checking if the transaction messages are valid. Transactions are settled with the controller agent rather than against a public ledger.

"},{"location":"aea-framework-documentation/tac-skills/#communication","title":"Communication","text":"

There are two types of interactions:

  • between the participants and the controller, the game communication
  • between the participants, the negotiation
"},{"location":"aea-framework-documentation/tac-skills/#registration-communication","title":"Registration Communication","text":"

This diagram shows the communication between the various entities during the registration phase.

    sequenceDiagram\n        participant Agent_2\n        participant Agent_1\n        participant Search\n        participant Controller\n\n        activate Search\n        activate Controller\n\n        Controller->>Search: register_service\n        activate Agent_1\n        Agent_1->>Search: search\n        Search-->>Agent_1: controller\n        Agent_1->>Controller: register\n        activate Agent_2\n        Agent_2->>Search: search\n        Search-->>Agent_2: controller\n        Agent_2->>Controller: register\n        Controller->>Controller: start_game\n        Controller->>Agent_1: game_data\n        Controller->>Agent_2: game_data\n\n        deactivate Agent_1\n        deactivate Agent_2\n        deactivate Search\n        deactivate Controller
"},{"location":"aea-framework-documentation/tac-skills/#transaction-communication","title":"Transaction Communication","text":"

This diagram shows the communication between two AEAs and the controller. In this case, we have an AEA in the role of the seller, referred to as Seller_Agent. We also have an AEA in the role of the buyer, referred to as Buyer_Agent. During a given TAC, an AEA can be in both roles simultaneously in different bilateral interactions.

    sequenceDiagram\n        participant Buyer_Agent\n        participant Seller_Agent\n        participant Search\n        participant Controller\n\n        activate Buyer_Agent\n        activate Seller_Agent\n        activate Search\n        activate Controller\n\n        Seller_Agent->>Search: register_service\n        Buyer_Agent->>Search: search\n        Search-->>Buyer_Agent: list_of_agents\n        Buyer_Agent->>Seller_Agent: call_for_proposal\n        Seller_Agent->>Buyer_Agent: proposal\n        Buyer_Agent->>Seller_Agent: accept\n        Seller_Agent->>Buyer_Agent: match_accept\n        Seller_Agent->>Controller: transaction\n        Controller->>Controller: transaction_execution\n        Controller->>Seller_Agent: confirm_transaction\n        Controller->>Buyer_Agent: confirm_transaction\n\n        deactivate Buyer_Agent\n        deactivate Seller_Agent\n        deactivate Search\n        deactivate Controller

In the above case, the proposal received contains a set of good which the seller wishes to sell and a cost of them. The buyer AEA needs to determine if this is a good deal for them and if so, it accepts.

There is an equivalent diagram for seller AEAs set up to search for buyers and their interaction with AEAs which are registered as buyers. In that scenario, the proposal will instead, be a list of goods that the buyer wishes to buy and the price it is willing to pay for them.

"},{"location":"aea-framework-documentation/tac-skills/#option-1-aea-manager-approach","title":"Option 1: AEA Manager Approach","text":"

Follow this approach when using the AEA Manager Desktop app. Otherwise, skip and follow the CLI approach below.

"},{"location":"aea-framework-documentation/tac-skills/#preparation-instructions","title":"Preparation Instructions","text":"

Install the AEA Manager.

"},{"location":"aea-framework-documentation/tac-skills/#demo-instructions","title":"Demo Instructions","text":"

The following steps assume you have launched the AEA Manager Desktop app.

  1. Add a new AEA called controller with public id fetchai/tac_controller:0.26.0.

  2. Add another new AEA called participant_1 with public id fetchai/tac_participant:0.28.0.

  3. Add another new AEA called participant_2 with public id fetchai/tac_participant:0.28.0.

  4. Navigate to the settings of controller and under components > skill > fetchai/fetchai/tac_controller:0.22.0 > models > parameters > args update registration_start_time to the time you want TAC to begin (e.g. 2 minutes in the future)

  5. Run the controller AEA. Navigate to its logs and copy the multiaddress displayed. Stop the controller.

  6. Navigate to the settings of participant_1 and under components > connection > fetchai/p2p_libp2p:0.22.0 update as follows (make sure to replace the placeholder with the multiaddress):

    {\n\"delegate_uri\": \"127.0.0.1:11001\",\n  \"entry_peers\": [\"REPLACE_WITH_MULTI_ADDRESS_HERE\"],\n  \"local_uri\": \"127.0.0.1:9001\",\n  \"log_file\": \"libp2p_node.log\",\n  \"public_uri\": \"127.0.0.1:9001\"\n}\n
  7. Navigate to the settings of participant_2 and under components > connection > fetchai/p2p_libp2p:0.22.0 update as follows (make sure to replace the placeholder with the multiaddress):

    {\n\"delegate_uri\": \"127.0.0.1:11002\",\n  \"entry_peers\": [\"REPLACE_WITH_MULTI_ADDRESS_HERE\"],\n  \"local_uri\": \"127.0.0.1:9002\",\n  \"log_file\": \"libp2p_node.log\",\n  \"public_uri\": \"127.0.0.1:9002\"\n}\n
  8. You may add more participants by repeating steps 3 (with an updated name) and 6 (bumping the port numbers. See the difference between steps 5 and 6).

  9. Run the controller, then participant_1 and participant_2 (and any other participants you added).

In the controller's log, you should see the details of the transactions participants submit as well as changes in their scores and holdings. In participants' logs, you should see the agents trading.

"},{"location":"aea-framework-documentation/tac-skills/#option-2-cli-approach","title":"Option 2: CLI Approach","text":"

Follow this approach when using the aea CLI.

"},{"location":"aea-framework-documentation/tac-skills/#preparation-instructions_1","title":"Preparation Instructions","text":""},{"location":"aea-framework-documentation/tac-skills/#dependencies","title":"Dependencies","text":"

Follow the Preliminaries and Installation sections from the AEA quick start.

"},{"location":"aea-framework-documentation/tac-skills/#demo-instructions_1","title":"Demo Instructions","text":""},{"location":"aea-framework-documentation/tac-skills/#create-tac-controller-aea","title":"Create TAC Controller AEA","text":"

In the root directory, fetch the controller AEA:

aea fetch fetchai/tac_controller:0.30.5\ncd tac_controller\naea install\naea build\n
Alternatively, create from scratch:

The following steps create the controller from scratch:

aea create tac_controller\ncd tac_controller\naea add connection fetchai/p2p_libp2p:0.27.5\naea add connection fetchai/soef:0.27.6\naea add connection fetchai/ledger:0.21.5\naea add skill fetchai/tac_control:0.25.6\naea config set --type dict agent.dependencies \\\n'{\n  \"aea-ledger-fetchai\": {\"version\": \"<2.0.0,>=1.0.0\"}\n}'\naea config set agent.default_connection fetchai/p2p_libp2p:0.27.5\naea config set agent.default_ledger fetchai\naea config set --type dict agent.default_routing \\\n'{\n  \"fetchai/oef_search:1.1.7\": \"fetchai/soef:0.27.6\"\n}'\naea install\naea build\n
"},{"location":"aea-framework-documentation/tac-skills/#create-the-tac-participant-aeas","title":"Create the TAC Participant AEAs","text":"

In a separate terminal, in the root directory, fetch at least two participants:

aea fetch fetchai/tac_participant:0.32.5 --alias tac_participant_one\ncd tac_participant_one\naea install\naea build\ncd ..\naea fetch fetchai/tac_participant:0.32.5 --alias tac_participant_two\ncd tac_participant_two\naea build\n
Alternatively, create from scratch:

In a separate terminal, in the root directory, create at least two tac participant AEAs:

aea create tac_participant_one\naea create tac_participant_two\n

Build participant one:

cd tac_participant_one\naea add connection fetchai/p2p_libp2p:0.27.5\naea add connection fetchai/soef:0.27.6\naea add connection fetchai/ledger:0.21.5\naea add skill fetchai/tac_participation:0.25.6\naea add skill fetchai/tac_negotiation:0.29.6\naea config set --type dict agent.dependencies \\\n'{\n  \"aea-ledger-fetchai\": {\"version\": \"<2.0.0,>=1.0.0\"}\n}'\naea config set agent.default_connection fetchai/p2p_libp2p:0.27.5\naea config set agent.default_ledger fetchai\naea config set --type dict agent.default_routing \\\n'{\n  \"fetchai/ledger_api:1.1.7\": \"fetchai/ledger:0.21.5\",\n  \"fetchai/oef_search:1.1.7\": \"fetchai/soef:0.27.6\"\n}'\naea config set --type dict agent.decision_maker_handler \\\n'{\n  \"dotted_path\": \"aea.decision_maker.gop:DecisionMakerHandler\",\n  \"file_path\": null\n}'\naea install\naea build\n

Then, build participant two:

cd tac_participant_two\naea add connection fetchai/p2p_libp2p:0.27.5\naea add connection fetchai/soef:0.27.6\naea add connection fetchai/ledger:0.21.5\naea add skill fetchai/tac_participation:0.25.6\naea add skill fetchai/tac_negotiation:0.29.6\naea config set --type dict agent.dependencies \\\n'{\n  \"aea-ledger-fetchai\": {\"version\": \"<2.0.0,>=1.0.0\"}\n}'\naea config set agent.default_connection fetchai/p2p_libp2p:0.27.5\naea config set agent.default_ledger fetchai\naea config set --type dict agent.default_routing \\\n'{\n  \"fetchai/ledger_api:1.1.7\": \"fetchai/ledger:0.21.5\",\n  \"fetchai/oef_search:1.1.7\": \"fetchai/soef:0.27.6\"\n}'\naea config set --type dict agent.decision_maker_handler \\\n'{\n  \"dotted_path\": \"aea.decision_maker.gop:DecisionMakerHandler\",\n  \"file_path\": null\n}'\naea install\naea build\n
"},{"location":"aea-framework-documentation/tac-skills/#add-keys-for-all-aeas","title":"Add Keys for All AEAs","text":"

Create the private key for the AEA for Fetch.ai Dorado:

aea generate-key fetchai\naea add-key fetchai fetchai_private_key.txt\n

Next, create a private key used to secure the AEA's communications:

aea generate-key fetchai fetchai_connection_private_key.txt\naea add-key fetchai fetchai_connection_private_key.txt --connection\n

Finally, certify the key for use by the connections that request that:

aea issue-certificates\n
"},{"location":"aea-framework-documentation/tac-skills/#update-the-game-parameters-in-the-controller","title":"Update the Game Parameters in the Controller","text":"

Navigate to the tac controller project, then use the command line to get and set the start time (set it to at least two minutes in the future):

aea config get vendor.fetchai.skills.tac_control.models.parameters.args.registration_start_time\naea config set vendor.fetchai.skills.tac_control.models.parameters.args.registration_start_time '01 01 2020  00:01'\n

To set the registration time, you may find handy the following command:

aea config set vendor.fetchai.skills.tac_control.models.parameters.args.registration_start_time \"$(date -d \"2 minutes\" +'%d %m %Y %H:%M')\"\n
"},{"location":"aea-framework-documentation/tac-skills/#update-the-connection-parameters","title":"Update the Connection Parameters","text":"

Briefly run the controller AEA:

aea run\n

Once you see a message of the form To join its network use multiaddr 'SOME_ADDRESS' take note of the address. (Alternatively, use aea get-multiaddress fetchai -c -i fetchai/p2p_libp2p:0.27.5 -u public_uri to retrieve the address.)

Then, in the participant one, run this command (replace SOME_ADDRESS with the correct value as described above):

aea config set --type dict vendor.fetchai.connections.p2p_libp2p.config \\\n'{\n  \"delegate_uri\": \"127.0.0.1:11001\",\n  \"entry_peers\": [\"SOME_ADDRESS\"],\n  \"local_uri\": \"127.0.0.1:9001\",\n  \"log_file\": \"libp2p_node.log\",\n  \"public_uri\": \"127.0.0.1:9001\"\n}'\n

Do the same in participant two (beware of the different port numbers):

aea config set --type dict vendor.fetchai.connections.p2p_libp2p.config \\\n'{\n  \"delegate_uri\": \"127.0.0.1:11002\",\n  \"entry_peers\": [\"SOME_ADDRESS\"],\n  \"local_uri\": \"127.0.0.1:9002\",\n  \"log_file\": \"libp2p_node.log\",\n  \"public_uri\": \"127.0.0.1:9002\"\n}'\n

This allows the TAC participants to connect to the same local agent communication network as the TAC controller.

"},{"location":"aea-framework-documentation/tac-skills/#run-the-aeas","title":"Run the AEAs","text":"

First, launch the tac_controller:

aea run\n

The CLI tool supports the launch of several agents at once.

For example, assuming you followed the tutorial, you can launch both the TAC agents as follows from the root directory:

aea launch tac_participant_one tac_participant_two\n

You may want to try --multithreaded option in order to run the agents in the same process.

"},{"location":"aea-framework-documentation/tac-skills/#cleaning-up","title":"Cleaning up","text":"

When you're finished, delete your AEAs:

aea delete tac_controller\naea delete tac_participant_one\naea delete tac_participant_two\n
"},{"location":"aea-framework-documentation/tac/","title":"TAC External App","text":"

Note

This app is no longer maintained.

The original TAC has its own repo.

Follow the instructions below to build and run the TAC demo.

"},{"location":"aea-framework-documentation/tac/#requirements","title":"Requirements","text":"

Make sure you are running Docker and Docker Compose.

"},{"location":"aea-framework-documentation/tac/#quick-start","title":"Quick Start","text":"

Clone the repo to include submodules.

git clone git@github.com:fetchai/agents-tac.git --recursive && cd agents-tac\n

Check you have pipenv.

which pipenv\n

If you don't have it, install it. Instructions are here.

Create and launch a virtual environment.

pipenv --python 3.7 && pipenv shell\n

Install the dependencies.

pipenv install\n

Install the package.

python setup.py install\n

Run the launch script. This may take a while.

python scripts/launch.py\n

The Visdom server is now running.

The controller GUI at http://localhost:8097 provides real time insights.

In the Environment tab, make sure you have the tac_controller environment selected.

"},{"location":"aea-framework-documentation/tac/#alternative-build-and-run","title":"Alternative Build and Run","text":"

In a new terminal window, clone the repo, build the sandbox, and launch it.

git clone git@github.com:fetchai/agents-tac.git --recursive && cd agents-tac\npipenv --python 3.7 && pipenv shell\npython setup.py install\ncd sandbox && docker-compose build\ndocker-compose up\n

In a new terminal window, enter the virtual environment, and connect a template agent to the sandbox.

pipenv shell\npython templates/v1/basic.py --name my_agent --dashboard\n

Click through to the controller GUI.

"},{"location":"aea-framework-documentation/tac/#possible-gotchas","title":"Possible Gotchas","text":"

Stop all running containers before restart.

docker stop $(docker ps -q)\n

To remove all images, run the following command.

# mac\ndocker ps -q | xargs docker stop ; docker system prune -a\n
"},{"location":"aea-framework-documentation/thermometer-skills/","title":"Thermometer Skills","text":"

The AEA thermometer skills demonstrate an interaction between two AEAs, one purchasing temperature data from the other.

  • The provider of thermometer data (the thermometer).
  • The buyer of thermometer data (the thermometer_client).
"},{"location":"aea-framework-documentation/thermometer-skills/#discussion","title":"Discussion","text":"

This demo aims to demonstrate how to create a very simple AEA with the usage of the AEA framework and a thermometer sensor. The thermometer AEA will read data from the sensor each time a client requests the data and will deliver it to the client upon payment. To keep the demo simple, we avoided the usage of a database since this would increase the complexity. As a result, the AEA can provide only one reading from the sensor. This demo does not utilise a smart contract. As a result, the ledger interaction is only for completing a transaction.

"},{"location":"aea-framework-documentation/thermometer-skills/#communication","title":"Communication","text":"

This diagram shows the communication between the various entities as data is successfully sold by the thermometer AEA to the client AEA.

    sequenceDiagram\n        participant Search\n        participant Client_AEA\n        participant Thermometer_AEA\n        participant Blockchain\n\n        activate Client_AEA\n        activate Search\n        activate Thermometer_AEA\n        activate Blockchain\n\n        Thermometer_AEA->>Search: register_service\n        Client_AEA->>Search: search\n        Search-->>Client_AEA: list_of_agents\n        Client_AEA->>Thermometer_AEA: call_for_proposal\n        Thermometer_AEA->>Client_AEA: propose\n        Client_AEA->>Thermometer_AEA: accept\n        Thermometer_AEA->>Client_AEA: match_accept\n        Client_AEA->>Blockchain: transfer_funds\n        Client_AEA->>Thermometer_AEA: send_transaction_hash\n        Thermometer_AEA->>Blockchain: check_transaction_status\n        Thermometer_AEA->>Client_AEA: send_data\n\n        deactivate Client_AEA\n        deactivate Search\n        deactivate Thermometer_AEA\n        deactivate Blockchain
"},{"location":"aea-framework-documentation/thermometer-skills/#option-1-aea-manager-approach","title":"Option 1: AEA Manager Approach","text":"

Follow this approach when using the AEA Manager Desktop app. Otherwise, skip and follow the CLI approach below.

"},{"location":"aea-framework-documentation/thermometer-skills/#preparation-instructions","title":"Preparation Instructions","text":"

Install the AEA Manager.

"},{"location":"aea-framework-documentation/thermometer-skills/#demo-instructions","title":"Demo Instructions","text":"

The following steps assume you have launched the AEA Manager Desktop app.

  1. Add a new AEA called my_thermometer_aea with public id fetchai/thermometer_aea:0.30.5.

  2. Add another new AEA called my_thermometer_client with public id fetchai/thermometer_client:0.32.5.

  3. Copy the address from the my_thermometer_client into your clip board. Then go to the Dorado block explorer and request some test tokens via Get Funds.

  4. Run the my_thermometer_aea AEA. Navigate to its logs and copy the multiaddress displayed.

  5. Navigate to the settings of the my_thermometer_client and under components > connection > fetchai/p2p_libp2p:0.22.0 update as follows (make sure to replace the placeholder with the multiaddress):

    {\n\"delegate_uri\": \"127.0.0.1:11001\",\n  \"entry_peers\": [\"REPLACE_WITH_MULTI_ADDRESS_HERE\"],\n  \"local_uri\": \"127.0.0.1:9001\",\n  \"log_file\": \"libp2p_node.log\",\n  \"public_uri\": \"127.0.0.1:9001\"\n}\n
  6. Run the my_thermometer_client.

In the AEA's logs, you should see the agent trading successfully.

"},{"location":"aea-framework-documentation/thermometer-skills/#option-2-cli-approach","title":"Option 2: CLI Approach","text":"

Follow this approach when using the aea CLI.

"},{"location":"aea-framework-documentation/thermometer-skills/#preparation-instructions_1","title":"Preparation Instructions","text":""},{"location":"aea-framework-documentation/thermometer-skills/#dependencies","title":"Dependencies","text":"

Follow the Preliminaries and Installation sections from the AEA quick start.

"},{"location":"aea-framework-documentation/thermometer-skills/#demo-instructions_1","title":"Demo Instructions","text":"

A demo to run the thermometer scenario with a true ledger transaction This demo assumes the buyer trusts the seller AEA to send the data upon successful payment.

"},{"location":"aea-framework-documentation/thermometer-skills/#create-thermometer-aea","title":"Create Thermometer AEA","text":"

First, fetch the thermometer AEA:

aea fetch fetchai/thermometer_aea:0.30.5 --alias my_thermometer_aea\ncd my_thermometer_aea\naea install\naea build\n
Alternatively, create from scratch:

The following steps create the thermometer AEA from scratch:

aea create my_thermometer_aea\ncd my_thermometer_aea\naea add connection fetchai/p2p_libp2p:0.27.5\naea add connection fetchai/soef:0.27.6\naea add connection fetchai/ledger:0.21.5\naea add skill fetchai/thermometer:0.27.6\naea install\naea build\naea config set agent.default_connection fetchai/p2p_libp2p:0.27.5\naea config set --type dict agent.default_routing \\\n'{\n  \"fetchai/ledger_api:1.1.7\": \"fetchai/ledger:0.21.5\",\n  \"fetchai/oef_search:1.1.7\": \"fetchai/soef:0.27.6\"\n}'\n
"},{"location":"aea-framework-documentation/thermometer-skills/#create-thermometer-client","title":"Create Thermometer Client","text":"

Then, fetch the thermometer client AEA:

aea fetch fetchai/thermometer_client:0.32.5 --alias my_thermometer_client\ncd my_thermometer_client\naea install\naea build\n
Alternatively, create from scratch:

The following steps create the thermometer client from scratch:

aea create my_thermometer_client\ncd my_thermometer_client\naea add connection fetchai/p2p_libp2p:0.27.5\naea add connection fetchai/soef:0.27.6\naea add connection fetchai/ledger:0.21.5\naea add skill fetchai/thermometer_client:0.26.6\naea install\naea build\naea config set agent.default_connection fetchai/p2p_libp2p:0.27.5\naea config set --type dict agent.default_routing \\\n'{\n  \"fetchai/ledger_api:1.1.7\": \"fetchai/ledger:0.21.5\",\n  \"fetchai/oef_search:1.1.7\": \"fetchai/soef:0.27.6\"\n}'\n
"},{"location":"aea-framework-documentation/thermometer-skills/#add-keys-for-the-thermometer-aea","title":"Add Keys for the Thermometer AEA","text":"

First, create the private key for the thermometer AEA based on the network you want to transact. To generate and add a private-public key pair for Fetch.ai Dorado use:

aea generate-key fetchai\naea add-key fetchai fetchai_private_key.txt\n

Next, create a private key used to secure the AEA's communications:

aea generate-key fetchai fetchai_connection_private_key.txt\naea add-key fetchai fetchai_connection_private_key.txt --connection\n

Finally, certify the key for use by the connections that request that:

aea issue-certificates\n
"},{"location":"aea-framework-documentation/thermometer-skills/#add-keys-and-generate-wealth-for-the-thermometer-client-aea","title":"Add Keys and Generate Wealth for the Thermometer Client AEA","text":"

The thermometer client needs to have some wealth to purchase the thermometer information.

First, create the private key for the thermometer client AEA based on the network you want to transact. To generate and add a private-public key pair for Fetch.ai use:

aea generate-key fetchai\naea add-key fetchai fetchai_private_key.txt\n

Then, create some wealth for your thermometer client based on the network you want to transact with. On the Fetch.ai testnet network:

aea generate-wealth fetchai\n

Next, create a private key used to secure the AEA's communications:

aea generate-key fetchai fetchai_connection_private_key.txt\naea add-key fetchai fetchai_connection_private_key.txt --connection\n

Finally, certify the key for use by the connections that request that:

aea issue-certificates\n
"},{"location":"aea-framework-documentation/thermometer-skills/#run-both-aeas","title":"Run both AEAs","text":"

Run both AEAs from their respective terminals.

First, run the thermometer AEA:

aea run\n

Once you see a message of the form To join its network use multiaddr 'SOME_ADDRESS' take note of the address. (Alternatively, use aea get-multiaddress fetchai -c -i fetchai/p2p_libp2p:0.27.5 -u public_uri to retrieve the address.) This is the entry peer address for the local agent communication network created by the thermometer AEA.

Then, in the thermometer client, run this command (replace SOME_ADDRESS with the correct value as described above):

aea config set --type dict vendor.fetchai.connections.p2p_libp2p.config \\\n'{\n  \"delegate_uri\": \"127.0.0.1:11001\",\n  \"entry_peers\": [\"SOME_ADDRESS\"],\n  \"local_uri\": \"127.0.0.1:9001\",\n  \"log_file\": \"libp2p_node.log\",\n  \"public_uri\": \"127.0.0.1:9001\"\n}'\n

This allows the thermometer client to connect to the same local agent communication network as the thermometer AEA.

Then run the thermometer client AEA:

aea run\n

You can see that the AEAs find each other, negotiate and eventually trade.

"},{"location":"aea-framework-documentation/thermometer-skills/#cleaning-up","title":"Cleaning up","text":"

When you're done, go up a level and delete the AEAs.

cd ..\naea delete my_thermometer_aea\naea delete my_thermometer_client\n
"},{"location":"aea-framework-documentation/trust/","title":"Trust Minimisation","text":"

AEA applications have different requirements for trustlessness or trust minimisation.

For example, using the AEA weather skills demo without ledger payments means that the client has to trust the weather station to send the weather data it purchased and that this data is in fact valid. Similarly, the weather station must trust that the client somehow sends the payment amount to which they agreed.

A step-up, if you run the weather skills demo with a ledger (e.g. Fetch.ai or Ethereum) then the client must still trust that the weather station sends valid data. However, all payment transactions are executed via the public ledger. This means the weather station no longer needs to trust the client for payment and can verify whether the transactions take place on the public ledger.

We can further minimise trust requirements by incorporating a third party as an arbitrator or escrow implemented in a smart contract to further reduce trust requirements. However, in the current weather skills demo, there are limits to trustlessness as the station ultimately offers unverifiable data.

Another example of minimising trust, is applications with (non-fungible) token transactions involving atomic swaps where trustlessness is clearly satisfied (e.g. in the TAC demo).

"},{"location":"aea-framework-documentation/upgrading/","title":"Upgrading","text":"

This page provides some tips on how to upgrade AEA projects between different versions of the AEA framework. For full release notes check the AEA repo.

The primary tool for upgrading AEA projects is the aea upgrade command in the CLI.

Below we describe the additional manual steps required to upgrade between different versions:

"},{"location":"aea-framework-documentation/upgrading/#v122-to-v123","title":"v1.2.2 to v1.2.3","text":"

Ensure you update the plugins to their latest version (all plugins are changed in this release)

Update the packages to the latest versions (especially protocols).

Update development environment

"},{"location":"aea-framework-documentation/upgrading/#v120-to-v122","title":"v1.2.0 to v1.2.2","text":"

Ensure you update the plugins to their latest version (all plugins are changed in this release)

Update the packages to the latest versions (especially protocols).

Regenerate your own written protocols (protocol generator was updated)

"},{"location":"aea-framework-documentation/upgrading/#v111-to-v120","title":"v1.1.1 to v1.2.0","text":"

Ensure you update the plugins to their latest version (fetchai and cosmos plugins are changed in this release)

Update the packages to the latest versions (especially p2p_libp2p related packages are updated)

Check packages\u2019 and agents\u2019 configurations are correct (e.g. the fetchai test-net name is changed for the Dorado network)

"},{"location":"aea-framework-documentation/upgrading/#v110-to-v111","title":"v1.1.0 to v1.1.1","text":"

No backwards incompatible changes.

Upgrade fetchai/p2p_libp2p connection package to the latest version which fixes a slow DHT lookup problem

We advise everyone to upgrade their fetchai packages and plugins to get the latest fixes.

"},{"location":"aea-framework-documentation/upgrading/#v102-to-v110","title":"v1.0.2 to v1.1.0","text":"

No backwards incompatible changes.

We advise everyone to upgrade their fetchai packages and plugins to get the latest fixes.

"},{"location":"aea-framework-documentation/upgrading/#v101-to-v102","title":"v1.0.1 to v1.0.2","text":"

No backwards incompatible changes.

We advise everyone to upgrade their fetchai packages and plugins to get the latest fixes.

"},{"location":"aea-framework-documentation/upgrading/#v100-to-v101","title":"v1.0.0 to v1.0.1","text":"

No backwards incompatible changes.

We advise everyone to upgrade their fetchai packages to get the latest fixes.

"},{"location":"aea-framework-documentation/upgrading/#v100rc2-to-v100","title":"v1.0.0rc2 to v1.0.0","text":"

No backwards incompatible changes to component development.

We advise everyone to upgrade to v1 as soon as possible. When upgrading from versions below v1.0.0rc1 first upgrade to the first release candidate, then to v1.

"},{"location":"aea-framework-documentation/upgrading/#v100rc1-to-v100rc2","title":"v1.0.0rc1 to v1.0.0rc2","text":"

No backwards incompatible changes to component development.

Various configuration changes introduced in v1.0.0rc1 are now enforced strictly.

"},{"location":"aea-framework-documentation/upgrading/#v0111-to-v100rc1","title":"v0.11.1 to v1.0.0rc1","text":"

No backwards incompatible changes to component development.

The aea-config.yaml now requires the field required_ledgers which must specify all ledgers for which private keys are required to run the agent. Please add it to your project.

The registry_path field has been removed from the aea-config.yaml. Please remove it from your project.

All packages provided by author fetchai must be upgraded.

"},{"location":"aea-framework-documentation/upgrading/#v0110-to-v0111","title":"v0.11.0 to v0.11.1","text":"

No backwards incompatible changes.

"},{"location":"aea-framework-documentation/upgrading/#v0101-to-v0110","title":"v0.10.1 to v0.11.0","text":"

Take special care when upgrading to v0.11.0. We introduced several breaking changes in preparation for v1!

"},{"location":"aea-framework-documentation/upgrading/#cli-gui","title":"CLI GUI","text":"

We removed the CLI GUI. It was not used by anyone as far as we know and needs to be significantly improved. Soon we will release the AEA Manager App to make up for this.

"},{"location":"aea-framework-documentation/upgrading/#message-routing","title":"Message Routing","text":"

Routing has been completely revised and simplified. The new message routing logic is described here.

When upgrading take the following steps:

  • For agent-to-agent communication: ensure the default routing and default connection are correctly defined and that the dialogues used specify the agent's address as the self_address. This is most likely already the case. Only in some edge cases will you need to use an EnvelopeContext to target a connection different from the one specified in the default_routing map.

  • For component-to-component communication: there is now only one single way to route component to component (skill to skill, skill to connection, connection to skill) messages, this is by specifying the component id in string form in the sender/to field. The EnvelopeContext can no longer be used, messages are routed based on their target (to field). Ensure that dialogues in skills set the skill_id as the self_address (in connections they need to set the connection_id).

"},{"location":"aea-framework-documentation/upgrading/#agent-configuration-and-ledger-plugins","title":"Agent Configuration and Ledger Plugins","text":"

Agent configuration files have a new optional field, dependencies, analogous to dependencies field in other AEA packages. The default value is the empty object {}. The field will be made mandatory in the next release.

Crypto modules have been extracted and released as independent plug-ins, released on PyPI. In particular:

  • Fetch.ai crypto classes have been released in the aea-ledger-fetchai package;
  • Ethereum crypto classes have been released in the aea-ledger-ethereum package;
  • Cosmos crypto classes have been released in the aea-ledger-cosmos package.

If an AEA project, or an AEA package, makes use of crypto functionalities, it will be needed to add the above packages as PyPI dependencies with version specifiers ranging from the latest minor and the latest minor + 1 (excluded). E.g. if the latest version if 0.1.0, the version specifier should be <0.2.0,>=0.1.0:

dependencies:\naea-ledger-cosmos:\nversion: <2.0.0,>=1.0.0\naea-ledger-ethereum:\nversion: <2.0.0,>=1.0.0\naea-ledger-fetchai:\nversion: <2.0.0,>=1.0.0\n

The version specifier sets are important, as these plug-ins, at version 0.1.0, depend on a specific range of the aea package.

Then, running aea install inside the AEA project should install them in the current Python environment.

For more, read the guide on ledger plugins.

"},{"location":"aea-framework-documentation/upgrading/#v0100-to-v0101","title":"v0.10.0 to v0.10.1","text":"

No backwards incompatible changes for skill and connection development.

"},{"location":"aea-framework-documentation/upgrading/#v092-to-v0100","title":"v0.9.2 to v0.10.0","text":"

Skill development sees no backward incompatible changes.

Connection development requires updating the keyword arguments of the constructor: the new data_dir argument must be defined.

Protocol specifications now need to contain a protocol_specification_id in addition to the public id. The protocol_specification_id is used for identifying Envelopes during transport. By being able to set the id independently of the protocol id, backwards compatibility in the specification (and therefore wire format) can be maintained even when the Python implementation changes.

Please update to the latest packages by running aea upgrade and then re-generating your own protocols.

"},{"location":"aea-framework-documentation/upgrading/#v091-to-v092","title":"v0.9.1 to v0.9.2","text":"

No backwards incompatible changes for skill and connection development.

"},{"location":"aea-framework-documentation/upgrading/#v090-to-v091","title":"v0.9.0 to v0.9.1","text":"

No backwards incompatible changes for skill and connection development.

"},{"location":"aea-framework-documentation/upgrading/#v080-to-v090","title":"v0.8.0 to v0.9.0","text":"

This release introduces proof of representation in the ACN. You will need to upgrade to the latest fetchai/p2p_libp2p or fetchai/p2p_libp2p_client connection and then use two key pairs, one for your AEA's decision maker and one for the connection.

Please update to the latest packages by running aea upgrade.

"},{"location":"aea-framework-documentation/upgrading/#v075-to-v080","title":"v0.7.5 to v0.8.0","text":"

Minimal backwards incompatible changes for skill and connection development:

  • The semantics of the <, <=, > and >= relations in ConstraintTypes are simplified.
  • Protocols now need to correctly define terminal states. Regenerate your protocol to identify if your protocol's dialogue rules are valid.

Please update to the latest packages by running aea upgrade.

"},{"location":"aea-framework-documentation/upgrading/#v074-to-v075","title":"v0.7.4 to v0.7.5","text":"

No backwards incompatible changes for skill and connection development.

"},{"location":"aea-framework-documentation/upgrading/#v073-to-v074","title":"v0.7.3 to v0.7.4","text":"

No backwards incompatible changes for skill and connection development.

"},{"location":"aea-framework-documentation/upgrading/#v072-to-v073","title":"v0.7.2 to v0.7.3","text":"

No backwards incompatible changes for skill and connection development.

"},{"location":"aea-framework-documentation/upgrading/#v071-to-v072","title":"v0.7.1 to v0.7.2","text":"

No backwards incompatible changes for skill and connection development.

"},{"location":"aea-framework-documentation/upgrading/#v070-to-v071","title":"v0.7.0 to v0.7.1","text":"

To improve performance, in particular optimize memory usage, we refactored the Message and Dialogue classes. This means all protocols need to be bumped to the latest version or regenerated using the aea generate protocol command in the CLI.

"},{"location":"aea-framework-documentation/upgrading/#v063-to-v070","title":"v0.6.3 to v0.7.0","text":"

Multiple breaking changes require action in this order:

  • Custom configuration overrides in aea-config.yaml are now identified via public_id rather than author, name and version individually. Please replace the three fields with the equivalent public_id.

  • Run aea upgrade command to upgrade your project's dependencies. Note, you still do have to manually update the public ids under default_routing and default_connection in aea-config.yaml as well as the public ids in the non-vendor packages.

  • Previously, connection fetchai/stub, skill fetchai/error and protocols fetchai/default, fetchai/signing and fetchai/state_update where part of the AEA distribution. Now they need to be fetched from registry. If you create a new project with aea create then this happens automatically. For existing projects, add the dependencies explicitly if not already present. You also must update the import paths as follows:

    • aea.connections.stub > packages.fetchai.connections.stub
    • aea.protocols.default > packages.fetchai.protocols.default
    • aea.protocols.signing > packages.fetchai.protocols.signing
    • aea.protocols.state_update > packages.fetchai.protocols.state_update
    • aea.skills.error > packages.fetchai.skills.error
  • If you use custom protocols, regenerate them.

  • In your own skills' __init__.py files add the public id (updating the string as appropriate):

from aea.configurations.base import PublicId\nPUBLIC_ID = PublicId.from_str(\"author/name:0.1.0\")\n
  • The fetchai/http protocol's bodyy field has been renamed to body.

  • Skills can now specify connections as dependencies in the configuration YAML.

"},{"location":"aea-framework-documentation/upgrading/#v062-to-v063","title":"v0.6.2 to v0.6.3","text":"

A new upgrade command is introduced to upgrade agent projects and components to their latest versions on the registry. To use the command first upgrade the AEA PyPI package to the latest version, then enter your project and run aea upgrade. The project's vendor dependencies will be updated where possible.

"},{"location":"aea-framework-documentation/upgrading/#v061-to-v062","title":"v0.6.1 to v0.6.2","text":"

No public APIs have been changed.

"},{"location":"aea-framework-documentation/upgrading/#v060-to-v061","title":"v0.6.0 to v0.6.1","text":"

The soef connection and oef_search protocol have backward incompatible changes.

"},{"location":"aea-framework-documentation/upgrading/#v054-to-v060","title":"v0.5.4 to v0.6.0","text":""},{"location":"aea-framework-documentation/upgrading/#dialogue-and-dialogues-api-updates","title":"Dialogue and Dialogues API Updates","text":"

The dialogue and dialogues APIs have changed significantly. The constructor is different for both classes and there are now four primary methods for the developer:

  • Dialogues.create: this method is used to create a new dialogue and message:
cfp_msg, fipa_dialogue = fipa_dialogues.create(\ncounterparty=opponent_address,\nperformative=FipaMessage.Performative.CFP,\nquery=query,\n)\n

The method will raise if the provided arguments are inconsistent.

  • Dialogues.create_with_message: this method is used to create a new dialogue from a message:
fipa_dialogue = fipa_dialogues.create_with_message(\ncounterparty=opponent_address,\ninitial_message=cfp_msg\n)\n

The method will raise if the provided arguments are inconsistent.

  • Dialogues.update: this method is used to handle messages passed by the framework:
fipa_dialogue = fipa_dialogues.update(\nmessage=cfp_msg\n)\n

The method will return a valid dialogue if it is a valid message, otherwise it will return None.

  • Dialogue.reply: this method is used to reply within a dialogue:
proposal_msg = fipa_dialogue.reply(\nperformative=FipaMessage.Performative.PROPOSE,\ntarget_message=cfp_msg,\nproposal=proposal,\n)\n

The method will raise if the provided arguments are inconsistent.

The new methods significantly reduce the lines of code needed to maintain a dialogue. They also make it easier for the developer to construct valid dialogues and messages.

"},{"location":"aea-framework-documentation/upgrading/#fetchaicrypto-default-crypto","title":"FetchAICrypto - Default Crypto","text":"

The FetchAICrypto has been upgraded to the default crypto. Update your default_ledger to fetchai.

"},{"location":"aea-framework-documentation/upgrading/#private-key-file-naming","title":"Private Key File Naming","text":"

The private key files are now consistently named with the ledger_id followed by _private_key.txt (e.g. fetchai_private_key.txt). Rename your existing files to match this pattern.

"},{"location":"aea-framework-documentation/upgrading/#type-in-package-yaml","title":"Type in Package YAML","text":"

The package YAML files now contain a type field. This must be added for the loading mechanism to work properly.

"},{"location":"aea-framework-documentation/upgrading/#moved-address-type","title":"Moved Address Type","text":"

The address type has moved to aea.common. The import paths must be updated.

"},{"location":"aea-framework-documentation/upgrading/#v053-to-v054","title":"v0.5.3 to v0.5.4","text":"

The contract base class was slightly modified. If you have implemented your own contract package you need to update it accordingly.

The dialogue reference nonce is now randomly generated. This can result in previously working but buggy implementations (which relied on the order of dialogue reference nonces) to now fail.

"},{"location":"aea-framework-documentation/upgrading/#v052-to-v053","title":"v0.5.2 to v0.5.3","text":"

Connection states and logger usage in connections where updated. If you have implemented your own connection package you need to update it accordingly.

Additional dialogue consistency checks where enabled. This can result in previously working but buggy implementations to now fail.

"},{"location":"aea-framework-documentation/upgrading/#v051-to-052","title":"v0.5.1 to 0.5.2","text":"

No public APIs have been changed.

"},{"location":"aea-framework-documentation/upgrading/#v050-to-051","title":"v0.5.0 to 0.5.1","text":"

No public APIs have been changed.

"},{"location":"aea-framework-documentation/upgrading/#v041-to-050","title":"v0.4.1 to 0.5.0","text":"

A number of breaking changes where introduced which make backwards compatibility of skills rare.

  • Ledger APIs LedgerApis have been removed from the AEA constructor and skill context. LedgerApis are now exposed in the LedgerConnection (fetchai/ledger). To communicate with the LedgerApis use the fetchai/ledger_api protocol. This allows for more flexibility (anyone can add another LedgerAPI to the registry and execute it with the connection) and removes dependencies from the core framework.
  • Skills can now depend on other skills. As a result, skills have a new required configuration field in skill.yaml files, by default empty: skills: [].
"},{"location":"aea-framework-documentation/upgrading/#v040-to-v041","title":"v0.4.0 to v0.4.1","text":"

There are no upgrade requirements if you use the CLI based approach to AEA development.

Connections are now added via Resources to the AEA, not the AEA constructor directly. For programmatic usage remove the list of connections from the AEA constructor and instead add the connections to resources.

"},{"location":"aea-framework-documentation/upgrading/#v033-to-v040","title":"v0.3.3 to v0.4.0","text":"
  • Message sending in the skills has been updated. In the past you had to construct messages, then serialize them and place them in an envelope:

    cfp_msg = FipaMessage(...)\nself.context.outbox.put_message(\nto=opponent_addr,\nsender=self.context.agent_address,\nprotocol_id=FipaMessage.protocol_id,\nmessage=FipaSerializer().encode(cfp_msg),\n)\n# or\ncfp_msg = FipaMessage(...)\nenvelope = Envelope(\nto=opponent_addr,\nsender=self.context.agent_address,\nprotocol_id=FipaMessage.protocol_id,\nmessage=FipaSerializer().encode(cfp_msg),\n)\nself.context.outbox.put(envelope)\n

    Now this has been simplified to:

    cfp_msg = FipaMessage(...)\ncfp_msg.counterparty = opponent_addr\nself.context.outbox.put_message(message=cfp_msg)\n

    You must update your skills as the old implementation is no longer supported.

  • Connection constructors have been simplified. In the past you had to implement both the __init__ as well as the from_config methods of a Connection. Now you only have to implement the __init__ method which by default at load time now receives the following keyword arguments: configuration: ConnectionConfig, identity: Identity, crypto_store: CryptoStore. See for example in the scaffold connection:

    class MyScaffoldConnection(Connection):\n\"\"\"Proxy to the functionality of the SDK or API.\"\"\"\nconnection_id = PublicId.from_str(\"fetchai/scaffold:0.1.0\")\ndef __init__(\nself,\nconfiguration: ConnectionConfig,\nidentity: Identity,\ncrypto_store: CryptoStore,\n):\n\"\"\"\n        Initialize a connection to an SDK or API.\n        :param configuration: the connection configuration.\n        :param crypto_store: object to access the connection crypto objects.\n        :param identity: the identity object.\n        \"\"\"\nsuper().__init__(\nconfiguration=configuration, crypto_store=crypto_store, identity=identity\n)\n

    As a result of this feature, you are now able to pass key-pairs to your connections via the CryptoStore.

    You must update your connections as the old implementation is no longer supported.

"},{"location":"aea-framework-documentation/wealth/","title":"Generating Wealth","text":"

To fund an AEA for testing on a test-net you need to request some test tokens from a faucet.

First, make sure you have installed the crypto plugin of the target test-net. E.g. for Fetch.AI:

pip install aea-ledger-fetchai\n

And for Ethereum:

pip install aea-ledger-ethereum\n

Add a private key to the agent

aea generate-key fetchai\naea add-key fetchai fetchai_private_key.txt\n

or

aea generate-key ethereum\naea add-key ethereum ethereum_private_key.txt\n

Note

If you already have keys in your project, the commands prompt you to confirm whether to replace the existing keys.

"},{"location":"aea-framework-documentation/wealth/#using-a-faucet-website","title":"Using a Faucet Website","text":"

First, print the address:

aea get-address fetchai\n

or

aea get-address ethereum\n

This will print the address to the console. Copy the address into the clipboard and request test tokens from the faucet here for Fetch.ai or here for Ethereum. It will take a while for the tokens to become available.

Second, after some time, check the wealth associated with the address:

aea get-wealth fetchai\n

or

aea get-wealth ethereum\n
"},{"location":"aea-framework-documentation/wealth/#using-the-cli","title":"Using the CLI","text":"

Simply generate wealth via the CLI:

aea generate-wealth fetchai\n

or

aea generate-wealth ethereum\n

Note

This approach can be unreliable for non-fetchai test nets.

"},{"location":"aea-framework-documentation/weather-skills/","title":"Weather Skills","text":"

The AEA weather skills demonstrate an interaction between two AEAs.

  • The provider of weather data (the weather_station).
  • The buyer of weather data (the weather_client).
"},{"location":"aea-framework-documentation/weather-skills/#discussion","title":"Discussion","text":"

The scope of the specific demo is to demonstrate how to create a simple AEA with the usage of the AEA framework and a database. The weather_station AEA will read data from the database, that is populated with readings from a weather station, based on the requested dates and will deliver the data to the client upon payment. This demo does not utilize a smart contract. As a result, we interact with a ledger only to complete a transaction.

You can use this AEA as an example of how to read data from a database and advertise these to possible clients.

"},{"location":"aea-framework-documentation/weather-skills/#communication","title":"Communication","text":"

This diagram shows the communication between the various entities as data is successfully sold by the weather station AEA to the client.

    sequenceDiagram\n        participant Search\n        participant Client_AEA\n        participant Weather_AEA\n        participant Blockchain\n\n        activate Client_AEA\n        activate Search\n        activate Weather_AEA\n        activate Blockchain\n\n        Weather_AEA->>Search: register_service\n        Client_AEA->>Search: search\n        Search-->>Client_AEA: list_of_agents\n        Client_AEA->>Weather_AEA: call_for_proposal\n        Weather_AEA->>Client_AEA: propose\n        Client_AEA->>Weather_AEA: accept\n        Weather_AEA->>Client_AEA: match_accept\n        Client_AEA->>Blockchain: transfer_funds\n        Client_AEA->>Weather_AEA: send_transaction_hash\n        Weather_AEA->>Blockchain: check_transaction_status\n        Weather_AEA->>Client_AEA: send_data\n\n        deactivate Client_AEA\n        deactivate Search\n        deactivate Weather_AEA\n        deactivate Blockchain  
"},{"location":"aea-framework-documentation/weather-skills/#option-1-aea-manager-approach","title":"Option 1: AEA Manager Approach","text":"

Follow this approach when using the AEA Manager Desktop app. Otherwise, skip and follow the CLI approach below.

"},{"location":"aea-framework-documentation/weather-skills/#preparation-instructions","title":"Preparation Instructions","text":"

Install the AEA Manager.

"},{"location":"aea-framework-documentation/weather-skills/#demo-instructions","title":"Demo Instructions","text":"

The following steps assume you have launched the AEA Manager Desktop app.

  1. Add a new AEA called my_weather_station with public id fetchai/weather_station:0.32.5.

  2. Add another new AEA called my_weather_client with public id fetchai/weather_client:0.33.5.

  3. Copy the address from the my_weather_client into your clip board. Then go to the Dorado block explorer and request some test tokens via Get Funds.

  4. Run the my_weather_station AEA. Navigate to its logs and copy the multiaddress displayed.

  5. Navigate to the settings of the my_weather_client and under components > connection > fetchai/p2p_libp2p:0.22.0 update as follows (make sure to replace the placeholder with the multiaddress):

    {\n\"delegate_uri\": \"127.0.0.1:11001\",\n  \"entry_peers\": [\"REPLACE_WITH_MULTI_ADDRESS_HERE\"],\n  \"local_uri\": \"127.0.0.1:9001\",\n  \"log_file\": \"libp2p_node.log\",\n  \"public_uri\": \"127.0.0.1:9001\"\n}\n
  6. Run the my_weather_client.

In the AEA's logs, you should see the agent trading successfully.

"},{"location":"aea-framework-documentation/weather-skills/#option-2-cli-approach","title":"Option 2: CLI Approach","text":"

Follow this approach when using the aea CLI.

"},{"location":"aea-framework-documentation/weather-skills/#preparation-instructions_1","title":"Preparation Instructions","text":""},{"location":"aea-framework-documentation/weather-skills/#dependencies","title":"Dependencies","text":"

Follow the Preliminaries and Installation sections from the AEA quick start.

"},{"location":"aea-framework-documentation/weather-skills/#demo-instructions_1","title":"Demo Instructions","text":"

A demo to run the same scenario but with a true ledger transaction on Fetch.ai testnet or Ethereum ropsten network. This demo assumes the buyer trusts the seller AEA to send the data upon successful payment.

"},{"location":"aea-framework-documentation/weather-skills/#create-the-weather-station","title":"Create the Weather Station","text":"

First, fetch the AEA that will provide weather measurements:

aea fetch fetchai/weather_station:0.32.5 --alias my_weather_station\ncd my_weather_station\naea install\naea build\n
Alternatively, create from scratch:

The following steps create the weather station from scratch:

aea create my_weather_station\ncd my_weather_station\naea add connection fetchai/p2p_libp2p:0.27.5\naea add connection fetchai/soef:0.27.6\naea add connection fetchai/ledger:0.21.5\naea add skill fetchai/weather_station:0.27.6\naea config set --type dict agent.dependencies \\\n'{\n  \"aea-ledger-fetchai\": {\"version\": \"<2.0.0,>=1.0.0\"}\n}'\naea config set agent.default_connection fetchai/p2p_libp2p:0.27.5\naea config set --type dict agent.default_routing \\\n'{\n  \"fetchai/ledger_api:1.1.7\": \"fetchai/ledger:0.21.5\",\n  \"fetchai/oef_search:1.1.7\": \"fetchai/soef:0.27.6\"\n}'\naea install\naea build\n
"},{"location":"aea-framework-documentation/weather-skills/#create-the-weather-client","title":"Create the Weather Client","text":"

In another terminal, fetch the AEA that will query the weather station:

aea fetch fetchai/weather_client:0.33.5 --alias my_weather_client\ncd my_weather_client\naea install\naea build\n
Alternatively, create from scratch:

The following steps create the weather client from scratch:

aea create my_weather_client\ncd my_weather_client\naea add connection fetchai/p2p_libp2p:0.27.5\naea add connection fetchai/soef:0.27.6\naea add connection fetchai/ledger:0.21.5\naea add skill fetchai/weather_client:0.26.6\naea config set --type dict agent.dependencies \\\n'{\n  \"aea-ledger-fetchai\": {\"version\": \"<2.0.0,>=1.0.0\"}\n}'\naea config set agent.default_connection fetchai/p2p_libp2p:0.27.5\naea config set --type dict agent.default_routing \\\n'{\n  \"fetchai/ledger_api:1.1.7\": \"fetchai/ledger:0.21.5\",\n  \"fetchai/oef_search:1.1.7\": \"fetchai/soef:0.27.6\"\n}'\naea install\naea build\n
"},{"location":"aea-framework-documentation/weather-skills/#add-keys-for-the-weather-station-aea","title":"Add Keys for the Weather Station AEA","text":"

First, create the private key for the weather station AEA based on the network you want to transact. To generate and add a private-public key pair for Fetch.ai Dorado use:

aea generate-key fetchai\naea add-key fetchai fetchai_private_key.txt\n

Next, create a private key used to secure the AEA's communications:

aea generate-key fetchai fetchai_connection_private_key.txt\naea add-key fetchai fetchai_connection_private_key.txt --connection\n

Finally, certify the key for use by the connections that request that:

aea issue-certificates\n
"},{"location":"aea-framework-documentation/weather-skills/#add-keys-and-generate-wealth-for-the-weather-client-aea","title":"Add Keys and Generate Wealth for the Weather Client AEA","text":"

The weather client needs to have some wealth to purchase the service from the weather station.

First, create the private key for the weather client AEA based on the network you want to transact. To generate and add a private-public key pair for Fetch.ai Dorado use:

aea generate-key fetchai\naea add-key fetchai fetchai_private_key.txt\n

Then, create some wealth for your weather client based on the network you want to transact with. On the Fetch.ai Dorado network:

aea generate-wealth fetchai\n

Next, create a private key used to secure the AEA's communications:

aea generate-key fetchai fetchai_connection_private_key.txt\naea add-key fetchai fetchai_connection_private_key.txt --connection\n

Finally, certify the key for use by the connections that request that:

aea issue-certificates\n
"},{"location":"aea-framework-documentation/weather-skills/#run-the-aeas","title":"Run the AEAs","text":"

Run both AEAs from their respective terminals.

First, run the weather station AEA:

aea run\n

Once you see a message of the form To join its network use multiaddr 'SOME_ADDRESS' take note of the address. (Alternatively, use aea get-multiaddress fetchai -c -i fetchai/p2p_libp2p:0.27.5 -u public_uri to retrieve the address.) This is the entry peer address for the local agent communication network created by the weather station.

Then, in the weather client, run this command (replace SOME_ADDRESS with the correct value as described above):

aea config set --type dict vendor.fetchai.connections.p2p_libp2p.config \\\n'{\n  \"delegate_uri\": \"127.0.0.1:11001\",\n  \"entry_peers\": [\"SOME_ADDRESS\"],\n  \"local_uri\": \"127.0.0.1:9001\",\n  \"log_file\": \"libp2p_node.log\",\n  \"public_uri\": \"127.0.0.1:9001\"\n}'\n

This allows the weather client to connect to the same local agent communication network as the weather station.

Then run the weather client AEA:

aea run\n

You will see that the AEAs negotiate and then transact using the selected ledger.

"},{"location":"aea-framework-documentation/weather-skills/#cleaning-up","title":"Cleaning up","text":"

When you're done, go up a level and delete the AEAs.

cd ..\naea delete my_weather_station\naea delete my_weather_client\n
"},{"location":"aea-framework-documentation/api/abstract_agent/","title":"AbstractAgent","text":""},{"location":"aea-framework-documentation/api/abstract_agent/#aeaabstract_agent","title":"aea.abstract_agent","text":"

This module contains the interface definition of the abstract agent.

"},{"location":"aea-framework-documentation/api/abstract_agent/#abstractagent-objects","title":"AbstractAgent Objects","text":"
class AbstractAgent(ABC)\n

This class provides an abstract base interface for an agent.

"},{"location":"aea-framework-documentation/api/abstract_agent/#name","title":"name","text":"
@property\n@abstractmethod\ndef name() -> str\n

Get agent's name.

"},{"location":"aea-framework-documentation/api/abstract_agent/#storage_uri","title":"storage_uri","text":"
@property\n@abstractmethod\ndef storage_uri() -> Optional[str]\n

Return storage uri.

"},{"location":"aea-framework-documentation/api/abstract_agent/#start","title":"start","text":"
@abstractmethod\ndef start() -> None\n

Start the agent.

Returns:

None

"},{"location":"aea-framework-documentation/api/abstract_agent/#stop","title":"stop","text":"
@abstractmethod\ndef stop() -> None\n

Stop the agent.

Returns:

None

"},{"location":"aea-framework-documentation/api/abstract_agent/#setup","title":"setup","text":"
@abstractmethod\ndef setup() -> None\n

Set up the agent.

Returns:

None

"},{"location":"aea-framework-documentation/api/abstract_agent/#act","title":"act","text":"
@abstractmethod\ndef act() -> None\n

Perform actions on period.

Returns:

None

"},{"location":"aea-framework-documentation/api/abstract_agent/#handle_envelope","title":"handle_envelope","text":"
@abstractmethod\ndef handle_envelope(envelope: Envelope) -> None\n

Handle an envelope.

Arguments:

  • envelope: the envelope to handle.

Returns:

None

"},{"location":"aea-framework-documentation/api/abstract_agent/#get_periodic_tasks","title":"get_periodic_tasks","text":"
@abstractmethod\ndef get_periodic_tasks(\n) -> Dict[Callable, Tuple[float, Optional[datetime.datetime]]]\n

Get all periodic tasks for agent.

Returns:

dict of callable with period specified

"},{"location":"aea-framework-documentation/api/abstract_agent/#get_message_handlers","title":"get_message_handlers","text":"
@abstractmethod\ndef get_message_handlers() -> List[Tuple[Callable[[Any], None], Callable]]\n

Get handlers with message getters.

Returns:

List of tuples of callables: handler and coroutine to get a message

"},{"location":"aea-framework-documentation/api/abstract_agent/#exception_handler","title":"exception_handler","text":"
@abstractmethod\ndef exception_handler(exception: Exception,\nfunction: Callable) -> Optional[bool]\n

Handle exception raised during agent main loop execution.

Arguments:

  • exception: exception raised
  • function: a callable exception raised in.

Returns:

skip exception if True, otherwise re-raise it

"},{"location":"aea-framework-documentation/api/abstract_agent/#teardown","title":"teardown","text":"
@abstractmethod\ndef teardown() -> None\n

Tear down the agent.

Returns:

None

"},{"location":"aea-framework-documentation/api/aea/","title":"AEA","text":""},{"location":"aea-framework-documentation/api/aea/#aeaaea","title":"aea.aea","text":"

This module contains the implementation of an autonomous economic agent (AEA).

"},{"location":"aea-framework-documentation/api/aea/#aea-objects","title":"AEA Objects","text":"
class AEA(Agent)\n

This class implements an autonomous economic agent.

"},{"location":"aea-framework-documentation/api/aea/#__init__","title":"__init__","text":"
def __init__(\nidentity: Identity,\nwallet: Wallet,\nresources: Resources,\ndata_dir: str,\nloop: Optional[AbstractEventLoop] = None,\nperiod: float = 0.05,\nexecution_timeout: float = 0,\nmax_reactions: int = 20,\nerror_handler_class: Optional[Type[AbstractErrorHandler]] = None,\nerror_handler_config: Optional[Dict[str, Any]] = None,\ndecision_maker_handler_class: Optional[\nType[DecisionMakerHandler]] = None,\ndecision_maker_handler_config: Optional[Dict[str, Any]] = None,\nskill_exception_policy: ExceptionPolicyEnum = ExceptionPolicyEnum.\npropagate,\nconnection_exception_policy: ExceptionPolicyEnum = ExceptionPolicyEnum.\npropagate,\nloop_mode: Optional[str] = None,\nruntime_mode: Optional[str] = None,\ndefault_ledger: Optional[str] = None,\ncurrency_denominations: Optional[Dict[str, str]] = None,\ndefault_connection: Optional[PublicId] = None,\ndefault_routing: Optional[Dict[PublicId, PublicId]] = None,\nconnection_ids: Optional[Collection[PublicId]] = None,\nsearch_service_address: str = DEFAULT_SEARCH_SERVICE_ADDRESS,\nstorage_uri: Optional[str] = None,\ntask_manager_mode: Optional[str] = None,\n**kwargs: Any) -> None\n

Instantiate the agent.

Arguments:

  • identity: the identity of the agent
  • wallet: the wallet of the agent.
  • resources: the resources (protocols and skills) of the agent.
  • data_dir: directory where to put local files.
  • loop: the event loop to run the connections.
  • period: period to call agent's act
  • execution_timeout: amount of time to limit single act/handle to execute.
  • max_reactions: the processing rate of envelopes per tick (i.e. single loop).
  • error_handler_class: the class implementing the error handler
  • error_handler_config: the configuration of the error handler
  • decision_maker_handler_class: the class implementing the decision maker handler to be used.
  • decision_maker_handler_config: the configuration of the decision maker handler
  • skill_exception_policy: the skill exception policy enum
  • connection_exception_policy: the connection exception policy enum
  • loop_mode: loop_mode to choose agent run loop.
  • runtime_mode: runtime mode (async, threaded) to run AEA in.
  • default_ledger: default ledger id
  • currency_denominations: mapping from ledger id to currency denomination
  • default_connection: public id to the default connection
  • default_routing: dictionary for default routing.
  • connection_ids: active connection ids. Default: consider all the ones in the resources.
  • search_service_address: the address of the search service used.
  • storage_uri: optional uri to set generic storage
  • task_manager_mode: task manager mode (threaded) to run tasks with.
  • kwargs: keyword arguments to be attached in the agent context namespace.

"},{"location":"aea-framework-documentation/api/aea/#get_build_dir","title":"get_build_dir","text":"
@classmethod\ndef get_build_dir(cls) -> str\n

Get agent build directory.

"},{"location":"aea-framework-documentation/api/aea/#context","title":"context","text":"
@property\ndef context() -> AgentContext\n

Get (agent) context.

"},{"location":"aea-framework-documentation/api/aea/#resources","title":"resources","text":"
@property\ndef resources() -> Resources\n

Get resources.

"},{"location":"aea-framework-documentation/api/aea/#resources_1","title":"resources","text":"
@resources.setter\ndef resources(resources: \"Resources\") -> None\n

Set resources.

"},{"location":"aea-framework-documentation/api/aea/#filter","title":"filter","text":"
@property\ndef filter() -> Filter\n

Get the filter.

"},{"location":"aea-framework-documentation/api/aea/#active_behaviours","title":"active_behaviours","text":"
@property\ndef active_behaviours() -> List[Behaviour]\n

Get all active behaviours to use in act.

"},{"location":"aea-framework-documentation/api/aea/#setup","title":"setup","text":"
def setup() -> None\n

Set up the agent.

Calls setup() on the resources.

"},{"location":"aea-framework-documentation/api/aea/#act","title":"act","text":"
def act() -> None\n

Perform actions.

Adds new handlers and behaviours for use/execution by the runtime.

"},{"location":"aea-framework-documentation/api/aea/#handle_envelope","title":"handle_envelope","text":"
def handle_envelope(envelope: Envelope) -> None\n

Handle an envelope.

Performs the following:

  • fetching the protocol referenced by the envelope, and
  • handling if the protocol is unsupported, using the error handler, or
  • handling if there is a decoding error, using the error handler, or
  • handling if no active handler is available for the specified protocol, using the error handler, or
  • handling the message recovered from the envelope with all active handlers for the specified protocol.

Arguments:

  • envelope: the envelope to handle.

Returns:

None

"},{"location":"aea-framework-documentation/api/aea/#get_periodic_tasks","title":"get_periodic_tasks","text":"
def get_periodic_tasks(\n) -> Dict[Callable, Tuple[float, Optional[datetime.datetime]]]\n

Get all periodic tasks for agent.

Returns:

dict of callable with period specified

"},{"location":"aea-framework-documentation/api/aea/#get_message_handlers","title":"get_message_handlers","text":"
def get_message_handlers() -> List[Tuple[Callable[[Any], None], Callable]]\n

Get handlers with message getters.

Returns:

List of tuples of callables: handler and coroutine to get a message

"},{"location":"aea-framework-documentation/api/aea/#exception_handler","title":"exception_handler","text":"
def exception_handler(exception: Exception, function: Callable) -> bool\n

Handle exception raised during agent main loop execution.

Arguments:

  • exception: exception raised
  • function: a callable exception raised in.

Returns:

bool, propagate exception if True otherwise skip it.

"},{"location":"aea-framework-documentation/api/aea/#teardown","title":"teardown","text":"
def teardown() -> None\n

Tear down the agent.

Performs the following:

  • tears down the resources.

"},{"location":"aea-framework-documentation/api/aea/#get_task_result","title":"get_task_result","text":"
def get_task_result(task_id: int) -> AsyncResult\n

Get the result from a task.

Arguments:

  • task_id: the id of the task

Returns:

async result for task_id

"},{"location":"aea-framework-documentation/api/aea/#enqueue_task","title":"enqueue_task","text":"
def enqueue_task(func: Callable,\nargs: Sequence = (),\nkwargs: Optional[Dict[str, Any]] = None) -> int\n

Enqueue a task with the task manager.

Arguments:

  • func: the callable instance to be enqueued
  • args: the positional arguments to be passed to the function.
  • kwargs: the keyword arguments to be passed to the function.

Returns:

the task id to get the the result.

"},{"location":"aea-framework-documentation/api/aea_builder/","title":"AEA Builder","text":""},{"location":"aea-framework-documentation/api/aea_builder/#aeaaea_builder","title":"aea.aea_builder","text":"

This module contains utilities for building an AEA.

"},{"location":"aea-framework-documentation/api/aea_builder/#_dependenciesmanager-objects","title":"_DependenciesManager Objects","text":"
class _DependenciesManager()\n

Class to manage dependencies of agent packages.

"},{"location":"aea-framework-documentation/api/aea_builder/#__init__","title":"__init__","text":"
def __init__() -> None\n

Initialize the dependency graph.

"},{"location":"aea-framework-documentation/api/aea_builder/#all_dependencies","title":"all_dependencies","text":"
@property\ndef all_dependencies() -> Set[ComponentId]\n

Get all dependencies.

"},{"location":"aea-framework-documentation/api/aea_builder/#dependencies_highest_version","title":"dependencies_highest_version","text":"
@property\ndef dependencies_highest_version() -> Set[ComponentId]\n

Get the dependencies with highest version.

"},{"location":"aea-framework-documentation/api/aea_builder/#get_components_by_type","title":"get_components_by_type","text":"
def get_components_by_type(\ncomponent_type: ComponentType\n) -> Dict[ComponentId, ComponentConfiguration]\n

Get the components by type.

"},{"location":"aea-framework-documentation/api/aea_builder/#protocols","title":"protocols","text":"
@property\ndef protocols() -> Dict[ComponentId, ProtocolConfig]\n

Get the protocols.

"},{"location":"aea-framework-documentation/api/aea_builder/#connections","title":"connections","text":"
@property\ndef connections() -> Dict[ComponentId, ConnectionConfig]\n

Get the connections.

"},{"location":"aea-framework-documentation/api/aea_builder/#skills","title":"skills","text":"
@property\ndef skills() -> Dict[ComponentId, SkillConfig]\n

Get the skills.

"},{"location":"aea-framework-documentation/api/aea_builder/#contracts","title":"contracts","text":"
@property\ndef contracts() -> Dict[ComponentId, ContractConfig]\n

Get the contracts.

"},{"location":"aea-framework-documentation/api/aea_builder/#add_component","title":"add_component","text":"
def add_component(configuration: ComponentConfiguration) -> None\n

Add a component to the dependency manager.

Arguments:

  • configuration: the component configuration to add.

"},{"location":"aea-framework-documentation/api/aea_builder/#remove_component","title":"remove_component","text":"
def remove_component(component_id: ComponentId) -> None\n

Remove a component.

Arguments:

  • component_id: the component id

Raises:

  • ValueError: if some component depends on this package.

"},{"location":"aea-framework-documentation/api/aea_builder/#pypi_dependencies","title":"pypi_dependencies","text":"
@property\ndef pypi_dependencies() -> Dependencies\n

Get all the PyPI dependencies.

We currently consider only dependency that have the default PyPI index url and that specify only the version field.

Returns:

the merged PyPI dependencies

"},{"location":"aea-framework-documentation/api/aea_builder/#install_dependencies","title":"install_dependencies","text":"
def install_dependencies() -> None\n

Install extra dependencies for components.

"},{"location":"aea-framework-documentation/api/aea_builder/#aeabuilder-objects","title":"AEABuilder Objects","text":"
class AEABuilder(WithLogger)\n

This class helps to build an AEA.

It follows the fluent interface. Every method of the builder returns the instance of the builder itself.

Note: the method 'build()' is guaranteed of being re-entrant with respect to the 'add_component(path)' method. That is, you can invoke the building method many times against the same builder instance, and the returned agent instance will not share the components with other agents, e.g.:

builder = AEABuilder() builder.add_component(...) ...

"},{"location":"aea-framework-documentation/api/aea_builder/#first-call","title":"first call","text":"

my_aea_1 = builder.build()

"},{"location":"aea-framework-documentation/api/aea_builder/#following-agents-will-have-different-components","title":"following agents will have different components.","text":"

my_aea_2 = builder.build() # all good

However, if you manually loaded some of the components and added them with the method 'add_component_instance()', then calling build more than one time is prevented:

builder = AEABuilder() builder.add_component_instance(...) ... # other initialization code

"},{"location":"aea-framework-documentation/api/aea_builder/#first-call_1","title":"first call","text":"

my_aea_1 = builder.build()

"},{"location":"aea-framework-documentation/api/aea_builder/#second-call-to-build-would-raise-a-value-error","title":"second call to build() would raise a Value Error.","text":""},{"location":"aea-framework-documentation/api/aea_builder/#call-reset","title":"call reset","text":"

builder.reset()

"},{"location":"aea-framework-documentation/api/aea_builder/#re-add-the-component-and-private-keys","title":"re-add the component and private keys","text":"

builder.add_component_instance(...) ... # add private keys

"},{"location":"aea-framework-documentation/api/aea_builder/#second-call","title":"second call","text":"

my_aea_2 = builder.builder()

"},{"location":"aea-framework-documentation/api/aea_builder/#__init___1","title":"__init__","text":"
def __init__(with_default_packages: bool = True,\nregistry_dir: str = DEFAULT_REGISTRY_NAME,\nbuild_dir_root: Optional[str] = None) -> None\n

Initialize the builder.

Arguments:

  • with_default_packages: add the default packages.
  • registry_dir: the registry directory.
  • build_dir_root: the root of the build directory.

"},{"location":"aea-framework-documentation/api/aea_builder/#reset","title":"reset","text":"
def reset(is_full_reset: bool = False) -> None\n

Reset the builder.

A full reset causes a reset of all data on the builder. A partial reset only resets: - name, - private keys, and - component instances

Arguments:

  • is_full_reset: whether it is a full reset or not.

"},{"location":"aea-framework-documentation/api/aea_builder/#set_period","title":"set_period","text":"
def set_period(period: Optional[float]) -> \"AEABuilder\"\n

Set agent act period.

Arguments:

  • period: period in seconds

Returns:

self

"},{"location":"aea-framework-documentation/api/aea_builder/#set_execution_timeout","title":"set_execution_timeout","text":"
def set_execution_timeout(execution_timeout: Optional[float]) -> \"AEABuilder\"\n

Set agent execution timeout in seconds.

Arguments:

  • execution_timeout: execution_timeout in seconds

Returns:

self

"},{"location":"aea-framework-documentation/api/aea_builder/#set_max_reactions","title":"set_max_reactions","text":"
def set_max_reactions(max_reactions: Optional[int]) -> \"AEABuilder\"\n

Set agent max reaction in one react.

Arguments:

  • max_reactions: int

Returns:

self

"},{"location":"aea-framework-documentation/api/aea_builder/#set_decision_maker_handler_details","title":"set_decision_maker_handler_details","text":"
def set_decision_maker_handler_details(decision_maker_handler_dotted_path: str,\nfile_path: str,\nconfig: Dict[str, Any]) -> \"AEABuilder\"\n

Set error handler details.

Arguments:

  • decision_maker_handler_dotted_path: the dotted path to the decision maker handler
  • file_path: the file path to the file which contains the decision maker handler
  • config: the configuration passed to the decision maker handler on instantiation

Returns:

self

"},{"location":"aea-framework-documentation/api/aea_builder/#set_error_handler_details","title":"set_error_handler_details","text":"
def set_error_handler_details(error_handler_dotted_path: str, file_path: str,\nconfig: Dict[str, Any]) -> \"AEABuilder\"\n

Set error handler details.

Arguments:

  • error_handler_dotted_path: the dotted path to the error handler
  • file_path: the file path to the file which contains the error handler
  • config: the configuration passed to the error handler on instantiation

Returns:

self

"},{"location":"aea-framework-documentation/api/aea_builder/#set_skill_exception_policy","title":"set_skill_exception_policy","text":"
def set_skill_exception_policy(\nskill_exception_policy: Optional[ExceptionPolicyEnum]) -> \"AEABuilder\"\n

Set skill exception policy.

Arguments:

  • skill_exception_policy: the policy

Returns:

self

"},{"location":"aea-framework-documentation/api/aea_builder/#set_connection_exception_policy","title":"set_connection_exception_policy","text":"
def set_connection_exception_policy(\nconnection_exception_policy: Optional[ExceptionPolicyEnum]\n) -> \"AEABuilder\"\n

Set connection exception policy.

Arguments:

  • connection_exception_policy: the policy

Returns:

self

"},{"location":"aea-framework-documentation/api/aea_builder/#set_default_routing","title":"set_default_routing","text":"
def set_default_routing(\ndefault_routing: Dict[PublicId, PublicId]) -> \"AEABuilder\"\n

Set default routing.

This is a map from public ids (protocols) to public ids (connections).

Arguments:

  • default_routing: the default routing mapping

Returns:

self

"},{"location":"aea-framework-documentation/api/aea_builder/#set_loop_mode","title":"set_loop_mode","text":"
def set_loop_mode(loop_mode: Optional[str]) -> \"AEABuilder\"\n

Set the loop mode.

Arguments:

  • loop_mode: the agent loop mode

Returns:

self

"},{"location":"aea-framework-documentation/api/aea_builder/#set_runtime_mode","title":"set_runtime_mode","text":"
def set_runtime_mode(runtime_mode: Optional[str]) -> \"AEABuilder\"\n

Set the runtime mode.

Arguments:

  • runtime_mode: the agent runtime mode

Returns:

self

"},{"location":"aea-framework-documentation/api/aea_builder/#set_task_manager_mode","title":"set_task_manager_mode","text":"
def set_task_manager_mode(task_manager_mode: Optional[str]) -> \"AEABuilder\"\n

Set the task_manager_mode.

Arguments:

  • task_manager_mode: the agent task_manager_mode

Returns:

self

"},{"location":"aea-framework-documentation/api/aea_builder/#set_storage_uri","title":"set_storage_uri","text":"
def set_storage_uri(storage_uri: Optional[str]) -> \"AEABuilder\"\n

Set the storage uri.

Arguments:

  • storage_uri: storage uri

Returns:

self

"},{"location":"aea-framework-documentation/api/aea_builder/#set_data_dir","title":"set_data_dir","text":"
def set_data_dir(data_dir: Optional[str]) -> \"AEABuilder\"\n

Set the data directory.

Arguments:

  • data_dir: path to directory where to store data.

Returns:

self

"},{"location":"aea-framework-documentation/api/aea_builder/#set_logging_config","title":"set_logging_config","text":"
def set_logging_config(logging_config: Dict) -> \"AEABuilder\"\n

Set the logging configurations.

The dictionary must satisfy the following schema:

https://docs.python.org/3/library/logging.config.html#logging-config-dictschema

Arguments:

  • logging_config: the logging configurations.

Returns:

self

"},{"location":"aea-framework-documentation/api/aea_builder/#set_search_service_address","title":"set_search_service_address","text":"
def set_search_service_address(search_service_address: str) -> \"AEABuilder\"\n

Set the search service address.

Arguments:

  • search_service_address: the search service address

Returns:

self

"},{"location":"aea-framework-documentation/api/aea_builder/#set_name","title":"set_name","text":"
def set_name(name: str) -> \"AEABuilder\"\n

Set the name of the agent.

Arguments:

  • name: the name of the agent.

Returns:

the AEABuilder

"},{"location":"aea-framework-documentation/api/aea_builder/#set_default_connection","title":"set_default_connection","text":"
def set_default_connection(\npublic_id: Optional[PublicId] = None) -> \"AEABuilder\"\n

Set the default connection.

Arguments:

  • public_id: the public id of the default connection package.

Returns:

the AEABuilder

"},{"location":"aea-framework-documentation/api/aea_builder/#add_private_key","title":"add_private_key","text":"
def add_private_key(identifier: str,\nprivate_key_path: Optional[PathLike] = None,\nis_connection: bool = False) -> \"AEABuilder\"\n

Add a private key path.

Arguments:

  • identifier: the identifier for that private key path.
  • private_key_path: an (optional) path to the private key file. If None, the key will be created at build time.
  • is_connection: if the pair is for the connection cryptos

Returns:

the AEABuilder

"},{"location":"aea-framework-documentation/api/aea_builder/#remove_private_key","title":"remove_private_key","text":"
def remove_private_key(identifier: str,\nis_connection: bool = False) -> \"AEABuilder\"\n

Remove a private key path by identifier, if present.

Arguments:

  • identifier: the identifier of the private key.
  • is_connection: if the pair is for the connection cryptos

Returns:

the AEABuilder

"},{"location":"aea-framework-documentation/api/aea_builder/#private_key_paths","title":"private_key_paths","text":"
@property\ndef private_key_paths() -> Dict[str, Optional[str]]\n

Get the private key paths.

"},{"location":"aea-framework-documentation/api/aea_builder/#connection_private_key_paths","title":"connection_private_key_paths","text":"
@property\ndef connection_private_key_paths() -> Dict[str, Optional[str]]\n

Get the connection private key paths.

"},{"location":"aea-framework-documentation/api/aea_builder/#set_default_ledger","title":"set_default_ledger","text":"
def set_default_ledger(identifier: Optional[str]) -> \"AEABuilder\"\n

Set a default ledger API to use.

Arguments:

  • identifier: the identifier of the ledger api

Returns:

the AEABuilder

"},{"location":"aea-framework-documentation/api/aea_builder/#set_required_ledgers","title":"set_required_ledgers","text":"
def set_required_ledgers(\nrequired_ledgers: Optional[List[str]]) -> \"AEABuilder\"\n

Set the required ledger identifiers.

These are the ledgers for which the AEA requires a key pair.

Arguments:

  • required_ledgers: the required ledgers.

Returns:

the AEABuilder.

"},{"location":"aea-framework-documentation/api/aea_builder/#set_build_entrypoint","title":"set_build_entrypoint","text":"
def set_build_entrypoint(build_entrypoint: Optional[str]) -> \"AEABuilder\"\n

Set build entrypoint.

Arguments:

  • build_entrypoint: path to the builder script.

Returns:

the AEABuilder

"},{"location":"aea-framework-documentation/api/aea_builder/#set_currency_denominations","title":"set_currency_denominations","text":"
def set_currency_denominations(\ncurrency_denominations: Dict[str, str]) -> \"AEABuilder\"\n

Set the mapping from ledger ids to currency denominations.

Arguments:

  • currency_denominations: the mapping

Returns:

the AEABuilder

"},{"location":"aea-framework-documentation/api/aea_builder/#add_component_1","title":"add_component","text":"
def add_component(component_type: ComponentType,\ndirectory: PathLike,\nskip_consistency_check: bool = False) -> \"AEABuilder\"\n

Add a component, given its type and the directory.

Arguments:

  • component_type: the component type.
  • directory: the directory path.
  • skip_consistency_check: if True, the consistency check are skipped.

Raises:

  • AEAException: if a component is already registered with the same component id. # noqa: DAR402 | or if there's a missing dependency. # noqa: DAR402

Returns:

the AEABuilder

"},{"location":"aea-framework-documentation/api/aea_builder/#add_component_instance","title":"add_component_instance","text":"
def add_component_instance(component: Component) -> \"AEABuilder\"\n

Add already initialized component object to resources or connections.

Please, pay attention, all dependencies have to be already loaded.

Notice also that this will make the call to 'build()' non re-entrant. You will have to reset() the builder before calling build() again.

Arguments:

  • component: Component instance already initialized.

Returns:

self

"},{"location":"aea-framework-documentation/api/aea_builder/#set_context_namespace","title":"set_context_namespace","text":"
def set_context_namespace(context_namespace: Dict[str, Any]) -> \"AEABuilder\"\n

Set the context namespace.

"},{"location":"aea-framework-documentation/api/aea_builder/#set_agent_pypi_dependencies","title":"set_agent_pypi_dependencies","text":"
def set_agent_pypi_dependencies(dependencies: Dependencies) -> \"AEABuilder\"\n

Set agent PyPI dependencies.

Arguments:

  • dependencies: PyPI dependencies for the agent.

Returns:

the AEABuilder.

"},{"location":"aea-framework-documentation/api/aea_builder/#remove_component_1","title":"remove_component","text":"
def remove_component(component_id: ComponentId) -> \"AEABuilder\"\n

Remove a component.

Arguments:

  • component_id: the public id of the component.

Returns:

the AEABuilder

"},{"location":"aea-framework-documentation/api/aea_builder/#add_protocol","title":"add_protocol","text":"
def add_protocol(directory: PathLike) -> \"AEABuilder\"\n

Add a protocol to the agent.

Arguments:

  • directory: the path to the protocol directory

Returns:

the AEABuilder

"},{"location":"aea-framework-documentation/api/aea_builder/#remove_protocol","title":"remove_protocol","text":"
def remove_protocol(public_id: PublicId) -> \"AEABuilder\"\n

Remove protocol.

Arguments:

  • public_id: the public id of the protocol

Returns:

the AEABuilder

"},{"location":"aea-framework-documentation/api/aea_builder/#add_connection","title":"add_connection","text":"
def add_connection(directory: PathLike) -> \"AEABuilder\"\n

Add a connection to the agent.

Arguments:

  • directory: the path to the connection directory

Returns:

the AEABuilder

"},{"location":"aea-framework-documentation/api/aea_builder/#remove_connection","title":"remove_connection","text":"
def remove_connection(public_id: PublicId) -> \"AEABuilder\"\n

Remove a connection.

Arguments:

  • public_id: the public id of the connection

Returns:

the AEABuilder

"},{"location":"aea-framework-documentation/api/aea_builder/#add_skill","title":"add_skill","text":"
def add_skill(directory: PathLike) -> \"AEABuilder\"\n

Add a skill to the agent.

Arguments:

  • directory: the path to the skill directory

Returns:

the AEABuilder

"},{"location":"aea-framework-documentation/api/aea_builder/#remove_skill","title":"remove_skill","text":"
def remove_skill(public_id: PublicId) -> \"AEABuilder\"\n

Remove protocol.

Arguments:

  • public_id: the public id of the skill

Returns:

the AEABuilder

"},{"location":"aea-framework-documentation/api/aea_builder/#add_contract","title":"add_contract","text":"
def add_contract(directory: PathLike) -> \"AEABuilder\"\n

Add a contract to the agent.

Arguments:

  • directory: the path to the contract directory

Returns:

the AEABuilder

"},{"location":"aea-framework-documentation/api/aea_builder/#remove_contract","title":"remove_contract","text":"
def remove_contract(public_id: PublicId) -> \"AEABuilder\"\n

Remove protocol.

Arguments:

  • public_id: the public id of the contract

Returns:

the AEABuilder

"},{"location":"aea-framework-documentation/api/aea_builder/#call_all_build_entrypoints","title":"call_all_build_entrypoints","text":"
def call_all_build_entrypoints() -> None\n

Call all the build entrypoints.

"},{"location":"aea-framework-documentation/api/aea_builder/#get_build_root_directory","title":"get_build_root_directory","text":"
def get_build_root_directory() -> str\n

Get build directory root.

"},{"location":"aea-framework-documentation/api/aea_builder/#run_build_for_component_configuration","title":"run_build_for_component_configuration","text":"
@classmethod\ndef run_build_for_component_configuration(\ncls,\nconfig: ComponentConfiguration,\nlogger: Optional[logging.Logger] = None) -> None\n

Run a build entrypoint script for component configuration.

"},{"location":"aea-framework-documentation/api/aea_builder/#install_pypi_dependencies","title":"install_pypi_dependencies","text":"
def install_pypi_dependencies() -> None\n

Install components extra dependencies.

"},{"location":"aea-framework-documentation/api/aea_builder/#build","title":"build","text":"
def build(connection_ids: Optional[Collection[PublicId]] = None,\npassword: Optional[str] = None) -> AEA\n

Build the AEA.

This method is re-entrant only if the components have been added through the method 'add_component'. If some of them have been loaded with 'add_component_instance', it can be called only once, and further calls are only possible after a call to 'reset' and re-loading of the components added via 'add_component_instance' and the private keys.

Arguments:

  • connection_ids: select only these connections to run the AEA.
  • password: the password to encrypt/decrypt the private key.

Returns:

the AEA object.

"},{"location":"aea-framework-documentation/api/aea_builder/#get_default_ledger","title":"get_default_ledger","text":"
def get_default_ledger() -> str\n

Return default ledger.

Returns:

the default ledger identifier.

"},{"location":"aea-framework-documentation/api/aea_builder/#get_required_ledgers","title":"get_required_ledgers","text":"
def get_required_ledgers() -> List[str]\n

Get the required ledger identifiers.

These are the ledgers for which the AEA requires a key pair.

Returns:

the list of required ledgers.

"},{"location":"aea-framework-documentation/api/aea_builder/#try_to_load_agent_configuration_file","title":"try_to_load_agent_configuration_file","text":"
@classmethod\ndef try_to_load_agent_configuration_file(\ncls, aea_project_path: Union[str, Path]) -> AgentConfig\n

Try to load the agent configuration file..

"},{"location":"aea-framework-documentation/api/aea_builder/#set_from_configuration","title":"set_from_configuration","text":"
def set_from_configuration(agent_configuration: AgentConfig,\naea_project_path: Path,\nskip_consistency_check: bool = False) -> None\n

Set builder variables from AgentConfig.

Arguments:

  • agent_configuration: AgentConfig to get values from.
  • aea_project_path: PathLike root directory of the agent project.
  • skip_consistency_check: if True, the consistency check are skipped.

"},{"location":"aea-framework-documentation/api/aea_builder/#from_aea_project","title":"from_aea_project","text":"
@classmethod\ndef from_aea_project(cls,\naea_project_path: PathLike,\nskip_consistency_check: bool = False,\npassword: Optional[str] = None) -> \"AEABuilder\"\n

Construct the builder from an AEA project.

  • load agent configuration file
  • set name and default configurations
  • load private keys
  • load ledger API configurations
  • set default ledger
  • load every component

Arguments:

  • aea_project_path: path to the AEA project.
  • skip_consistency_check: if True, the consistency check are skipped.
  • password: the password to encrypt/decrypt private keys.

Returns:

an AEABuilder.

"},{"location":"aea-framework-documentation/api/aea_builder/#get_configuration_file_path","title":"get_configuration_file_path","text":"
@staticmethod\ndef get_configuration_file_path(aea_project_path: Union[Path, str]) -> Path\n

Return path to aea-config file for the given aea project path.

"},{"location":"aea-framework-documentation/api/aea_builder/#make_component_logger","title":"make_component_logger","text":"
def make_component_logger(configuration: ComponentConfiguration,\nagent_name: str) -> Optional[logging.Logger]\n

Make the logger for a component.

Arguments:

  • configuration: the component configuration
  • agent_name: the agent name

Returns:

the logger.

"},{"location":"aea-framework-documentation/api/agent/","title":"Agent","text":""},{"location":"aea-framework-documentation/api/agent/#aeaagent","title":"aea.agent","text":"

This module contains the implementation of a generic agent.

"},{"location":"aea-framework-documentation/api/agent/#agent-objects","title":"Agent Objects","text":"
class Agent(AbstractAgent, WithLogger)\n

This class provides an abstract base class for a generic agent.

"},{"location":"aea-framework-documentation/api/agent/#__init__","title":"__init__","text":"
def __init__(identity: Identity,\nconnections: List[Connection],\nloop: Optional[AbstractEventLoop] = None,\nperiod: float = 1.0,\nloop_mode: Optional[str] = None,\nruntime_mode: Optional[str] = None,\nstorage_uri: Optional[str] = None,\nlogger: Logger = _default_logger,\ntask_manager_mode: Optional[str] = None) -> None\n

Instantiate the agent.

Arguments:

  • identity: the identity of the agent.
  • connections: the list of connections of the agent.
  • loop: the event loop to run the connections.
  • period: period to call agent's act
  • loop_mode: loop_mode to choose agent run loop.
  • runtime_mode: runtime mode to up agent.
  • storage_uri: optional uri to set generic storage
  • task_manager_mode: task manager mode.
  • logger: the logger.
  • task_manager_mode: mode of the task manager.

"},{"location":"aea-framework-documentation/api/agent/#storage_uri","title":"storage_uri","text":"
@property\ndef storage_uri() -> Optional[str]\n

Return storage uri.

"},{"location":"aea-framework-documentation/api/agent/#is_running","title":"is_running","text":"
@property\ndef is_running() -> bool\n

Get running state of the runtime and agent.

"},{"location":"aea-framework-documentation/api/agent/#is_stopped","title":"is_stopped","text":"
@property\ndef is_stopped() -> bool\n

Get running state of the runtime and agent.

"},{"location":"aea-framework-documentation/api/agent/#identity","title":"identity","text":"
@property\ndef identity() -> Identity\n

Get the identity.

"},{"location":"aea-framework-documentation/api/agent/#inbox","title":"inbox","text":"
@property\ndef inbox() -> InBox\n

Get the inbox.

The inbox contains Envelopes from the Multiplexer. The agent can pick these messages for processing.

Returns:

InBox instance

"},{"location":"aea-framework-documentation/api/agent/#outbox","title":"outbox","text":"
@property\ndef outbox() -> OutBox\n

Get the outbox.

The outbox contains Envelopes for the Multiplexer. Envelopes placed in the Outbox are processed by the Multiplexer.

Returns:

OutBox instance

"},{"location":"aea-framework-documentation/api/agent/#name","title":"name","text":"
@property\ndef name() -> str\n

Get the agent name.

"},{"location":"aea-framework-documentation/api/agent/#tick","title":"tick","text":"
@property\ndef tick() -> int\n

Get the tick or agent loop count.

Each agent loop (one call to each one of act(), react(), update()) increments the tick.

Returns:

tick count

"},{"location":"aea-framework-documentation/api/agent/#state","title":"state","text":"
@property\ndef state() -> RuntimeStates\n

Get state of the agent's runtime.

Returns:

RuntimeStates

"},{"location":"aea-framework-documentation/api/agent/#period","title":"period","text":"
@property\ndef period() -> float\n

Get a period to call act.

"},{"location":"aea-framework-documentation/api/agent/#runtime","title":"runtime","text":"
@property\ndef runtime() -> BaseRuntime\n

Get the runtime.

"},{"location":"aea-framework-documentation/api/agent/#setup","title":"setup","text":"
def setup() -> None\n

Set up the agent.

"},{"location":"aea-framework-documentation/api/agent/#start","title":"start","text":"
def start() -> None\n

Start the agent.

Performs the following:

  • calls start() on runtime.
  • waits for runtime to complete running (blocking)

"},{"location":"aea-framework-documentation/api/agent/#handle_envelope","title":"handle_envelope","text":"
def handle_envelope(envelope: Envelope) -> None\n

Handle an envelope.

Arguments:

  • envelope: the envelope to handle.

"},{"location":"aea-framework-documentation/api/agent/#act","title":"act","text":"
def act() -> None\n

Perform actions on period.

"},{"location":"aea-framework-documentation/api/agent/#stop","title":"stop","text":"
def stop() -> None\n

Stop the agent.

Performs the following:

  • calls stop() on runtime
  • waits for runtime to stop (blocking)

"},{"location":"aea-framework-documentation/api/agent/#teardown","title":"teardown","text":"
def teardown() -> None\n

Tear down the agent.

"},{"location":"aea-framework-documentation/api/agent/#get_periodic_tasks","title":"get_periodic_tasks","text":"
def get_periodic_tasks(\n) -> Dict[Callable, Tuple[float, Optional[datetime.datetime]]]\n

Get all periodic tasks for agent.

Returns:

dict of callable with period specified

"},{"location":"aea-framework-documentation/api/agent/#get_message_handlers","title":"get_message_handlers","text":"
def get_message_handlers() -> List[Tuple[Callable[[Any], None], Callable]]\n

Get handlers with message getters.

Returns:

List of tuples of callables: handler and coroutine to get a message

"},{"location":"aea-framework-documentation/api/agent/#exception_handler","title":"exception_handler","text":"
def exception_handler(exception: Exception, function: Callable) -> bool\n

Handle exception raised during agent main loop execution.

Arguments:

  • exception: exception raised
  • function: a callable exception raised in.

Returns:

bool, propagate exception if True otherwise skip it.

"},{"location":"aea-framework-documentation/api/agent_loop/","title":"Agent Loop","text":""},{"location":"aea-framework-documentation/api/agent_loop/#aeaagent_loop","title":"aea.agent_loop","text":"

This module contains the implementation of an agent loop using asyncio.

"},{"location":"aea-framework-documentation/api/agent_loop/#agentloopexception-objects","title":"AgentLoopException Objects","text":"
class AgentLoopException(AEAException)\n

Exception for agent loop runtime errors.

"},{"location":"aea-framework-documentation/api/agent_loop/#agentloopstates-objects","title":"AgentLoopStates Objects","text":"
class AgentLoopStates(Enum)\n

Internal agent loop states.

"},{"location":"aea-framework-documentation/api/agent_loop/#baseagentloop-objects","title":"BaseAgentLoop Objects","text":"
class BaseAgentLoop(Runnable, WithLogger, ABC)\n

Base abstract agent loop class.

"},{"location":"aea-framework-documentation/api/agent_loop/#__init__","title":"__init__","text":"
def __init__(agent: AbstractAgent,\nloop: Optional[AbstractEventLoop] = None,\nthreaded: bool = False) -> None\n

Init loop.

Arguments:

  • agent: Agent or AEA to run.
  • loop: optional asyncio event loop. if not specified a new loop will be created.
  • threaded: if True, run in threaded mode, else async

"},{"location":"aea-framework-documentation/api/agent_loop/#agent","title":"agent","text":"
@property\ndef agent() -> AbstractAgent\n

Get agent.

"},{"location":"aea-framework-documentation/api/agent_loop/#state","title":"state","text":"
@property\ndef state() -> AgentLoopStates\n

Get current main loop state.

"},{"location":"aea-framework-documentation/api/agent_loop/#wait_state","title":"wait_state","text":"
async def wait_state(\nstate_or_states: Union[Any, Sequence[Any]]) -> Tuple[Any, Any]\n

Wait state to be set.

Arguments:

  • state_or_states: state or list of states.

Returns:

tuple of previous state and new state.

"},{"location":"aea-framework-documentation/api/agent_loop/#is_running","title":"is_running","text":"
@property\ndef is_running() -> bool\n

Get running state of the loop.

"},{"location":"aea-framework-documentation/api/agent_loop/#set_loop","title":"set_loop","text":"
def set_loop(loop: AbstractEventLoop) -> None\n

Set event loop and all event loop related objects.

"},{"location":"aea-framework-documentation/api/agent_loop/#run","title":"run","text":"
async def run() -> None\n

Run agent loop.

"},{"location":"aea-framework-documentation/api/agent_loop/#send_to_skill","title":"send_to_skill","text":"
@abstractmethod\ndef send_to_skill(message_or_envelope: Union[Message, Envelope],\ncontext: Optional[EnvelopeContext] = None) -> None\n

Send message or envelope to another skill.

If message passed it will be wrapped into envelope with optional envelope context.

Arguments:

  • message_or_envelope: envelope to send to another skill.
  • context: envelope context

"},{"location":"aea-framework-documentation/api/agent_loop/#skill2skill_queue","title":"skill2skill_queue","text":"
@property\n@abstractmethod\ndef skill2skill_queue() -> Queue\n

Get skill to skill message queue.

"},{"location":"aea-framework-documentation/api/agent_loop/#asyncagentloop-objects","title":"AsyncAgentLoop Objects","text":"
class AsyncAgentLoop(BaseAgentLoop)\n

Asyncio based agent loop suitable only for AEA.

"},{"location":"aea-framework-documentation/api/agent_loop/#__init___1","title":"__init__","text":"
def __init__(agent: AbstractAgent,\nloop: AbstractEventLoop = None,\nthreaded: bool = False) -> None\n

Init agent loop.

Arguments:

  • agent: AEA instance
  • loop: asyncio loop to use. optional
  • threaded: is a new thread to be started for the agent loop

"},{"location":"aea-framework-documentation/api/agent_loop/#skill2skill_queue_1","title":"skill2skill_queue","text":"
@property\ndef skill2skill_queue() -> Queue\n

Get skill to skill message queue.

"},{"location":"aea-framework-documentation/api/agent_loop/#send_to_skill_1","title":"send_to_skill","text":"
def send_to_skill(message_or_envelope: Union[Message, Envelope],\ncontext: Optional[EnvelopeContext] = None) -> None\n

Send message or envelope to another skill.

If message passed it will be wrapped into envelope with optional envelope context.

Arguments:

  • message_or_envelope: envelope to send to another skill.
  • context: envelope context
"},{"location":"aea-framework-documentation/api/common/","title":"Common","text":""},{"location":"aea-framework-documentation/api/common/#aeacommon","title":"aea.common","text":"

This module contains the common types and interfaces used in the aea framework.

"},{"location":"aea-framework-documentation/api/exceptions/","title":"Exceptions","text":""},{"location":"aea-framework-documentation/api/exceptions/#aeaexceptions","title":"aea.exceptions","text":"

Exceptions for the AEA package.

"},{"location":"aea-framework-documentation/api/exceptions/#aeaexception-objects","title":"AEAException Objects","text":"
class AEAException(Exception)\n

User-defined exception for the AEA framework.

"},{"location":"aea-framework-documentation/api/exceptions/#aeapackageloadingerror-objects","title":"AEAPackageLoadingError Objects","text":"
class AEAPackageLoadingError(AEAException)\n

Class for exceptions that are raised for loading errors of AEA packages.

"},{"location":"aea-framework-documentation/api/exceptions/#aeasetuperror-objects","title":"AEASetupError Objects","text":"
class AEASetupError(AEAException)\n

Class for exceptions that are raised for setup errors of AEA packages.

"},{"location":"aea-framework-documentation/api/exceptions/#aeateardownerror-objects","title":"AEATeardownError Objects","text":"
class AEATeardownError(AEAException)\n

Class for exceptions that are raised for teardown errors of AEA packages.

"},{"location":"aea-framework-documentation/api/exceptions/#aeaactexception-objects","title":"AEAActException Objects","text":"
class AEAActException(AEAException)\n

Class for exceptions that are raised for act errors of AEA packages.

"},{"location":"aea-framework-documentation/api/exceptions/#aeahandleexception-objects","title":"AEAHandleException Objects","text":"
class AEAHandleException(AEAException)\n

Class for exceptions that are raised for handler errors of AEA packages.

"},{"location":"aea-framework-documentation/api/exceptions/#aeainstantiationexception-objects","title":"AEAInstantiationException Objects","text":"
class AEAInstantiationException(AEAException)\n

Class for exceptions that are raised for instantiation errors of AEA packages.

"},{"location":"aea-framework-documentation/api/exceptions/#aeapluginerror-objects","title":"AEAPluginError Objects","text":"
class AEAPluginError(AEAException)\n

Class for exceptions that are raised for wrong plugin setup of the working set.

"},{"location":"aea-framework-documentation/api/exceptions/#aeaenforceerror-objects","title":"AEAEnforceError Objects","text":"
class AEAEnforceError(AEAException)\n

Class for enforcement errors.

"},{"location":"aea-framework-documentation/api/exceptions/#aeavalidationerror-objects","title":"AEAValidationError Objects","text":"
class AEAValidationError(AEAException)\n

Class for validation errors of an AEA.

"},{"location":"aea-framework-documentation/api/exceptions/#aeacomponentloadexception-objects","title":"AEAComponentLoadException Objects","text":"
class AEAComponentLoadException(AEAException)\n

Class for component loading errors of an AEA.

"},{"location":"aea-framework-documentation/api/exceptions/#aeawalletnoaddressexception-objects","title":"AEAWalletNoAddressException Objects","text":"
class AEAWalletNoAddressException(AEAException)\n

Class for attempts to instantiate a wallet without addresses.

"},{"location":"aea-framework-documentation/api/exceptions/#_stopruntime-objects","title":"_StopRuntime Objects","text":"
class _StopRuntime(Exception)\n

Exception to stop runtime.

For internal usage only! Used to perform asyncio call from sync callbacks.

"},{"location":"aea-framework-documentation/api/exceptions/#__init__","title":"__init__","text":"
def __init__(reraise: Optional[Exception] = None) -> None\n

Init _StopRuntime exception.

Arguments:

  • reraise: exception to reraise.

"},{"location":"aea-framework-documentation/api/exceptions/#enforce","title":"enforce","text":"
def enforce(is_valid_condition: bool,\nexception_text: str,\nexception_class: Type[Exception] = AEAEnforceError) -> None\n

Evaluate a condition and raise an exception with the provided text if it is not satisfied.

Arguments:

  • is_valid_condition: the valid condition
  • exception_text: the exception to be raised
  • exception_class: the class of exception

"},{"location":"aea-framework-documentation/api/exceptions/#parse_exception","title":"parse_exception","text":"
def parse_exception(exception: Exception, limit: int = -1) -> str\n

Parse an exception to get the relevant lines.

Arguments:

  • exception: the exception to be parsed
  • limit: the limit

Returns:

exception as string

"},{"location":"aea-framework-documentation/api/launcher/","title":"Launcher","text":""},{"location":"aea-framework-documentation/api/launcher/#aealauncher","title":"aea.launcher","text":"

This module contains the implementation of multiple AEA configs launcher.

"},{"location":"aea-framework-documentation/api/launcher/#load_agent","title":"load_agent","text":"
def load_agent(agent_dir: Union[PathLike, str],\npassword: Optional[str] = None) -> AEA\n

Load AEA from directory.

Arguments:

  • agent_dir: agent configuration directory
  • password: the password to encrypt/decrypt the private key.

Returns:

AEA instance

"},{"location":"aea-framework-documentation/api/launcher/#aeadirtask-objects","title":"AEADirTask Objects","text":"
class AEADirTask(AbstractExecutorTask)\n

Task to run agent from agent configuration directory.

"},{"location":"aea-framework-documentation/api/launcher/#__init__","title":"__init__","text":"
def __init__(agent_dir: Union[PathLike, str],\npassword: Optional[str] = None) -> None\n

Init aea config dir task.

Arguments:

  • agent_dir: directory with aea config.
  • password: the password to encrypt/decrypt the private key.

"},{"location":"aea-framework-documentation/api/launcher/#id","title":"id","text":"
@property\ndef id() -> Union[PathLike, str]\n

Return agent_dir.

"},{"location":"aea-framework-documentation/api/launcher/#start","title":"start","text":"
def start() -> None\n

Start task.

"},{"location":"aea-framework-documentation/api/launcher/#stop","title":"stop","text":"
def stop() -> None\n

Stop task.

"},{"location":"aea-framework-documentation/api/launcher/#create_async_task","title":"create_async_task","text":"
def create_async_task(loop: AbstractEventLoop) -> TaskAwaitable\n

Return asyncio Task for task run in asyncio loop.

"},{"location":"aea-framework-documentation/api/launcher/#aeadirmultiprocesstask-objects","title":"AEADirMultiprocessTask Objects","text":"
class AEADirMultiprocessTask(AbstractMultiprocessExecutorTask)\n

Task to run agent from agent configuration directory.

Version for multiprocess executor mode.

"},{"location":"aea-framework-documentation/api/launcher/#__init___1","title":"__init__","text":"
def __init__(agent_dir: Union[PathLike, str],\nlog_level: Optional[str] = None,\npassword: Optional[str] = None) -> None\n

Init aea config dir task.

Arguments:

  • agent_dir: directory with aea config.
  • log_level: debug level applied for AEA in subprocess
  • password: the password to encrypt/decrypt the private key.

"},{"location":"aea-framework-documentation/api/launcher/#id_1","title":"id","text":"
@property\ndef id() -> Union[PathLike, str]\n

Return agent_dir.

"},{"location":"aea-framework-documentation/api/launcher/#failed","title":"failed","text":"
@property\ndef failed() -> bool\n

Return was exception failed or not.

If it's running it's not failed.

Returns:

bool

"},{"location":"aea-framework-documentation/api/launcher/#start_1","title":"start","text":"
def start() -> Tuple[Callable, Sequence[Any]]\n

Return function and arguments to call within subprocess.

"},{"location":"aea-framework-documentation/api/launcher/#stop_1","title":"stop","text":"
def stop() -> None\n

Stop task.

"},{"location":"aea-framework-documentation/api/launcher/#aealauncher-objects","title":"AEALauncher Objects","text":"
class AEALauncher(AbstractMultipleRunner)\n

Run multiple AEA instances.

"},{"location":"aea-framework-documentation/api/launcher/#__init___2","title":"__init__","text":"
def __init__(agent_dirs: Sequence[Union[PathLike, str]],\nmode: str,\nfail_policy: ExecutorExceptionPolicies = ExecutorExceptionPolicies\n.propagate,\nlog_level: Optional[str] = None,\npassword: Optional[str] = None) -> None\n

Init AEALauncher.

Arguments:

  • agent_dirs: sequence of AEA config directories.
  • mode: executor name to use.
  • fail_policy: one of ExecutorExceptionPolicies to be used with Executor
  • log_level: debug level applied for AEA in subprocesses
  • password: the password to encrypt/decrypt the private key.
"},{"location":"aea-framework-documentation/api/multiplexer/","title":"Multiplexer","text":""},{"location":"aea-framework-documentation/api/multiplexer/#aeamultiplexer","title":"aea.multiplexer","text":"

Module for the multiplexer class and related classes.

"},{"location":"aea-framework-documentation/api/multiplexer/#multiplexerstatus-objects","title":"MultiplexerStatus Objects","text":"
class MultiplexerStatus(AsyncState)\n

The connection status class.

"},{"location":"aea-framework-documentation/api/multiplexer/#__init__","title":"__init__","text":"
def __init__() -> None\n

Initialize the connection status.

"},{"location":"aea-framework-documentation/api/multiplexer/#is_connected","title":"is_connected","text":"
@property\ndef is_connected() -> bool\n

Return is connected.

"},{"location":"aea-framework-documentation/api/multiplexer/#is_connecting","title":"is_connecting","text":"
@property\ndef is_connecting() -> bool\n

Return is connecting.

"},{"location":"aea-framework-documentation/api/multiplexer/#is_disconnected","title":"is_disconnected","text":"
@property\ndef is_disconnected() -> bool\n

Return is disconnected.

"},{"location":"aea-framework-documentation/api/multiplexer/#is_disconnecting","title":"is_disconnecting","text":"
@property\ndef is_disconnecting() -> bool\n

Return is disconnected.

"},{"location":"aea-framework-documentation/api/multiplexer/#asyncmultiplexer-objects","title":"AsyncMultiplexer Objects","text":"
class AsyncMultiplexer(Runnable, WithLogger)\n

This class can handle multiple connections at once.

"},{"location":"aea-framework-documentation/api/multiplexer/#__init___1","title":"__init__","text":"
def __init__(\nconnections: Optional[Sequence[Connection]] = None,\ndefault_connection_index: int = 0,\nloop: Optional[AbstractEventLoop] = None,\nexception_policy: ExceptionPolicyEnum = ExceptionPolicyEnum.propagate,\nthreaded: bool = False,\nagent_name: str = \"standalone\",\ndefault_routing: Optional[Dict[PublicId, PublicId]] = None,\ndefault_connection: Optional[PublicId] = None,\nprotocols: Optional[List[Union[Protocol, Message]]] = None) -> None\n

Initialize the connection multiplexer.

Arguments:

  • connections: a sequence of connections.
  • default_connection_index: the index of the connection to use as default. This information is used for envelopes which don't specify any routing context. If connections is None, this parameter is ignored.
  • loop: the event loop to run the multiplexer. If None, a new event loop is created.
  • exception_policy: the exception policy used for connections.
  • threaded: if True, run in threaded mode, else async
  • agent_name: the name of the agent that owns the multiplexer, for logging purposes.
  • default_routing: default routing map
  • default_connection: default connection
  • protocols: protocols used

"},{"location":"aea-framework-documentation/api/multiplexer/#default_connection","title":"default_connection","text":"
@property\ndef default_connection() -> Optional[Connection]\n

Get the default connection.

"},{"location":"aea-framework-documentation/api/multiplexer/#in_queue","title":"in_queue","text":"
@property\ndef in_queue() -> AsyncFriendlyQueue\n

Get the in queue.

"},{"location":"aea-framework-documentation/api/multiplexer/#out_queue","title":"out_queue","text":"
@property\ndef out_queue() -> asyncio.Queue\n

Get the out queue.

"},{"location":"aea-framework-documentation/api/multiplexer/#connections","title":"connections","text":"
@property\ndef connections() -> Tuple[Connection, ...]\n

Get the connections.

"},{"location":"aea-framework-documentation/api/multiplexer/#is_connected_1","title":"is_connected","text":"
@property\ndef is_connected() -> bool\n

Check whether the multiplexer is processing envelopes.

"},{"location":"aea-framework-documentation/api/multiplexer/#default_routing","title":"default_routing","text":"
@property\ndef default_routing() -> Dict[PublicId, PublicId]\n

Get the default routing.

"},{"location":"aea-framework-documentation/api/multiplexer/#default_routing_1","title":"default_routing","text":"
@default_routing.setter\ndef default_routing(default_routing: Dict[PublicId, PublicId]) -> None\n

Set the default routing.

"},{"location":"aea-framework-documentation/api/multiplexer/#connection_status","title":"connection_status","text":"
@property\ndef connection_status() -> MultiplexerStatus\n

Get the connection status.

"},{"location":"aea-framework-documentation/api/multiplexer/#run","title":"run","text":"
async def run() -> None\n

Run multiplexer connect and receive/send tasks.

"},{"location":"aea-framework-documentation/api/multiplexer/#set_loop","title":"set_loop","text":"
def set_loop(loop: AbstractEventLoop) -> None\n

Set event loop and all event loop related objects.

Arguments:

  • loop: asyncio event loop.

"},{"location":"aea-framework-documentation/api/multiplexer/#add_connection","title":"add_connection","text":"
def add_connection(connection: Connection, is_default: bool = False) -> None\n

Add a connection to the multiplexer.

Arguments:

  • connection: the connection to add.
  • is_default: whether the connection added should be the default one.

"},{"location":"aea-framework-documentation/api/multiplexer/#connect","title":"connect","text":"
async def connect() -> None\n

Connect the multiplexer.

"},{"location":"aea-framework-documentation/api/multiplexer/#disconnect","title":"disconnect","text":"
async def disconnect() -> None\n

Disconnect the multiplexer.

"},{"location":"aea-framework-documentation/api/multiplexer/#get","title":"get","text":"
def get(block: bool = False,\ntimeout: Optional[float] = None) -> Optional[Envelope]\n

Get an envelope within a timeout.

Arguments:

  • block: make the call blocking (ignore the timeout).
  • timeout: the timeout to wait until an envelope is received.

Returns:

the envelope, or None if no envelope is available within a timeout.

"},{"location":"aea-framework-documentation/api/multiplexer/#async_get","title":"async_get","text":"
async def async_get() -> Envelope\n

Get an envelope async way.

Returns:

the envelope

"},{"location":"aea-framework-documentation/api/multiplexer/#async_wait","title":"async_wait","text":"
async def async_wait() -> None\n

Get an envelope async way.

Returns:

the envelope

"},{"location":"aea-framework-documentation/api/multiplexer/#put","title":"put","text":"
def put(envelope: Envelope) -> None\n

Schedule an envelope for sending it.

Notice that the output queue is an asyncio.Queue which uses an event loop running on a different thread than the one used in this function.

Arguments:

  • envelope: the envelope to be sent.

"},{"location":"aea-framework-documentation/api/multiplexer/#multiplexer-objects","title":"Multiplexer Objects","text":"
class Multiplexer(AsyncMultiplexer)\n

Transit sync multiplexer for compatibility.

"},{"location":"aea-framework-documentation/api/multiplexer/#__init___2","title":"__init__","text":"
def __init__(*args: Any, **kwargs: Any) -> None\n

Initialize the connection multiplexer.

Arguments:

  • args: arguments
  • kwargs: keyword arguments

"},{"location":"aea-framework-documentation/api/multiplexer/#set_loop_1","title":"set_loop","text":"
def set_loop(loop: AbstractEventLoop) -> None\n

Set event loop and all event loop related objects.

Arguments:

  • loop: asyncio event loop.

"},{"location":"aea-framework-documentation/api/multiplexer/#connect_1","title":"connect","text":"
def connect() -> None\n

Connect the multiplexer.

Synchronously in thread spawned if new loop created.

"},{"location":"aea-framework-documentation/api/multiplexer/#disconnect_1","title":"disconnect","text":"
def disconnect() -> None\n

Disconnect the multiplexer.

Also stops a dedicated thread for event loop if spawned on connect.

"},{"location":"aea-framework-documentation/api/multiplexer/#put_1","title":"put","text":"
def put(envelope: Envelope) -> None\n

Schedule an envelope for sending it.

Notice that the output queue is an asyncio.Queue which uses an event loop running on a different thread than the one used in this function.

Arguments:

  • envelope: the envelope to be sent.

"},{"location":"aea-framework-documentation/api/multiplexer/#inbox-objects","title":"InBox Objects","text":"
class InBox()\n

A queue from where you can only consume envelopes.

"},{"location":"aea-framework-documentation/api/multiplexer/#__init___3","title":"__init__","text":"
def __init__(multiplexer: AsyncMultiplexer) -> None\n

Initialize the inbox.

Arguments:

  • multiplexer: the multiplexer

"},{"location":"aea-framework-documentation/api/multiplexer/#empty","title":"empty","text":"
def empty() -> bool\n

Check for a envelope on the in queue.

Returns:

boolean indicating whether there is an envelope or not

"},{"location":"aea-framework-documentation/api/multiplexer/#get_1","title":"get","text":"
def get(block: bool = False, timeout: Optional[float] = None) -> Envelope\n

Check for a envelope on the in queue.

Arguments:

  • block: make the call blocking (ignore the timeout).
  • timeout: times out the block after timeout seconds.

Raises:

  • Empty: if the attempt to get an envelope fails.

Returns:

the envelope object.

"},{"location":"aea-framework-documentation/api/multiplexer/#get_nowait","title":"get_nowait","text":"
def get_nowait() -> Optional[Envelope]\n

Check for a envelope on the in queue and wait for no time.

Returns:

the envelope object

"},{"location":"aea-framework-documentation/api/multiplexer/#async_get_1","title":"async_get","text":"
async def async_get() -> Envelope\n

Check for a envelope on the in queue.

Returns:

the envelope object.

"},{"location":"aea-framework-documentation/api/multiplexer/#async_wait_1","title":"async_wait","text":"
async def async_wait() -> None\n

Check for a envelope on the in queue.

"},{"location":"aea-framework-documentation/api/multiplexer/#outbox-objects","title":"OutBox Objects","text":"
class OutBox()\n

A queue from where you can only enqueue envelopes.

"},{"location":"aea-framework-documentation/api/multiplexer/#__init___4","title":"__init__","text":"
def __init__(multiplexer: AsyncMultiplexer) -> None\n

Initialize the outbox.

Arguments:

  • multiplexer: the multiplexer

"},{"location":"aea-framework-documentation/api/multiplexer/#empty_1","title":"empty","text":"
def empty() -> bool\n

Check for a envelope on the in queue.

Returns:

boolean indicating whether there is an envelope or not

"},{"location":"aea-framework-documentation/api/multiplexer/#put_2","title":"put","text":"
def put(envelope: Envelope) -> None\n

Put an envelope into the queue.

Arguments:

  • envelope: the envelope.

"},{"location":"aea-framework-documentation/api/multiplexer/#put_message","title":"put_message","text":"
def put_message(message: Message,\ncontext: Optional[EnvelopeContext] = None) -> None\n

Put a message in the outbox.

This constructs an envelope with the input arguments.

Arguments:

  • message: the message
  • context: the envelope context
"},{"location":"aea-framework-documentation/api/runner/","title":"Runner","text":""},{"location":"aea-framework-documentation/api/runner/#aearunner","title":"aea.runner","text":"

This module contains the implementation of AEA multiple instances runner.

"},{"location":"aea-framework-documentation/api/runner/#aeainstancetask-objects","title":"AEAInstanceTask Objects","text":"
class AEAInstanceTask(AbstractExecutorTask)\n

Task to run agent instance.

"},{"location":"aea-framework-documentation/api/runner/#__init__","title":"__init__","text":"
def __init__(agent: AEA) -> None\n

Init aea instance task.

Arguments:

  • agent: AEA instance to run within task.

"},{"location":"aea-framework-documentation/api/runner/#id","title":"id","text":"
@property\ndef id() -> str\n

Return agent name.

"},{"location":"aea-framework-documentation/api/runner/#start","title":"start","text":"
def start() -> None\n

Start task.

"},{"location":"aea-framework-documentation/api/runner/#stop","title":"stop","text":"
def stop() -> None\n

Stop task.

"},{"location":"aea-framework-documentation/api/runner/#create_async_task","title":"create_async_task","text":"
def create_async_task(loop: AbstractEventLoop) -> TaskAwaitable\n

Return asyncio Task for task run in asyncio loop.

Arguments:

  • loop: abstract event loop

Returns:

task to run runtime

"},{"location":"aea-framework-documentation/api/runner/#aearunner-objects","title":"AEARunner Objects","text":"
class AEARunner(AbstractMultipleRunner)\n

Run multiple AEA instances.

"},{"location":"aea-framework-documentation/api/runner/#__init___1","title":"__init__","text":"
def __init__(\nagents: Sequence[AEA],\nmode: str,\nfail_policy: ExecutorExceptionPolicies = ExecutorExceptionPolicies.\npropagate\n) -> None\n

Init AEARunner.

Arguments:

  • agents: sequence of AEA instances to run.
  • mode: executor name to use.
  • fail_policy: one of ExecutorExceptionPolicies to be used with Executor
"},{"location":"aea-framework-documentation/api/runtime/","title":"Runtime","text":""},{"location":"aea-framework-documentation/api/runtime/#aearuntime","title":"aea.runtime","text":"

This module contains the implementation of runtime for economic agent (AEA).

"},{"location":"aea-framework-documentation/api/runtime/#runtimestates-objects","title":"RuntimeStates Objects","text":"
class RuntimeStates(Enum)\n

Runtime states.

"},{"location":"aea-framework-documentation/api/runtime/#baseruntime-objects","title":"BaseRuntime Objects","text":"
class BaseRuntime(Runnable, WithLogger)\n

Abstract runtime class to create implementations.

"},{"location":"aea-framework-documentation/api/runtime/#__init__","title":"__init__","text":"
def __init__(agent: AbstractAgent,\nmultiplexer_options: Dict,\nloop_mode: Optional[str] = None,\nloop: Optional[AbstractEventLoop] = None,\nthreaded: bool = False,\ntask_manager_mode: Optional[str] = None) -> None\n

Init runtime.

Arguments:

  • agent: Agent to run.
  • multiplexer_options: options for the multiplexer.
  • loop_mode: agent main loop mode.
  • loop: optional event loop. if not provided a new one will be created.
  • threaded: if True, run in threaded mode, else async
  • task_manager_mode: mode of the task manager.

"},{"location":"aea-framework-documentation/api/runtime/#storage","title":"storage","text":"
@property\ndef storage() -> Optional[Storage]\n

Get optional storage.

"},{"location":"aea-framework-documentation/api/runtime/#loop_mode","title":"loop_mode","text":"
@property\ndef loop_mode() -> str\n

Get current loop mode.

"},{"location":"aea-framework-documentation/api/runtime/#task_manager","title":"task_manager","text":"
@property\ndef task_manager() -> TaskManager\n

Get the task manager.

"},{"location":"aea-framework-documentation/api/runtime/#loop","title":"loop","text":"
@property\ndef loop() -> Optional[AbstractEventLoop]\n

Get event loop.

"},{"location":"aea-framework-documentation/api/runtime/#agent_loop","title":"agent_loop","text":"
@property\ndef agent_loop() -> BaseAgentLoop\n

Get the agent loop.

"},{"location":"aea-framework-documentation/api/runtime/#multiplexer","title":"multiplexer","text":"
@property\ndef multiplexer() -> AsyncMultiplexer\n

Get multiplexer.

"},{"location":"aea-framework-documentation/api/runtime/#is_running","title":"is_running","text":"
@property\ndef is_running() -> bool\n

Get running state of the runtime.

"},{"location":"aea-framework-documentation/api/runtime/#is_stopped","title":"is_stopped","text":"
@property\ndef is_stopped() -> bool\n

Get stopped state of the runtime.

"},{"location":"aea-framework-documentation/api/runtime/#state","title":"state","text":"
@property\ndef state() -> RuntimeStates\n

Get runtime state.

Returns:

RuntimeStates

"},{"location":"aea-framework-documentation/api/runtime/#decision_maker","title":"decision_maker","text":"
@property\ndef decision_maker() -> DecisionMaker\n

Return decision maker if set.

"},{"location":"aea-framework-documentation/api/runtime/#set_decision_maker","title":"set_decision_maker","text":"
def set_decision_maker(decision_maker_handler: DecisionMakerHandler) -> None\n

Set decision maker with handler provided.

"},{"location":"aea-framework-documentation/api/runtime/#set_loop","title":"set_loop","text":"
def set_loop(loop: AbstractEventLoop) -> None\n

Set event loop to be used.

Arguments:

  • loop: event loop to use.

"},{"location":"aea-framework-documentation/api/runtime/#asyncruntime-objects","title":"AsyncRuntime Objects","text":"
class AsyncRuntime(BaseRuntime)\n

Asynchronous runtime: uses asyncio loop for multiplexer and async agent main loop.

"},{"location":"aea-framework-documentation/api/runtime/#__init___1","title":"__init__","text":"
def __init__(agent: AbstractAgent,\nmultiplexer_options: Dict,\nloop_mode: Optional[str] = None,\nloop: Optional[AbstractEventLoop] = None,\nthreaded: bool = False,\ntask_manager_mode: Optional[str] = None) -> None\n

Init runtime.

Arguments:

  • agent: Agent to run.
  • multiplexer_options: options for the multiplexer.
  • loop_mode: agent main loop mode.
  • loop: optional event loop. if not provided a new one will be created.
  • threaded: if True, run in threaded mode, else async
  • task_manager_mode: mode of the task manager.

"},{"location":"aea-framework-documentation/api/runtime/#set_loop_1","title":"set_loop","text":"
def set_loop(loop: AbstractEventLoop) -> None\n

Set event loop to be used.

Arguments:

  • loop: event loop to use.

"},{"location":"aea-framework-documentation/api/runtime/#run","title":"run","text":"
async def run() -> None\n

Start runtime task.

Starts multiplexer and agent loop.

"},{"location":"aea-framework-documentation/api/runtime/#stop_runtime","title":"stop_runtime","text":"
async def stop_runtime() -> None\n

Stop runtime coroutine.

Stop main loop. Tear down the agent.. Disconnect multiplexer.

"},{"location":"aea-framework-documentation/api/runtime/#run_runtime","title":"run_runtime","text":"
async def run_runtime() -> None\n

Run runtime which means start agent loop, multiplexer and storage.

"},{"location":"aea-framework-documentation/api/runtime/#threadedruntime-objects","title":"ThreadedRuntime Objects","text":"
class ThreadedRuntime(AsyncRuntime)\n

Run agent and multiplexer in different threads with own asyncio loops.

"},{"location":"aea-framework-documentation/api/components/base/","title":"Base","text":""},{"location":"aea-framework-documentation/api/components/base/#aeacomponentsbase","title":"aea.components.base","text":"

This module contains definitions of agent components.

"},{"location":"aea-framework-documentation/api/components/base/#component-objects","title":"Component Objects","text":"
class Component(ABC, WithLogger)\n

Abstract class for an agent component.

"},{"location":"aea-framework-documentation/api/components/base/#__init__","title":"__init__","text":"
def __init__(configuration: Optional[ComponentConfiguration] = None,\nis_vendor: bool = False,\n**kwargs: Any) -> None\n

Initialize a package.

Arguments:

  • configuration: the package configuration.
  • is_vendor: whether the package is vendorized.
  • kwargs: the keyword arguments for the logger.

"},{"location":"aea-framework-documentation/api/components/base/#component_type","title":"component_type","text":"
@property\ndef component_type() -> ComponentType\n

Get the component type.

"},{"location":"aea-framework-documentation/api/components/base/#is_vendor","title":"is_vendor","text":"
@property\ndef is_vendor() -> bool\n

Get whether the component is vendorized or not.

"},{"location":"aea-framework-documentation/api/components/base/#prefix_import_path","title":"prefix_import_path","text":"
@property\ndef prefix_import_path() -> str\n

Get the prefix import path for this component.

"},{"location":"aea-framework-documentation/api/components/base/#component_id","title":"component_id","text":"
@property\ndef component_id() -> ComponentId\n

Ge the package id.

"},{"location":"aea-framework-documentation/api/components/base/#public_id","title":"public_id","text":"
@property\ndef public_id() -> PublicId\n

Get the public id.

"},{"location":"aea-framework-documentation/api/components/base/#configuration","title":"configuration","text":"
@property\ndef configuration() -> ComponentConfiguration\n

Get the component configuration.

"},{"location":"aea-framework-documentation/api/components/base/#directory","title":"directory","text":"
@property\ndef directory() -> Path\n

Get the directory. Raise error if it has not been set yet.

"},{"location":"aea-framework-documentation/api/components/base/#directory_1","title":"directory","text":"
@directory.setter\ndef directory(path: Path) -> None\n

Set the directory. Raise error if already set.

"},{"location":"aea-framework-documentation/api/components/base/#build_directory","title":"build_directory","text":"
@property\ndef build_directory() -> Optional[str]\n

Get build directory for the component.

"},{"location":"aea-framework-documentation/api/components/base/#load_aea_package","title":"load_aea_package","text":"
def load_aea_package(configuration: ComponentConfiguration) -> None\n

Load the AEA package from configuration.

It adds all the init.py modules into sys.modules.

Arguments:

  • configuration: the configuration object.

"},{"location":"aea-framework-documentation/api/components/base/#perform_load_aea_package","title":"perform_load_aea_package","text":"
def perform_load_aea_package(dir_: Path, author: str, package_type_plural: str,\npackage_name: str) -> None\n

Load the AEA package from values provided.

It adds all the init.py modules into sys.modules.

Arguments:

  • dir_: path of the component.
  • author: str
  • package_type_plural: str
  • package_name: str
"},{"location":"aea-framework-documentation/api/components/loader/","title":"Loader","text":""},{"location":"aea-framework-documentation/api/components/loader/#aeacomponentsloader","title":"aea.components.loader","text":"

This module contains utilities for loading components.

"},{"location":"aea-framework-documentation/api/components/loader/#component_type_to_class","title":"component_type_to_class","text":"
def component_type_to_class(component_type: ComponentType) -> Type[Component]\n

Get the component class from the component type.

Arguments:

  • component_type: the component type

Returns:

the component class

"},{"location":"aea-framework-documentation/api/components/loader/#load_component_from_config","title":"load_component_from_config","text":"
def load_component_from_config(configuration: ComponentConfiguration, *args,\n**kwargs) -> Component\n

Load a component from a directory.

Arguments:

  • configuration: the component configuration.
  • args: the positional arguments.
  • kwargs: the keyword arguments.

Returns:

the component instance.

"},{"location":"aea-framework-documentation/api/components/loader/#aeapackagenotfound-objects","title":"AEAPackageNotFound Objects","text":"
class AEAPackageNotFound(Exception)\n

Exception when failed to import package, cause not exists.

"},{"location":"aea-framework-documentation/api/components/utils/","title":"Utils","text":""},{"location":"aea-framework-documentation/api/components/utils/#aeacomponentsutils","title":"aea.components.utils","text":"

This module contains the component loading utils.

"},{"location":"aea-framework-documentation/api/configurations/constants/","title":"Constants","text":""},{"location":"aea-framework-documentation/api/configurations/constants/#aeaconfigurationsconstants","title":"aea.configurations.constants","text":"

Module to declare constants.

"},{"location":"aea-framework-documentation/api/configurations/data_types/","title":"Data Types","text":""},{"location":"aea-framework-documentation/api/configurations/data_types/#aeaconfigurationsdata_types","title":"aea.configurations.data_types","text":"

Base config data types.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#jsonserializable-objects","title":"JSONSerializable Objects","text":"
class JSONSerializable(ABC)\n

Interface for JSON-serializable objects.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#json","title":"json","text":"
@property\n@abstractmethod\ndef json() -> Dict\n

Compute the JSON representation.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#from_json","title":"from_json","text":"
@classmethod\n@abstractmethod\ndef from_json(cls, obj: Dict) -> \"JSONSerializable\"\n

Build from a JSON object.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#packageversion-objects","title":"PackageVersion Objects","text":"
@functools.total_ordering\nclass PackageVersion()\n

A package version.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#__init__","title":"__init__","text":"
def __init__(version_like: PackageVersionLike) -> None\n

Initialize a package version.

Arguments:

  • version_like: a string, os a semver.VersionInfo object.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#is_latest","title":"is_latest","text":"
@property\ndef is_latest() -> bool\n

Check whether the version is 'latest'.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#__str__","title":"__str__","text":"
def __str__() -> str\n

Get the string representation.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#__eq__","title":"__eq__","text":"
def __eq__(other: Any) -> bool\n

Check equality.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#__lt__","title":"__lt__","text":"
def __lt__(other: Any) -> bool\n

Compare with another object.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#packagetype-objects","title":"PackageType Objects","text":"
class PackageType(Enum)\n

Package types.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#to_plural","title":"to_plural","text":"
def to_plural() -> str\n

Get the plural name.

PackageType.AGENT.to_plural() 'agents' PackageType.PROTOCOL.to_plural() 'protocols' PackageType.CONNECTION.to_plural() 'connections' PackageType.SKILL.to_plural() 'skills' PackageType.CONTRACT.to_plural() 'contracts'

Returns:

pluralised package type

"},{"location":"aea-framework-documentation/api/configurations/data_types/#__str___1","title":"__str__","text":"
def __str__() -> str\n

Convert to string.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#componenttype-objects","title":"ComponentType Objects","text":"
class ComponentType(Enum)\n

Enum of component types supported.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#to_package_type","title":"to_package_type","text":"
def to_package_type() -> PackageType\n

Get package type for component type.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#plurals","title":"plurals","text":"
@staticmethod\ndef plurals() -> Collection[str]\n

Get the collection of type names, plural.

ComponentType.plurals() ['protocols', 'connections', 'skills', 'contracts']

Returns:

list of all pluralised component types

"},{"location":"aea-framework-documentation/api/configurations/data_types/#to_plural_1","title":"to_plural","text":"
def to_plural() -> str\n

Get the plural version of the component type.

ComponentType.PROTOCOL.to_plural() 'protocols' ComponentType.CONNECTION.to_plural() 'connections' ComponentType.SKILL.to_plural() 'skills' ComponentType.CONTRACT.to_plural() 'contracts'

Returns:

pluralised component type

"},{"location":"aea-framework-documentation/api/configurations/data_types/#__str___2","title":"__str__","text":"
def __str__() -> str\n

Get the string representation.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#publicid-objects","title":"PublicId Objects","text":"
class PublicId(JSONSerializable)\n

This class implement a public identifier.

A public identifier is composed of three elements: - author - name - version

The concatenation of those three elements gives the public identifier:

author/name:version\n

public_id = PublicId(\"author\", \"my_package\", \"0.1.0\") assert public_id.author == \"author\" assert public_id.name == \"my_package\" assert public_id.version == \"0.1.0\" another_public_id = PublicId(\"author\", \"my_package\", \"0.1.0\") assert hash(public_id) == hash(another_public_id) assert public_id == another_public_id latest_public_id = PublicId(\"author\", \"my_package\", \"latest\") latest_public_id latest_public_id.package_version.is_latest True

"},{"location":"aea-framework-documentation/api/configurations/data_types/#__init___1","title":"__init__","text":"
def __init__(author: SimpleIdOrStr,\nname: SimpleIdOrStr,\nversion: Optional[PackageVersionLike] = None) -> None\n

Initialize the public identifier.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#author","title":"author","text":"
@property\ndef author() -> str\n

Get the author.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#name","title":"name","text":"
@property\ndef name() -> str\n

Get the name.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#version","title":"version","text":"
@property\ndef version() -> str\n

Get the version string.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#package_version","title":"package_version","text":"
@property\ndef package_version() -> PackageVersion\n

Get the package version object.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#to_any","title":"to_any","text":"
def to_any() -> \"PublicId\"\n

Return the same public id, but with any version.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#same_prefix","title":"same_prefix","text":"
def same_prefix(other: \"PublicId\") -> bool\n

Check if the other public id has the same author and name of this.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#to_latest","title":"to_latest","text":"
def to_latest() -> \"PublicId\"\n

Return the same public id, but with latest version.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#is_valid_str","title":"is_valid_str","text":"
@classmethod\ndef is_valid_str(cls, public_id_string: str) -> bool\n

Check if a string is a public id.

Arguments:

  • public_id_string: the public id in string format.

Returns:

bool indicating validity

"},{"location":"aea-framework-documentation/api/configurations/data_types/#from_str","title":"from_str","text":"
@classmethod\ndef from_str(cls, public_id_string: str) -> \"PublicId\"\n

Initialize the public id from the string.

str(PublicId.from_str(\"author/package_name:0.1.0\")) 'author/package_name:0.1.0'

A bad formatted input raises value error:

PublicId.from_str(\"bad/formatted:input\") Traceback (most recent call last): ... ValueError: Input 'bad/formatted:input' is not well formatted.

Arguments:

  • public_id_string: the public id in string format.

Raises:

  • ValueError: if the string in input is not well formatted.

Returns:

the public id object.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#try_from_str","title":"try_from_str","text":"
@classmethod\ndef try_from_str(cls, public_id_string: str) -> Optional[\"PublicId\"]\n

Safely try to get public id from string.

Arguments:

  • public_id_string: the public id in string format.

Returns:

the public id object or None

"},{"location":"aea-framework-documentation/api/configurations/data_types/#from_uri_path","title":"from_uri_path","text":"
@classmethod\ndef from_uri_path(cls, public_id_uri_path: str) -> \"PublicId\"\n

Initialize the public id from the string.

str(PublicId.from_uri_path(\"author/package_name/0.1.0\")) 'author/package_name:0.1.0'

A bad formatted input raises value error:

PublicId.from_uri_path(\"bad/formatted:input\") Traceback (most recent call last): ... ValueError: Input 'bad/formatted:input' is not well formatted.

Arguments:

  • public_id_uri_path: the public id in uri path string format.

Raises:

  • ValueError: if the string in input is not well formatted.

Returns:

the public id object.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#to_uri_path","title":"to_uri_path","text":"
@property\ndef to_uri_path() -> str\n

Turn the public id into a uri path string.

Returns:

uri path string

"},{"location":"aea-framework-documentation/api/configurations/data_types/#json_1","title":"json","text":"
@property\ndef json() -> Dict\n

Compute the JSON representation.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#from_json_1","title":"from_json","text":"
@classmethod\ndef from_json(cls, obj: Dict) -> \"PublicId\"\n

Build from a JSON object.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#__hash__","title":"__hash__","text":"
def __hash__() -> int\n

Get the hash.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#__str___3","title":"__str__","text":"
def __str__() -> str\n

Get the string representation.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#__repr__","title":"__repr__","text":"
def __repr__() -> str\n

Get the representation.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#__eq___1","title":"__eq__","text":"
def __eq__(other: Any) -> bool\n

Compare with another object.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#__lt___1","title":"__lt__","text":"
def __lt__(other: Any) -> bool\n

Compare two public ids.

public_id_1 = PublicId(\"author_1\", \"name_1\", \"0.1.0\") public_id_2 = PublicId(\"author_1\", \"name_1\", \"0.1.1\") public_id_3 = PublicId(\"author_1\", \"name_2\", \"0.1.0\") public_id_1 > public_id_2 False public_id_1 < public_id_2 True

public_id_1 < public_id_3 Traceback (most recent call last): ... ValueError: The public IDs author_1/name_1:0.1.0 and author_1/name_2:0.1.0 cannot be compared. Their author or name attributes are different.

Arguments:

  • other: the object to compate to

Raises:

  • ValueError: if the public ids cannot be confirmed

Returns:

whether or not the inequality is satisfied

"},{"location":"aea-framework-documentation/api/configurations/data_types/#packageid-objects","title":"PackageId Objects","text":"
class PackageId()\n

A package identifier.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#__init___2","title":"__init__","text":"
def __init__(package_type: Union[PackageType, str],\npublic_id: PublicId) -> None\n

Initialize the package id.

Arguments:

  • package_type: the package type.
  • public_id: the public id.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#package_type","title":"package_type","text":"
@property\ndef package_type() -> PackageType\n

Get the package type.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#public_id","title":"public_id","text":"
@property\ndef public_id() -> PublicId\n

Get the public id.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#author_1","title":"author","text":"
@property\ndef author() -> str\n

Get the author of the package.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#name_1","title":"name","text":"
@property\ndef name() -> str\n

Get the name of the package.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#version_1","title":"version","text":"
@property\ndef version() -> str\n

Get the version of the package.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#package_prefix","title":"package_prefix","text":"
@property\ndef package_prefix() -> Tuple[PackageType, str, str]\n

Get the package identifier without the version.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#from_uri_path_1","title":"from_uri_path","text":"
@classmethod\ndef from_uri_path(cls, package_id_uri_path: str) -> \"PackageId\"\n

Initialize the package id from the string.

str(PackageId.from_uri_path(\"skill/author/package_name/0.1.0\")) '(skill, author/package_name:0.1.0)'

A bad formatted input raises value error:

PackageId.from_uri_path(\"very/bad/formatted:input\") Traceback (most recent call last): ... ValueError: Input 'very/bad/formatted:input' is not well formatted.

Arguments:

  • package_id_uri_path: the package id in uri path string format.

Raises:

  • ValueError: if the string in input is not well formatted.

Returns:

the package id object.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#to_uri_path_1","title":"to_uri_path","text":"
@property\ndef to_uri_path() -> str\n

Turn the package id into a uri path string.

Returns:

uri path string

"},{"location":"aea-framework-documentation/api/configurations/data_types/#__hash___1","title":"__hash__","text":"
def __hash__() -> int\n

Get the hash.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#__str___4","title":"__str__","text":"
def __str__() -> str\n

Get the string representation.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#__repr___1","title":"__repr__","text":"
def __repr__() -> str\n

Get the object representation in string.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#__eq___2","title":"__eq__","text":"
def __eq__(other: Any) -> bool\n

Compare with another object.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#__lt___2","title":"__lt__","text":"
def __lt__(other: Any) -> bool\n

Compare two public ids.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#componentid-objects","title":"ComponentId Objects","text":"
class ComponentId(PackageId)\n

Class to represent a component identifier.

A component id is a package id, but excludes the case when the package is an agent.

pacakge_id = PackageId(PackageType.PROTOCOL, PublicId(\"author\", \"name\", \"0.1.0\")) component_id = ComponentId(ComponentType.PROTOCOL, PublicId(\"author\", \"name\", \"0.1.0\")) pacakge_id == component_id True

component_id2 = ComponentId(ComponentType.PROTOCOL, PublicId(\"author\", \"name\", \"0.1.1\")) pacakge_id == component_id2 False

"},{"location":"aea-framework-documentation/api/configurations/data_types/#__init___3","title":"__init__","text":"
def __init__(component_type: Union[ComponentType, str],\npublic_id: PublicId) -> None\n

Initialize the component id.

Arguments:

  • component_type: the component type.
  • public_id: the public id.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#component_type","title":"component_type","text":"
@property\ndef component_type() -> ComponentType\n

Get the component type.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#component_prefix","title":"component_prefix","text":"
@property\ndef component_prefix() -> PackageIdPrefix\n

Get the component identifier without the version.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#same_prefix_1","title":"same_prefix","text":"
def same_prefix(other: \"ComponentId\") -> bool\n

Check if the other component id has the same type, author and name of this.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#prefix_import_path","title":"prefix_import_path","text":"
@property\ndef prefix_import_path() -> str\n

Get the prefix import path for this component.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#json_2","title":"json","text":"
@property\ndef json() -> Dict\n

Get the JSON representation.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#from_json_2","title":"from_json","text":"
@classmethod\ndef from_json(cls, json_data: Dict) -> \"ComponentId\"\n

Create component id from json data.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#pypipackagename-objects","title":"PyPIPackageName Objects","text":"
class PyPIPackageName(RegexConstrainedString)\n

A PyPI Package name.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#gitref-objects","title":"GitRef Objects","text":"
class GitRef(RegexConstrainedString)\n

A Git reference.

It can be a branch name, a commit hash or a tag.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#dependency-objects","title":"Dependency Objects","text":"
class Dependency()\n

This class represents a PyPI dependency.

It contains the following information: - version: a version specifier(s) (e.g. '==0.1.0'). - index: the PyPI index where to download the package from (default: https://pypi.org) - git: the URL to the Git repository (e.g. https://github.com/fetchai/agents-aea.git) - ref: either the branch name, the tag, the commit number or a Git reference (default: 'master'.)

If the 'git' field is set, the 'version' field will be ignored. These fields will be forwarded to the 'pip' command.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#__init___4","title":"__init__","text":"
def __init__(name: Union[PyPIPackageName, str],\nversion: Union[str, SpecifierSet] = \"\",\nindex: Optional[str] = None,\ngit: Optional[str] = None,\nref: Optional[Union[GitRef, str]] = None) -> None\n

Initialize a PyPI dependency.

Arguments:

  • name: the package name.
  • version: the specifier set object
  • index: the URL to the PyPI server.
  • git: the URL to a git repository.
  • ref: the Git reference (branch/commit/tag).

"},{"location":"aea-framework-documentation/api/configurations/data_types/#name_2","title":"name","text":"
@property\ndef name() -> str\n

Get the name.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#version_2","title":"version","text":"
@property\ndef version() -> str\n

Get the version.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#index","title":"index","text":"
@property\ndef index() -> Optional[str]\n

Get the index.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#git","title":"git","text":"
@property\ndef git() -> Optional[str]\n

Get the git.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#ref","title":"ref","text":"
@property\ndef ref() -> Optional[str]\n

Get the ref.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#from_json_3","title":"from_json","text":"
@classmethod\ndef from_json(cls, obj: Dict[str, Dict[str, str]]) -> \"Dependency\"\n

Parse a dependency object from a dictionary.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#to_json","title":"to_json","text":"
def to_json() -> Dict[str, Dict[str, str]]\n

Transform the object to JSON.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#get_pip_install_args","title":"get_pip_install_args","text":"
def get_pip_install_args() -> List[str]\n

Get 'pip install' arguments.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#__str___5","title":"__str__","text":"
def __str__() -> str\n

Get the string representation.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#__eq___3","title":"__eq__","text":"
def __eq__(other: Any) -> bool\n

Compare with another object.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#dependencies","title":"Dependencies","text":"

A dictionary from package name to dependency data structure (see above). The package name must satisfy the constraints on Python packages names.

The main advantage of having a dictionary is that we implicitly filter out dependency duplicates. We cannot have two items with the same package name since the keys of a YAML object form a set.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#crudcollection-objects","title":"CRUDCollection Objects","text":"
class CRUDCollection(Generic[T])\n

Interface of a CRUD collection.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#__init___5","title":"__init__","text":"
def __init__() -> None\n

Instantiate a CRUD collection.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#create","title":"create","text":"
def create(item_id: str, item: T) -> None\n

Add an item.

Arguments:

  • item_id: the item id.
  • item: the item to be added.

Raises:

  • ValueError: if the item with the same id is already in the collection.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#read","title":"read","text":"
def read(item_id: str) -> Optional[T]\n

Get an item by its name.

Arguments:

  • item_id: the item id.

Returns:

the associated item, or None if the item id is not present.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#update","title":"update","text":"
def update(item_id: str, item: T) -> None\n

Update an existing item.

Arguments:

  • item_id: the item id.
  • item: the item to be added.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#delete","title":"delete","text":"
def delete(item_id: str) -> None\n

Delete an item.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#read_all","title":"read_all","text":"
def read_all() -> List[Tuple[str, T]]\n

Read all the items.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#keys","title":"keys","text":"
def keys() -> Set[str]\n

Get the set of keys.

"},{"location":"aea-framework-documentation/api/configurations/manager/","title":"Manager","text":""},{"location":"aea-framework-documentation/api/configurations/manager/#aeaconfigurationsmanager","title":"aea.configurations.manager","text":"

Implementation of the AgentConfigManager.

"},{"location":"aea-framework-documentation/api/configurations/manager/#variabledoesnotexist-objects","title":"VariableDoesNotExist Objects","text":"
class VariableDoesNotExist(ValueError)\n

Variable does not exist in a config exception.

"},{"location":"aea-framework-documentation/api/configurations/manager/#handle_dotted_path","title":"handle_dotted_path","text":"
def handle_dotted_path(\nvalue: str,\nauthor: str,\naea_project_path: Union[str, Path] = \".\"\n) -> Tuple[List[str], Path, ConfigLoader, Optional[ComponentId]]\n

Separate the path between path to resource and json path to attribute.

Allowed values: 'agent.an_attribute_name' 'protocols.my_protocol.an_attribute_name' 'connections.my_connection.an_attribute_name' 'contracts.my_contract.an_attribute_name' 'skills.my_skill.an_attribute_name' 'vendor.author.[protocols|contracts|connections|skills].package_name.attribute_name

We also return the component id to retrieve the configuration of a specific component. Notice that at this point we don't know the version, so we put 'latest' as version, but later we will ignore it because we will filter with only the component prefix (i.e. the triple type, author and name).

Arguments:

  • value: dotted path.
  • author: the author string.
  • aea_project_path: project path

Returns:

Tuple[list of settings dict keys, filepath, config loader, component id].

"},{"location":"aea-framework-documentation/api/configurations/manager/#find_component_directory_from_component_id","title":"find_component_directory_from_component_id","text":"
def find_component_directory_from_component_id(\naea_project_directory: Path, component_id: ComponentId) -> Path\n

Find a component directory from component id.

"},{"location":"aea-framework-documentation/api/configurations/manager/#agentconfigmanager-objects","title":"AgentConfigManager Objects","text":"
class AgentConfigManager()\n

AeaConfig manager.

"},{"location":"aea-framework-documentation/api/configurations/manager/#__init__","title":"__init__","text":"
def __init__(agent_config: AgentConfig,\naea_project_directory: Union[str, Path],\nenv_vars_friendly: bool = False) -> None\n

Init manager.

Arguments:

  • agent_config: AgentConfig to manage.
  • aea_project_directory: directory where project for agent_config placed.
  • env_vars_friendly: whether or not it is env vars friendly

"},{"location":"aea-framework-documentation/api/configurations/manager/#load_component_configuration","title":"load_component_configuration","text":"
def load_component_configuration(\ncomponent_id: ComponentId,\nskip_consistency_check: bool = True) -> ComponentConfiguration\n

Load component configuration from the project directory.

Arguments:

  • component_id: Id of the component to load config for.
  • skip_consistency_check: bool.

Returns:

ComponentConfiguration

"},{"location":"aea-framework-documentation/api/configurations/manager/#agent_config_file_path","title":"agent_config_file_path","text":"
@property\ndef agent_config_file_path() -> Path\n

Return agent config file path.

"},{"location":"aea-framework-documentation/api/configurations/manager/#load","title":"load","text":"
@classmethod\ndef load(cls,\naea_project_path: Union[Path, str],\nsubstitude_env_vars: bool = False) -> \"AgentConfigManager\"\n

Create AgentConfigManager instance from agent project path.

"},{"location":"aea-framework-documentation/api/configurations/manager/#set_variable","title":"set_variable","text":"
def set_variable(path: VariablePath, value: JSON_TYPES) -> None\n

Set config variable.

Arguments:

  • path: str dotted path or List[Union[ComponentId, str]]
  • value: one of the json friendly objects.

"},{"location":"aea-framework-documentation/api/configurations/manager/#get_variable","title":"get_variable","text":"
def get_variable(path: VariablePath) -> JSON_TYPES\n

Set config variable.

Arguments:

  • path: str dotted path or List[Union[ComponentId, str]]

Returns:

json friendly value.

"},{"location":"aea-framework-documentation/api/configurations/manager/#update_config","title":"update_config","text":"
def update_config(overrides: Dict) -> None\n

Apply overrides for agent config.

Validates and applies agent config and component overrides. Does not save it on the disc!

Arguments:

  • overrides: overridden values dictionary

Returns:

None

"},{"location":"aea-framework-documentation/api/configurations/manager/#validate_current_config","title":"validate_current_config","text":"
def validate_current_config() -> None\n

Check is current config valid.

"},{"location":"aea-framework-documentation/api/configurations/manager/#json","title":"json","text":"
@property\ndef json() -> Dict\n

Return current agent config json representation.

"},{"location":"aea-framework-documentation/api/configurations/manager/#dump_config","title":"dump_config","text":"
def dump_config() -> None\n

Save agent config on the disc.

"},{"location":"aea-framework-documentation/api/configurations/manager/#verify_private_keys","title":"verify_private_keys","text":"
@classmethod\ndef verify_private_keys(\ncls,\naea_project_path: Union[Path, str],\nprivate_key_helper: Callable[[AgentConfig, Path, Optional[str]], None],\nsubstitude_env_vars: bool = False,\npassword: Optional[str] = None) -> \"AgentConfigManager\"\n

Verify private keys.

Does not saves the config! Use AgentConfigManager.dump_config()

Arguments:

  • aea_project_path: path to an AEA project.
  • private_key_helper: private_key_helper is a function that use agent config to check the keys
  • substitude_env_vars: replace env vars with values, does not dump config
  • password: the password to encrypt/decrypt the private key.

Returns:

the agent configuration manager.

"},{"location":"aea-framework-documentation/api/configurations/manager/#get_overridables","title":"get_overridables","text":"
def get_overridables() -> Tuple[Dict, Dict[ComponentId, Dict]]\n

Get config overridables.

"},{"location":"aea-framework-documentation/api/configurations/pypi/","title":"Pypi","text":""},{"location":"aea-framework-documentation/api/configurations/pypi/#aeaconfigurationspypi","title":"aea.configurations.pypi","text":"

This module contains a checker for PyPI version consistency.

"},{"location":"aea-framework-documentation/api/configurations/pypi/#and_","title":"and_","text":"
def and_(s1: SpecifierSet, s2: SpecifierSet) -> SpecifierSet\n

Do the and between two specifier sets.

"},{"location":"aea-framework-documentation/api/configurations/pypi/#is_satisfiable","title":"is_satisfiable","text":"
def is_satisfiable(specifier_set: SpecifierSet) -> bool\n

Check if the specifier set is satisfiable.

Satisfiable means that there exists a version number that satisfies all the constraints. It is worth noticing that it doesn't mean that that version number with that package actually exists.

from packaging.specifiers import SpecifierSet

The specifier set \">0.9, ==1.0\" is satisfiable: the version number \"1.0\" satisfies the constraints

s1 = SpecifierSet(\">0.9,==1.0\") \"1.0\" in s1 True is_satisfiable(s1) True

The specifier set \"==1.0, >1.1\" is not satisfiable:

s1 = SpecifierSet(\"==1.0,>1.1\") is_satisfiable(s1) False

For other details, please refer to PEP440:

https://www.python.org/dev/peps/pep-0440\n

Arguments:

  • specifier_set: the specifier set.

Returns:

False if the constraints are surely non-satisfiable, True if we don't know.

"},{"location":"aea-framework-documentation/api/configurations/pypi/#is_simple_dep","title":"is_simple_dep","text":"
def is_simple_dep(dep: Dependency) -> bool\n

Check if it is a simple dependency.

Namely, if it has no field specified, or only the 'version' field set.

Arguments:

  • dep: the dependency

Returns:

whether it is a simple dependency or not

"},{"location":"aea-framework-documentation/api/configurations/pypi/#to_set_specifier","title":"to_set_specifier","text":"
def to_set_specifier(dep: Dependency) -> SpecifierSet\n

Get the set specifier. It assumes to be a simple dependency (see above).

"},{"location":"aea-framework-documentation/api/configurations/pypi/#merge_dependencies","title":"merge_dependencies","text":"
def merge_dependencies(dep1: Dependencies, dep2: Dependencies) -> Dependencies\n

Merge two groups of dependencies.

If some of them are not \"simple\" (see above), and there is no risk of conflict because there is no other package with the same name, we leave them; otherwise we raise an error.

Arguments:

  • dep1: the first operand
  • dep2: the second operand.

Returns:

the merged dependencies.

"},{"location":"aea-framework-documentation/api/configurations/pypi/#merge_dependencies_list","title":"merge_dependencies_list","text":"
def merge_dependencies_list(*deps: Dependencies) -> Dependencies\n

Merge a list of dependencies.

Arguments:

  • deps: the list of dependencies

Returns:

the merged dependencies.

"},{"location":"aea-framework-documentation/api/configurations/utils/","title":"Utils","text":""},{"location":"aea-framework-documentation/api/configurations/utils/#aeaconfigurationsutils","title":"aea.configurations.utils","text":"

AEA configuration utils.

"},{"location":"aea-framework-documentation/api/configurations/utils/#replace_component_ids","title":"replace_component_ids","text":"
@singledispatch\ndef replace_component_ids(\n_arg: PackageConfiguration,\n_replacements: Dict[ComponentType, Dict[PublicId, PublicId]]) -> None\n

Update public id references in a package configuration.

This depends on the actual configuration being considered.

"},{"location":"aea-framework-documentation/api/configurations/utils/#_","title":"_","text":"
@replace_component_ids.register(AgentConfig)  # type: ignore\ndef _(arg: AgentConfig, replacements: Dict[ComponentType,\nDict[PublicId, PublicId]]) -> None\n

Replace references in agent configuration.

It breaks down in: 1) replace public ids in 'protocols', 'connections', 'contracts' and 'skills'; 2) replace public ids in default routing; 3) replace public id of default connection; 4) replace custom component configurations.

Arguments:

  • arg: the agent configuration.
  • replacements: the replacement mapping.

"},{"location":"aea-framework-documentation/api/configurations/utils/#__1","title":"_","text":"
@replace_component_ids.register(ProtocolConfig)  # type: ignore\ndef _(_arg: ProtocolConfig,\n_replacements: Dict[ComponentType, Dict[PublicId, PublicId]]) -> None\n

Do nothing - protocols have no references.

"},{"location":"aea-framework-documentation/api/configurations/utils/#__2","title":"_","text":"
@replace_component_ids.register(ConnectionConfig)  # type: ignore\ndef _(arg: ConnectionConfig,\nreplacements: Dict[ComponentType, Dict[PublicId, PublicId]]) -> None\n

Replace references in a connection configuration.

"},{"location":"aea-framework-documentation/api/configurations/utils/#__3","title":"_","text":"
@replace_component_ids.register(ContractConfig)  # type: ignore\ndef _(_arg: ContractConfig,\n_replacements: Dict[ComponentType, Dict[PublicId, PublicId]]) -> None\n

Do nothing - contracts have no references.

"},{"location":"aea-framework-documentation/api/configurations/utils/#__4","title":"_","text":"
@replace_component_ids.register(SkillConfig)  # type: ignore\ndef _(arg: SkillConfig, replacements: Dict[ComponentType,\nDict[PublicId, PublicId]]) -> None\n

Replace references in a skill configuration.

"},{"location":"aea-framework-documentation/api/configurations/utils/#get_latest_component_id_from_prefix","title":"get_latest_component_id_from_prefix","text":"
def get_latest_component_id_from_prefix(\nagent_config: AgentConfig,\ncomponent_prefix: PackageIdPrefix) -> Optional[ComponentId]\n

Get component id with the greatest version in an agent configuration given its prefix.

Arguments:

  • agent_config: the agent configuration.
  • component_prefix: the package prefix.

Returns:

the package id with the greatest version, or None if not found.

"},{"location":"aea-framework-documentation/api/configurations/validation/","title":"Validation","text":""},{"location":"aea-framework-documentation/api/configurations/validation/#aeaconfigurationsvalidation","title":"aea.configurations.validation","text":"

Implementation of the configuration validation.

"},{"location":"aea-framework-documentation/api/configurations/validation/#make_jsonschema_base_uri","title":"make_jsonschema_base_uri","text":"
def make_jsonschema_base_uri(base_uri_path: Path) -> str\n

Make the JSONSchema base URI, cross-platform.

Arguments:

  • base_uri_path: the path to the base directory.

Returns:

the string in URI form.

"},{"location":"aea-framework-documentation/api/configurations/validation/#extrapropertieserror-objects","title":"ExtraPropertiesError Objects","text":"
class ExtraPropertiesError(ValueError)\n

Extra properties exception.

"},{"location":"aea-framework-documentation/api/configurations/validation/#__str__","title":"__str__","text":"
def __str__() -> str\n

Get string representation of the object.

"},{"location":"aea-framework-documentation/api/configurations/validation/#__repr__","title":"__repr__","text":"
def __repr__() -> str\n

Get string representation of the object.

"},{"location":"aea-framework-documentation/api/configurations/validation/#customtypechecker-objects","title":"CustomTypeChecker Objects","text":"
class CustomTypeChecker(TypeChecker)\n

Custom type checker to handle env variables.

"},{"location":"aea-framework-documentation/api/configurations/validation/#is_type","title":"is_type","text":"
def is_type(instance, type) -> bool\n

Check is instance of type.

"},{"location":"aea-framework-documentation/api/configurations/validation/#own_additional_properties","title":"own_additional_properties","text":"
def own_additional_properties(validator, aP, instance, schema) -> Iterator\n

Additional properties validator.

"},{"location":"aea-framework-documentation/api/configurations/validation/#configvalidator-objects","title":"ConfigValidator Objects","text":"
class ConfigValidator()\n

Configuration validator implementation.

"},{"location":"aea-framework-documentation/api/configurations/validation/#__init__","title":"__init__","text":"
def __init__(schema_filename: str, env_vars_friendly: bool = False) -> None\n

Initialize the parser for configuration files.

Arguments:

  • schema_filename: the path to the JSON-schema file in 'aea/configurations/schemas'.
  • env_vars_friendly: whether or not it is env var friendly.

"},{"location":"aea-framework-documentation/api/configurations/validation/#split_component_id_and_config","title":"split_component_id_and_config","text":"
@staticmethod\ndef split_component_id_and_config(\ncomponent_index: int,\ncomponent_configuration_json: Dict) -> ComponentId\n

Split component id and configuration.

Arguments:

  • component_index: the position of the component configuration in the agent config file..
  • component_configuration_json: the JSON object to process.

Raises:

  • ValueError: if the component id cannot be extracted.

Returns:

the component id and the configuration object.

"},{"location":"aea-framework-documentation/api/configurations/validation/#validate_component_configuration","title":"validate_component_configuration","text":"
@classmethod\ndef validate_component_configuration(cls,\ncomponent_id: ComponentId,\nconfiguration: Dict,\nenv_vars_friendly: bool = False) -> None\n

Validate the component configuration of an agent configuration file.

This check is to detect inconsistencies in the specified fields.

Arguments:

  • component_id: the component id.
  • configuration: the configuration dictionary.
  • env_vars_friendly: bool, if set True, will not raise errors over the env variable definitions.

Raises:

  • ValueError: if the configuration is not valid.

"},{"location":"aea-framework-documentation/api/configurations/validation/#validate","title":"validate","text":"
def validate(json_data: Dict) -> None\n

Validate a JSON object against the right JSON schema.

Arguments:

  • json_data: the JSON data.

"},{"location":"aea-framework-documentation/api/configurations/validation/#validate_agent_components_configuration","title":"validate_agent_components_configuration","text":"
def validate_agent_components_configuration(\ncomponent_configurations: Dict) -> None\n

Validate agent component configurations overrides.

Arguments:

  • component_configurations: the component configurations to validate.

"},{"location":"aea-framework-documentation/api/configurations/validation/#required_fields","title":"required_fields","text":"
@property\ndef required_fields() -> List[str]\n

Get the required fields.

Returns:

list of required fields.

"},{"location":"aea-framework-documentation/api/configurations/validation/#validate_data_with_pattern","title":"validate_data_with_pattern","text":"
def validate_data_with_pattern(data: dict,\npattern: dict,\nexcludes: Optional[List[Tuple[str]]] = None,\nskip_env_vars: bool = False) -> List[str]\n

Validate data dict with pattern dict for attributes present and type match.

Arguments:

  • data: data dict to validate
  • pattern: dict with pattern to check over
  • excludes: list of tuples of str of paths to be skipped during the check
  • skip_env_vars: is set True will not check data type over env variables.

Returns:

list of str with error descriptions

"},{"location":"aea-framework-documentation/api/configurations/validation/#filter_data","title":"filter_data","text":"
def filter_data(base: Any, updates: Any) -> Any\n

Return difference in values or SAME_MARK object if values are the same.

"},{"location":"aea-framework-documentation/api/connections/base/","title":"Base","text":""},{"location":"aea-framework-documentation/api/connections/base/#aeaconnectionsbase","title":"aea.connections.base","text":"

The base connection package.

"},{"location":"aea-framework-documentation/api/connections/base/#connectionstates-objects","title":"ConnectionStates Objects","text":"
class ConnectionStates(Enum)\n

Connection states enum.

"},{"location":"aea-framework-documentation/api/connections/base/#connection-objects","title":"Connection Objects","text":"
class Connection(Component, ABC)\n

Abstract definition of a connection.

"},{"location":"aea-framework-documentation/api/connections/base/#__init__","title":"__init__","text":"
def __init__(configuration: ConnectionConfig,\ndata_dir: str,\nidentity: Optional[Identity] = None,\ncrypto_store: Optional[CryptoStore] = None,\nrestricted_to_protocols: Optional[Set[PublicId]] = None,\nexcluded_protocols: Optional[Set[PublicId]] = None,\n**kwargs: Any) -> None\n

Initialize the connection.

The configuration must be specified if and only if the following parameters are None: connection_id, excluded_protocols or restricted_to_protocols.

Arguments:

  • configuration: the connection configuration.
  • data_dir: directory where to put local files.
  • identity: the identity object held by the agent.
  • crypto_store: the crypto store for encrypted communication.
  • restricted_to_protocols: the set of protocols ids of the only supported protocols for this connection.
  • excluded_protocols: the set of protocols ids that we want to exclude for this connection.
  • kwargs: keyword arguments passed to component base

"},{"location":"aea-framework-documentation/api/connections/base/#loop","title":"loop","text":"
@property\ndef loop() -> asyncio.AbstractEventLoop\n

Get the event loop.

"},{"location":"aea-framework-documentation/api/connections/base/#address","title":"address","text":"
@property\ndef address() -> \"Address\"\n

Get the address.

"},{"location":"aea-framework-documentation/api/connections/base/#crypto_store","title":"crypto_store","text":"
@property\ndef crypto_store() -> CryptoStore\n

Get the crypto store.

"},{"location":"aea-framework-documentation/api/connections/base/#has_crypto_store","title":"has_crypto_store","text":"
@property\ndef has_crypto_store() -> bool\n

Check if the connection has the crypto store.

"},{"location":"aea-framework-documentation/api/connections/base/#data_dir","title":"data_dir","text":"
@property\ndef data_dir() -> str\n

Get the data directory.

"},{"location":"aea-framework-documentation/api/connections/base/#component_type","title":"component_type","text":"
@property\ndef component_type() -> ComponentType\n

Get the component type.

"},{"location":"aea-framework-documentation/api/connections/base/#configuration","title":"configuration","text":"
@property\ndef configuration() -> ConnectionConfig\n

Get the connection configuration.

"},{"location":"aea-framework-documentation/api/connections/base/#restricted_to_protocols","title":"restricted_to_protocols","text":"
@property\ndef restricted_to_protocols() -> Set[PublicId]\n

Get the ids of the protocols this connection is restricted to.

"},{"location":"aea-framework-documentation/api/connections/base/#excluded_protocols","title":"excluded_protocols","text":"
@property\ndef excluded_protocols() -> Set[PublicId]\n

Get the ids of the excluded protocols for this connection.

"},{"location":"aea-framework-documentation/api/connections/base/#state","title":"state","text":"
@property\ndef state() -> ConnectionStates\n

Get the connection status.

"},{"location":"aea-framework-documentation/api/connections/base/#state_1","title":"state","text":"
@state.setter\ndef state(value: ConnectionStates) -> None\n

Set the connection status.

"},{"location":"aea-framework-documentation/api/connections/base/#connect","title":"connect","text":"
@abstractmethod\nasync def connect() -> None\n

Set up the connection.

"},{"location":"aea-framework-documentation/api/connections/base/#disconnect","title":"disconnect","text":"
@abstractmethod\nasync def disconnect() -> None\n

Tear down the connection.

"},{"location":"aea-framework-documentation/api/connections/base/#send","title":"send","text":"
@abstractmethod\nasync def send(envelope: \"Envelope\") -> None\n

Send an envelope.

Arguments:

  • envelope: the envelope to send.

Returns:

None

"},{"location":"aea-framework-documentation/api/connections/base/#receive","title":"receive","text":"
@abstractmethod\nasync def receive(*args: Any, **kwargs: Any) -> Optional[\"Envelope\"]\n

Receive an envelope.

Arguments:

  • args: positional arguments
  • kwargs: keyword arguments

Returns:

the received envelope, or None if an error occurred.

"},{"location":"aea-framework-documentation/api/connections/base/#from_dir","title":"from_dir","text":"
@classmethod\ndef from_dir(cls, directory: str, identity: Identity,\ncrypto_store: CryptoStore, data_dir: str,\n**kwargs: Any) -> \"Connection\"\n

Load the connection from a directory.

Arguments:

  • directory: the directory to the connection package.
  • identity: the identity object.
  • crypto_store: object to access the connection crypto objects.
  • data_dir: the assets directory.
  • kwargs: keyword arguments passed to connection base

Returns:

the connection object.

"},{"location":"aea-framework-documentation/api/connections/base/#from_config","title":"from_config","text":"
@classmethod\ndef from_config(cls, configuration: ConnectionConfig, identity: Identity,\ncrypto_store: CryptoStore, data_dir: str,\n**kwargs: Any) -> \"Connection\"\n

Load a connection from a configuration.

Arguments:

  • configuration: the connection configuration.
  • identity: the identity object.
  • crypto_store: object to access the connection crypto objects.
  • data_dir: the directory of the AEA project data.
  • kwargs: keyword arguments passed to component base

Returns:

an instance of the concrete connection class.

"},{"location":"aea-framework-documentation/api/connections/base/#is_connected","title":"is_connected","text":"
@property\ndef is_connected() -> bool\n

Return is connected state.

"},{"location":"aea-framework-documentation/api/connections/base/#is_connecting","title":"is_connecting","text":"
@property\ndef is_connecting() -> bool\n

Return is connecting state.

"},{"location":"aea-framework-documentation/api/connections/base/#is_disconnected","title":"is_disconnected","text":"
@property\ndef is_disconnected() -> bool\n

Return is disconnected state.

"},{"location":"aea-framework-documentation/api/connections/base/#basesyncconnection-objects","title":"BaseSyncConnection Objects","text":"
class BaseSyncConnection(Connection)\n

Base sync connection class to write connections with sync code.

"},{"location":"aea-framework-documentation/api/connections/base/#__init___1","title":"__init__","text":"
def __init__(configuration: ConnectionConfig,\ndata_dir: str,\nidentity: Optional[Identity] = None,\ncrypto_store: Optional[CryptoStore] = None,\nrestricted_to_protocols: Optional[Set[PublicId]] = None,\nexcluded_protocols: Optional[Set[PublicId]] = None,\n**kwargs: Any) -> None\n

Initialize the connection.

The configuration must be specified if and only if the following parameters are None: connection_id, excluded_protocols or restricted_to_protocols.

Arguments:

  • configuration: the connection configuration.
  • data_dir: directory where to put local files.
  • identity: the identity object held by the agent.
  • crypto_store: the crypto store for encrypted communication.
  • restricted_to_protocols: the set of protocols ids of the only supported protocols for this connection.
  • excluded_protocols: the set of protocols ids that we want to exclude for this connection.
  • kwargs: keyword arguments passed to connection base

"},{"location":"aea-framework-documentation/api/connections/base/#put_envelope","title":"put_envelope","text":"
def put_envelope(envelope: Optional[\"Envelope\"]) -> None\n

Put envelope in to the incoming queue.

"},{"location":"aea-framework-documentation/api/connections/base/#connect_1","title":"connect","text":"
async def connect() -> None\n

Connect connection.

"},{"location":"aea-framework-documentation/api/connections/base/#disconnect_1","title":"disconnect","text":"
async def disconnect() -> None\n

Disconnect connection.

"},{"location":"aea-framework-documentation/api/connections/base/#send_1","title":"send","text":"
async def send(envelope: \"Envelope\") -> None\n

Send envelope to connection.

"},{"location":"aea-framework-documentation/api/connections/base/#receive_1","title":"receive","text":"
async def receive(*args: Any, **kwargs: Any) -> Optional[\"Envelope\"]\n

Get an envelope from the connection.

"},{"location":"aea-framework-documentation/api/connections/base/#start_main","title":"start_main","text":"
def start_main() -> None\n

Start main function of the connection.

"},{"location":"aea-framework-documentation/api/connections/base/#main","title":"main","text":"
def main() -> None\n

Run main body of the connection in dedicated thread.

"},{"location":"aea-framework-documentation/api/connections/base/#on_connect","title":"on_connect","text":"
@abstractmethod\ndef on_connect() -> None\n

Run on connect method called.

"},{"location":"aea-framework-documentation/api/connections/base/#on_disconnect","title":"on_disconnect","text":"
@abstractmethod\ndef on_disconnect() -> None\n

Run on disconnect method called.

"},{"location":"aea-framework-documentation/api/connections/base/#on_send","title":"on_send","text":"
@abstractmethod\ndef on_send(envelope: \"Envelope\") -> None\n

Run on send method called.

"},{"location":"aea-framework-documentation/api/context/base/","title":"Context","text":""},{"location":"aea-framework-documentation/api/context/base/#aeacontextbase","title":"aea.context.base","text":"

This module contains the agent context class.

"},{"location":"aea-framework-documentation/api/context/base/#agentcontext-objects","title":"AgentContext Objects","text":"
class AgentContext()\n

Provide read access to relevant objects of the agent for the skills.

"},{"location":"aea-framework-documentation/api/context/base/#__init__","title":"__init__","text":"
def __init__(identity: Identity,\nconnection_status: MultiplexerStatus,\noutbox: OutBox,\ndecision_maker_message_queue: Queue,\ndecision_maker_handler_context: SimpleNamespace,\ntask_manager: TaskManager,\ndefault_ledger_id: str,\ncurrency_denominations: Dict[str, str],\ndefault_connection: Optional[PublicId],\ndefault_routing: Dict[PublicId, PublicId],\nsearch_service_address: Address,\ndecision_maker_address: Address,\ndata_dir: str,\nstorage_callable: Callable[[], Optional[Storage]] = lambda: None,\nsend_to_skill: Optional[Callable] = None,\n**kwargs: Any) -> None\n

Initialize an agent context.

Arguments:

  • identity: the identity object
  • connection_status: the connection status of the multiplexer
  • outbox: the outbox
  • decision_maker_message_queue: the (in) queue of the decision maker
  • decision_maker_handler_context: the decision maker's name space
  • task_manager: the task manager
  • default_ledger_id: the default ledger id
  • currency_denominations: mapping from ledger ids to currency denominations
  • default_connection: the default connection
  • default_routing: the default routing
  • search_service_address: the address of the search service
  • decision_maker_address: the address of the decision maker
  • data_dir: directory where to put local files.
  • storage_callable: function that returns optional storage attached to agent.
  • send_to_skill: callable for sending envelopes to skills.
  • kwargs: keyword arguments to be attached in the agent context namespace.

"},{"location":"aea-framework-documentation/api/context/base/#send_to_skill","title":"send_to_skill","text":"
def send_to_skill(message_or_envelope: Union[Message, Envelope],\ncontext: Optional[EnvelopeContext] = None) -> None\n

Send message or envelope to another skill.

If message passed it will be wrapped into envelope with optional envelope context.

Arguments:

  • message_or_envelope: envelope to send to another skill.
  • context: the optional envelope context

"},{"location":"aea-framework-documentation/api/context/base/#storage","title":"storage","text":"
@property\ndef storage() -> Optional[Storage]\n

Return storage instance if enabled in AEA.

"},{"location":"aea-framework-documentation/api/context/base/#data_dir","title":"data_dir","text":"
@property\ndef data_dir() -> str\n

Return assets directory.

"},{"location":"aea-framework-documentation/api/context/base/#shared_state","title":"shared_state","text":"
@property\ndef shared_state() -> Dict[str, Any]\n

Get the shared state dictionary.

The shared state is the only object which skills can use to exchange state directly. It is accessible (read and write) from all skills.

Returns:

dictionary of the shared state.

"},{"location":"aea-framework-documentation/api/context/base/#identity","title":"identity","text":"
@property\ndef identity() -> Identity\n

Get the identity.

"},{"location":"aea-framework-documentation/api/context/base/#agent_name","title":"agent_name","text":"
@property\ndef agent_name() -> str\n

Get agent name.

"},{"location":"aea-framework-documentation/api/context/base/#addresses","title":"addresses","text":"
@property\ndef addresses() -> Dict[str, Address]\n

Get addresses.

"},{"location":"aea-framework-documentation/api/context/base/#public_keys","title":"public_keys","text":"
@property\ndef public_keys() -> Dict[str, str]\n

Get public keys.

"},{"location":"aea-framework-documentation/api/context/base/#address","title":"address","text":"
@property\ndef address() -> Address\n

Get the default address.

"},{"location":"aea-framework-documentation/api/context/base/#public_key","title":"public_key","text":"
@property\ndef public_key() -> str\n

Get the default public key.

"},{"location":"aea-framework-documentation/api/context/base/#connection_status","title":"connection_status","text":"
@property\ndef connection_status() -> MultiplexerStatus\n

Get connection status of the multiplexer.

"},{"location":"aea-framework-documentation/api/context/base/#outbox","title":"outbox","text":"
@property\ndef outbox() -> OutBox\n

Get outbox.

"},{"location":"aea-framework-documentation/api/context/base/#decision_maker_message_queue","title":"decision_maker_message_queue","text":"
@property\ndef decision_maker_message_queue() -> Queue\n

Get decision maker queue.

"},{"location":"aea-framework-documentation/api/context/base/#decision_maker_handler_context","title":"decision_maker_handler_context","text":"
@property\ndef decision_maker_handler_context() -> SimpleNamespace\n

Get the decision maker handler context.

"},{"location":"aea-framework-documentation/api/context/base/#task_manager","title":"task_manager","text":"
@property\ndef task_manager() -> TaskManager\n

Get the task manager.

"},{"location":"aea-framework-documentation/api/context/base/#search_service_address","title":"search_service_address","text":"
@property\ndef search_service_address() -> Address\n

Get the address of the search service.

"},{"location":"aea-framework-documentation/api/context/base/#decision_maker_address","title":"decision_maker_address","text":"
@property\ndef decision_maker_address() -> Address\n

Get the address of the decision maker.

"},{"location":"aea-framework-documentation/api/context/base/#default_ledger_id","title":"default_ledger_id","text":"
@property\ndef default_ledger_id() -> str\n

Get the default ledger id.

"},{"location":"aea-framework-documentation/api/context/base/#currency_denominations","title":"currency_denominations","text":"
@property\ndef currency_denominations() -> Dict[str, str]\n

Get a dictionary mapping ledger ids to currency denominations.

"},{"location":"aea-framework-documentation/api/context/base/#default_connection","title":"default_connection","text":"
@property\ndef default_connection() -> Optional[PublicId]\n

Get the default connection.

"},{"location":"aea-framework-documentation/api/context/base/#default_routing","title":"default_routing","text":"
@property\ndef default_routing() -> Dict[PublicId, PublicId]\n

Get the default routing.

"},{"location":"aea-framework-documentation/api/context/base/#namespace","title":"namespace","text":"
@property\ndef namespace() -> SimpleNamespace\n

Get the agent context namespace.

"},{"location":"aea-framework-documentation/api/contracts/base/","title":"Base","text":""},{"location":"aea-framework-documentation/api/contracts/base/#aeacontractsbase","title":"aea.contracts.base","text":"

The base contract.

"},{"location":"aea-framework-documentation/api/contracts/base/#contract-objects","title":"Contract Objects","text":"
class Contract(Component)\n

Abstract definition of a contract.

"},{"location":"aea-framework-documentation/api/contracts/base/#__init__","title":"__init__","text":"
def __init__(contract_config: ContractConfig, **kwargs: Any) -> None\n

Initialize the contract.

Arguments:

  • contract_config: the contract configurations.
  • kwargs: the keyword arguments.

"},{"location":"aea-framework-documentation/api/contracts/base/#id","title":"id","text":"
@property\ndef id() -> PublicId\n

Get the name.

"},{"location":"aea-framework-documentation/api/contracts/base/#configuration","title":"configuration","text":"
@property\ndef configuration() -> ContractConfig\n

Get the configuration.

"},{"location":"aea-framework-documentation/api/contracts/base/#get_instance","title":"get_instance","text":"
@classmethod\ndef get_instance(cls,\nledger_api: LedgerApi,\ncontract_address: Optional[str] = None) -> Any\n

Get the instance.

Arguments:

  • ledger_api: the ledger api we are using.
  • contract_address: the contract address.

Returns:

the contract instance

"},{"location":"aea-framework-documentation/api/contracts/base/#from_dir","title":"from_dir","text":"
@classmethod\ndef from_dir(cls, directory: str, **kwargs: Any) -> \"Contract\"\n

Load the protocol from a directory.

Arguments:

  • directory: the directory to the skill package.
  • kwargs: the keyword arguments.

Returns:

the contract object.

"},{"location":"aea-framework-documentation/api/contracts/base/#from_config","title":"from_config","text":"
@classmethod\ndef from_config(cls, configuration: ContractConfig,\n**kwargs: Any) -> \"Contract\"\n

Load contract from configuration.

Arguments:

  • configuration: the contract configuration.
  • kwargs: the keyword arguments.

Returns:

the contract object.

"},{"location":"aea-framework-documentation/api/contracts/base/#get_deploy_transaction","title":"get_deploy_transaction","text":"
@classmethod\ndef get_deploy_transaction(cls, ledger_api: LedgerApi, deployer_address: str,\n**kwargs: Any) -> Optional[JSONLike]\n

Handler method for the 'GET_DEPLOY_TRANSACTION' requests.

Implement this method in the sub class if you want to handle the contract requests manually.

Arguments:

  • ledger_api: the ledger apis.
  • deployer_address: The address that will deploy the contract.
  • kwargs: keyword arguments.

Returns:

the tx

"},{"location":"aea-framework-documentation/api/contracts/base/#get_raw_transaction","title":"get_raw_transaction","text":"
@classmethod\ndef get_raw_transaction(cls, ledger_api: LedgerApi, contract_address: str,\n**kwargs: Any) -> Optional[JSONLike]\n

Handler method for the 'GET_RAW_TRANSACTION' requests.

Implement this method in the sub class if you want to handle the contract requests manually.

Arguments:

  • ledger_api: the ledger apis.
  • contract_address: the contract address.
  • kwargs: the keyword arguments.

Returns:

the tx # noqa: DAR202

"},{"location":"aea-framework-documentation/api/contracts/base/#get_raw_message","title":"get_raw_message","text":"
@classmethod\ndef get_raw_message(cls, ledger_api: LedgerApi, contract_address: str,\n**kwargs: Any) -> Optional[bytes]\n

Handler method for the 'GET_RAW_MESSAGE' requests.

Implement this method in the sub class if you want to handle the contract requests manually.

Arguments:

  • ledger_api: the ledger apis.
  • contract_address: the contract address.
  • kwargs: the keyword arguments.

Returns:

the tx # noqa: DAR202

"},{"location":"aea-framework-documentation/api/contracts/base/#get_state","title":"get_state","text":"
@classmethod\ndef get_state(cls, ledger_api: LedgerApi, contract_address: str,\n**kwargs: Any) -> Optional[JSONLike]\n

Handler method for the 'GET_STATE' requests.

Implement this method in the sub class if you want to handle the contract requests manually.

Arguments:

  • ledger_api: the ledger apis.
  • contract_address: the contract address.
  • kwargs: the keyword arguments.

Returns:

the tx # noqa: DAR202

"},{"location":"aea-framework-documentation/api/crypto/base/","title":"Base","text":""},{"location":"aea-framework-documentation/api/crypto/base/#aeacryptobase","title":"aea.crypto.base","text":"

Abstract module wrapping the public and private key cryptography and ledger api.

"},{"location":"aea-framework-documentation/api/crypto/base/#crypto-objects","title":"Crypto Objects","text":"
class Crypto(Generic[EntityClass], ABC)\n

Base class for a crypto object.

"},{"location":"aea-framework-documentation/api/crypto/base/#__init__","title":"__init__","text":"
def __init__(private_key_path: Optional[str] = None,\npassword: Optional[str] = None,\n**kwargs: Any) -> None\n

Initialize the crypto object.

The actual behaviour of this constructor is determined by the abstract methods 'generate_private_key()' and 'load_private_key_from_path(). Either way, the entity object will be accessible as a property.

Arguments:

  • private_key_path: the path to the private key. If None, the key will be generated by 'generate_private_key()'. If not None, the path will be processed by 'load_private_key_from_path()'.
  • password: the password to encrypt/decrypt the private key.
  • kwargs: keyword arguments.

"},{"location":"aea-framework-documentation/api/crypto/base/#generate_private_key","title":"generate_private_key","text":"
@classmethod\n@abstractmethod\ndef generate_private_key(cls) -> EntityClass\n

Generate a private key.

Returns:

the entity object. Implementation dependent.

"},{"location":"aea-framework-documentation/api/crypto/base/#load_private_key_from_path","title":"load_private_key_from_path","text":"
@classmethod\n@abstractmethod\ndef load_private_key_from_path(cls,\nfile_name: str,\npassword: Optional[str] = None) -> EntityClass\n

Load a private key in hex format for raw private key and json format for encrypted private key from a file.

Arguments:

  • file_name: the path to the hex/json file.
  • password: the password to encrypt/decrypt the private key.

Returns:

the entity object.

"},{"location":"aea-framework-documentation/api/crypto/base/#entity","title":"entity","text":"
@property\ndef entity() -> EntityClass\n

Return an entity object.

Returns:

an entity object

"},{"location":"aea-framework-documentation/api/crypto/base/#private_key","title":"private_key","text":"
@property\n@abstractmethod\ndef private_key() -> str\n

Return a private key.

Returns:

a private key string

"},{"location":"aea-framework-documentation/api/crypto/base/#public_key","title":"public_key","text":"
@property\n@abstractmethod\ndef public_key() -> str\n

Return a public key.

Returns:

a public key string

"},{"location":"aea-framework-documentation/api/crypto/base/#address","title":"address","text":"
@property\n@abstractmethod\ndef address() -> str\n

Return the address.

Returns:

an address string

"},{"location":"aea-framework-documentation/api/crypto/base/#sign_message","title":"sign_message","text":"
@abstractmethod\ndef sign_message(message: bytes, is_deprecated_mode: bool = False) -> str\n

Sign a message in bytes string form.

Arguments:

  • message: the message to be signed
  • is_deprecated_mode: if the deprecated signing is used

Returns:

signature of the message in string form

"},{"location":"aea-framework-documentation/api/crypto/base/#sign_transaction","title":"sign_transaction","text":"
@abstractmethod\ndef sign_transaction(transaction: JSONLike) -> JSONLike\n

Sign a transaction in dict form.

Arguments:

  • transaction: the transaction to be signed

Returns:

signed transaction

"},{"location":"aea-framework-documentation/api/crypto/base/#load","title":"load","text":"
@classmethod\ndef load(cls, private_key_file: str, password: Optional[str] = None) -> str\n

Load private key from file.

Arguments:

  • private_key_file: the file where the key is stored.
  • password: the password to encrypt/decrypt the private key.

Returns:

private_key in hex string format

"},{"location":"aea-framework-documentation/api/crypto/base/#dump","title":"dump","text":"
def dump(private_key_file: str, password: Optional[str] = None) -> None\n

Dump private key to file.

Arguments:

  • private_key_file: the file where the key is stored.
  • password: the password to encrypt/decrypt the private key.

"},{"location":"aea-framework-documentation/api/crypto/base/#encrypt","title":"encrypt","text":"
@abstractmethod\ndef encrypt(password: str) -> str\n

Encrypt the private key and return in json.

Arguments:

  • password: the password to decrypt.

Returns:

json string containing encrypted private key.

"},{"location":"aea-framework-documentation/api/crypto/base/#decrypt","title":"decrypt","text":"
@classmethod\n@abstractmethod\ndef decrypt(cls, keyfile_json: str, password: str) -> str\n

Decrypt the private key and return in raw form.

Arguments:

  • keyfile_json: json string containing encrypted private key.
  • password: the password to decrypt.

Returns:

the raw private key.

"},{"location":"aea-framework-documentation/api/crypto/base/#helper-objects","title":"Helper Objects","text":"
class Helper(ABC)\n

Interface for helper class usable as Mixin for LedgerApi or as standalone class.

"},{"location":"aea-framework-documentation/api/crypto/base/#is_transaction_settled","title":"is_transaction_settled","text":"
@staticmethod\n@abstractmethod\ndef is_transaction_settled(tx_receipt: JSONLike) -> bool\n

Check whether a transaction is settled or not.

Arguments:

  • tx_receipt: the receipt associated to the transaction.

Returns:

True if the transaction has been settled, False o/w.

"},{"location":"aea-framework-documentation/api/crypto/base/#is_transaction_valid","title":"is_transaction_valid","text":"
@staticmethod\n@abstractmethod\ndef is_transaction_valid(tx: JSONLike, seller: Address, client: Address,\ntx_nonce: str, amount: int) -> bool\n

Check whether a transaction is valid or not.

Arguments:

  • tx: the transaction.
  • seller: the address of the seller.
  • client: the address of the client.
  • tx_nonce: the transaction nonce.
  • amount: the amount we expect to get from the transaction.

Returns:

True if the random_message is equals to tx['input']

"},{"location":"aea-framework-documentation/api/crypto/base/#get_contract_address","title":"get_contract_address","text":"
@staticmethod\n@abstractmethod\ndef get_contract_address(tx_receipt: JSONLike) -> Optional[str]\n

Get the contract address from a transaction receipt.

Arguments:

  • tx_receipt: the transaction digest

Returns:

the contract address if successful

"},{"location":"aea-framework-documentation/api/crypto/base/#generate_tx_nonce","title":"generate_tx_nonce","text":"
@staticmethod\n@abstractmethod\ndef generate_tx_nonce(seller: Address, client: Address) -> str\n

Generate a unique hash to distinguish transactions with the same terms.

Arguments:

  • seller: the address of the seller.
  • client: the address of the client.

Returns:

return the hash in hex.

"},{"location":"aea-framework-documentation/api/crypto/base/#get_address_from_public_key","title":"get_address_from_public_key","text":"
@classmethod\n@abstractmethod\ndef get_address_from_public_key(cls, public_key: str) -> str\n

Get the address from the public key.

Arguments:

  • public_key: the public key

Returns:

str

"},{"location":"aea-framework-documentation/api/crypto/base/#recover_message","title":"recover_message","text":"
@classmethod\n@abstractmethod\ndef recover_message(cls,\nmessage: bytes,\nsignature: str,\nis_deprecated_mode: bool = False) -> Tuple[Address, ...]\n

Recover the addresses from the hash.

Arguments:

  • message: the message we expect
  • signature: the transaction signature
  • is_deprecated_mode: if the deprecated signing was used

Returns:

the recovered addresses

"},{"location":"aea-framework-documentation/api/crypto/base/#recover_public_keys_from_message","title":"recover_public_keys_from_message","text":"
@classmethod\n@abstractmethod\ndef recover_public_keys_from_message(\ncls,\nmessage: bytes,\nsignature: str,\nis_deprecated_mode: bool = False) -> Tuple[str, ...]\n

Get the public key used to produce the signature of the message

Arguments:

  • message: raw bytes used to produce signature
  • signature: signature of the message
  • is_deprecated_mode: if the deprecated signing was used

Returns:

the recovered public keys

"},{"location":"aea-framework-documentation/api/crypto/base/#get_hash","title":"get_hash","text":"
@staticmethod\n@abstractmethod\ndef get_hash(message: bytes) -> str\n

Get the hash of a message.

Arguments:

  • message: the message to be hashed.

Returns:

the hash of the message.

"},{"location":"aea-framework-documentation/api/crypto/base/#is_valid_address","title":"is_valid_address","text":"
@classmethod\n@abstractmethod\ndef is_valid_address(cls, address: Address) -> bool\n

Check if the address is valid.

Arguments:

  • address: the address to validate

"},{"location":"aea-framework-documentation/api/crypto/base/#load_contract_interface","title":"load_contract_interface","text":"
@classmethod\n@abstractmethod\ndef load_contract_interface(cls, file_path: Path) -> Dict[str, str]\n

Load contract interface.

Arguments:

  • file_path: the file path to the interface

Returns:

the interface

"},{"location":"aea-framework-documentation/api/crypto/base/#ledgerapi-objects","title":"LedgerApi Objects","text":"
class LedgerApi(Helper, ABC)\n

Interface for ledger APIs.

"},{"location":"aea-framework-documentation/api/crypto/base/#api","title":"api","text":"
@property\n@abstractmethod\ndef api() -> Any\n

Get the underlying API object.

This can be used for low-level operations with the concrete ledger APIs. If there is no such object, return None.

"},{"location":"aea-framework-documentation/api/crypto/base/#get_balance","title":"get_balance","text":"
@abstractmethod\ndef get_balance(address: Address) -> Optional[int]\n

Get the balance of a given account.

This usually takes the form of a web request to be waited synchronously.

Arguments:

  • address: the address.

Returns:

the balance.

"},{"location":"aea-framework-documentation/api/crypto/base/#get_state","title":"get_state","text":"
@abstractmethod\ndef get_state(callable_name: str, *args: Any,\n**kwargs: Any) -> Optional[JSONLike]\n

Call a specified function on the underlying ledger API.

This usually takes the form of a web request to be waited synchronously.

Arguments:

  • callable_name: the name of the API function to be called.
  • args: the positional arguments for the API function.
  • kwargs: the keyword arguments for the API function.

Returns:

the ledger API response.

"},{"location":"aea-framework-documentation/api/crypto/base/#get_transfer_transaction","title":"get_transfer_transaction","text":"
@abstractmethod\ndef get_transfer_transaction(sender_address: Address,\ndestination_address: Address, amount: int,\ntx_fee: int, tx_nonce: str,\n**kwargs: Any) -> Optional[JSONLike]\n

Submit a transfer transaction to the ledger.

Arguments:

  • sender_address: the sender address of the payer.
  • destination_address: the destination address of the payee.
  • amount: the amount of wealth to be transferred.
  • tx_fee: the transaction fee.
  • tx_nonce: verifies the authenticity of the tx
  • kwargs: the keyword arguments.

Returns:

the transfer transaction

"},{"location":"aea-framework-documentation/api/crypto/base/#send_signed_transaction","title":"send_signed_transaction","text":"
@abstractmethod\ndef send_signed_transaction(tx_signed: JSONLike) -> Optional[str]\n

Send a signed transaction and wait for confirmation.

Use keyword arguments for the specifying the signed transaction payload.

Arguments:

  • tx_signed: the signed transaction

"},{"location":"aea-framework-documentation/api/crypto/base/#get_transaction_receipt","title":"get_transaction_receipt","text":"
@abstractmethod\ndef get_transaction_receipt(tx_digest: str) -> Optional[JSONLike]\n

Get the transaction receipt for a transaction digest.

Arguments:

  • tx_digest: the digest associated to the transaction.

Returns:

the tx receipt, if present

"},{"location":"aea-framework-documentation/api/crypto/base/#get_transaction","title":"get_transaction","text":"
@abstractmethod\ndef get_transaction(tx_digest: str) -> Optional[JSONLike]\n

Get the transaction for a transaction digest.

Arguments:

  • tx_digest: the digest associated to the transaction.

Returns:

the tx, if present

"},{"location":"aea-framework-documentation/api/crypto/base/#get_contract_instance","title":"get_contract_instance","text":"
@abstractmethod\ndef get_contract_instance(contract_interface: Dict[str, str],\ncontract_address: Optional[str] = None) -> Any\n

Get the instance of a contract.

Arguments:

  • contract_interface: the contract interface.
  • contract_address: the contract address.

Returns:

the contract instance

"},{"location":"aea-framework-documentation/api/crypto/base/#get_deploy_transaction","title":"get_deploy_transaction","text":"
@abstractmethod\ndef get_deploy_transaction(contract_interface: Dict[str, str],\ndeployer_address: Address,\n**kwargs: Any) -> Optional[JSONLike]\n

Get the transaction to deploy the smart contract.

Arguments:

  • contract_interface: the contract interface.
  • deployer_address: The address that will deploy the contract.
  • kwargs: the keyword arguments.

Returns:

tx: the transaction dictionary.

"},{"location":"aea-framework-documentation/api/crypto/base/#update_with_gas_estimate","title":"update_with_gas_estimate","text":"
@abstractmethod\ndef update_with_gas_estimate(transaction: JSONLike) -> JSONLike\n

Attempts to update the transaction with a gas estimate

Arguments:

  • transaction: the transaction

Returns:

the updated transaction

"},{"location":"aea-framework-documentation/api/crypto/base/#faucetapi-objects","title":"FaucetApi Objects","text":"
class FaucetApi(ABC)\n

Interface for testnet faucet APIs.

"},{"location":"aea-framework-documentation/api/crypto/base/#get_wealth","title":"get_wealth","text":"
@abstractmethod\ndef get_wealth(address: Address, url: Optional[str] = None) -> None\n

Get wealth from the faucet for the provided address.

Arguments:

  • address: the address.
  • url: the url

Returns:

None

"},{"location":"aea-framework-documentation/api/crypto/helpers/","title":"Helpers","text":""},{"location":"aea-framework-documentation/api/crypto/helpers/#aeacryptohelpers","title":"aea.crypto.helpers","text":"

Module wrapping the helpers of public and private key cryptography.

"},{"location":"aea-framework-documentation/api/crypto/helpers/#try_validate_private_key_path","title":"try_validate_private_key_path","text":"
def try_validate_private_key_path(ledger_id: str,\nprivate_key_path: str,\npassword: Optional[str] = None) -> None\n

Try validate a private key path.

Arguments:

  • ledger_id: one of 'fetchai', 'ethereum'
  • private_key_path: the path to the private key.
  • password: the password to encrypt/decrypt the private key.

Raises:

  • None: ValueError if the identifier is invalid.

"},{"location":"aea-framework-documentation/api/crypto/helpers/#create_private_key","title":"create_private_key","text":"
def create_private_key(ledger_id: str,\nprivate_key_file: str,\npassword: Optional[str] = None) -> None\n

Create a private key for the specified ledger identifier.

Arguments:

  • ledger_id: the ledger identifier.
  • private_key_file: the private key file.
  • password: the password to encrypt/decrypt the private key.

Raises:

  • None: ValueError if the identifier is invalid.

"},{"location":"aea-framework-documentation/api/crypto/helpers/#try_generate_testnet_wealth","title":"try_generate_testnet_wealth","text":"
def try_generate_testnet_wealth(identifier: str,\naddress: str,\nurl: Optional[str] = None,\n_sync: bool = True) -> None\n

Try generate wealth on a testnet.

Arguments:

  • identifier: the identifier of the ledger
  • address: the address to check for
  • url: the url
  • _sync: whether to wait to sync or not; currently unused

"},{"location":"aea-framework-documentation/api/crypto/helpers/#private_key_verify","title":"private_key_verify","text":"
def private_key_verify(aea_conf: AgentConfig,\naea_project_path: Path,\npassword: Optional[str] = None) -> None\n

Check key.

Arguments:

  • aea_conf: AgentConfig
  • aea_project_path: Path, where project placed.
  • password: the password to encrypt/decrypt the private key.

"},{"location":"aea-framework-documentation/api/crypto/helpers/#make_certificate","title":"make_certificate","text":"
def make_certificate(ledger_id: str,\ncrypto_private_key_path: str,\nmessage: bytes,\noutput_path: str,\npassword: Optional[str] = None) -> str\n

Create certificate.

Arguments:

  • ledger_id: the ledger id
  • crypto_private_key_path: the path to the private key.
  • message: the message to be signed.
  • output_path: the location where to save the certificate.
  • password: the password to encrypt/decrypt the private keys.

Returns:

the signature/certificate

"},{"location":"aea-framework-documentation/api/crypto/helpers/#get_wallet_from_agent_config","title":"get_wallet_from_agent_config","text":"
def get_wallet_from_agent_config(agent_config: AgentConfig,\npassword: Optional[str] = None) -> Wallet\n

Get wallet from agent_cofig provided.

Arguments:

  • agent_config: the agent configuration object
  • password: the password to encrypt/decrypt the private keys.

Returns:

wallet

"},{"location":"aea-framework-documentation/api/crypto/helpers/#decrypterror-objects","title":"DecryptError Objects","text":"
class DecryptError(ValueError)\n

Error on bytes decryption with password.

"},{"location":"aea-framework-documentation/api/crypto/helpers/#__init__","title":"__init__","text":"
def __init__(msg: Optional[str] = None) -> None\n

Init exception.

"},{"location":"aea-framework-documentation/api/crypto/helpers/#keyisincorrect-objects","title":"KeyIsIncorrect Objects","text":"
class KeyIsIncorrect(ValueError)\n

Error decoding hex string to bytes for private key.

"},{"location":"aea-framework-documentation/api/crypto/helpers/#hex_to_bytes_for_key","title":"hex_to_bytes_for_key","text":"
def hex_to_bytes_for_key(data: str) -> bytes\n

Convert hex string to bytes with error handling.

"},{"location":"aea-framework-documentation/api/crypto/ledger_apis/","title":"LedgerApis","text":""},{"location":"aea-framework-documentation/api/crypto/ledger_apis/#aeacryptoledger_apis","title":"aea.crypto.ledger_apis","text":"

Module wrapping all the public and private keys cryptography.

"},{"location":"aea-framework-documentation/api/crypto/ledger_apis/#ledgerapis-objects","title":"LedgerApis Objects","text":"
class LedgerApis()\n

Store all the ledger apis we initialise.

"},{"location":"aea-framework-documentation/api/crypto/ledger_apis/#has_ledger","title":"has_ledger","text":"
@staticmethod\ndef has_ledger(identifier: str) -> bool\n

Check if it has the api.

"},{"location":"aea-framework-documentation/api/crypto/ledger_apis/#get_api","title":"get_api","text":"
@classmethod\ndef get_api(cls, identifier: str) -> LedgerApi\n

Get the ledger API.

"},{"location":"aea-framework-documentation/api/crypto/ledger_apis/#get_balance","title":"get_balance","text":"
@classmethod\ndef get_balance(cls, identifier: str, address: str) -> Optional[int]\n

Get the token balance.

Arguments:

  • identifier: the identifier of the ledger
  • address: the address to check for

Returns:

the token balance

"},{"location":"aea-framework-documentation/api/crypto/ledger_apis/#get_transfer_transaction","title":"get_transfer_transaction","text":"
@classmethod\ndef get_transfer_transaction(cls, identifier: str, sender_address: str,\ndestination_address: str, amount: int,\ntx_fee: int, tx_nonce: str,\n**kwargs: Any) -> Optional[Any]\n

Get a transaction to transfer from self to destination.

Arguments:

  • identifier: the identifier of the ledger
  • sender_address: the address of the sender
  • destination_address: the address of the receiver
  • amount: the amount
  • tx_nonce: verifies the authenticity of the tx
  • tx_fee: the tx fee
  • kwargs: the keyword arguments.

Returns:

tx

"},{"location":"aea-framework-documentation/api/crypto/ledger_apis/#send_signed_transaction","title":"send_signed_transaction","text":"
@classmethod\ndef send_signed_transaction(cls, identifier: str,\ntx_signed: Any) -> Optional[str]\n

Send a signed transaction and wait for confirmation.

Arguments:

  • identifier: the identifier of the ledger
  • tx_signed: the signed transaction

Returns:

the tx_digest, if present

"},{"location":"aea-framework-documentation/api/crypto/ledger_apis/#get_transaction_receipt","title":"get_transaction_receipt","text":"
@classmethod\ndef get_transaction_receipt(cls, identifier: str,\ntx_digest: str) -> Optional[Any]\n

Get the transaction receipt for a transaction digest.

Arguments:

  • identifier: the identifier of the ledger
  • tx_digest: the digest associated to the transaction.

Returns:

the tx receipt, if present

"},{"location":"aea-framework-documentation/api/crypto/ledger_apis/#get_transaction","title":"get_transaction","text":"
@classmethod\ndef get_transaction(cls, identifier: str, tx_digest: str) -> Optional[Any]\n

Get the transaction for a transaction digest.

Arguments:

  • identifier: the identifier of the ledger
  • tx_digest: the digest associated to the transaction.

Returns:

the tx, if present

"},{"location":"aea-framework-documentation/api/crypto/ledger_apis/#get_contract_address","title":"get_contract_address","text":"
@staticmethod\ndef get_contract_address(identifier: str,\ntx_receipt: Any) -> Optional[Address]\n

Get the contract address from a transaction receipt.

Arguments:

  • identifier: the identifier of the ledger
  • tx_receipt: the transaction receipt

Returns:

the contract address if successful

"},{"location":"aea-framework-documentation/api/crypto/ledger_apis/#is_transaction_settled","title":"is_transaction_settled","text":"
@staticmethod\ndef is_transaction_settled(identifier: str, tx_receipt: Any) -> bool\n

Check whether the transaction is settled and correct.

Arguments:

  • identifier: the identifier of the ledger
  • tx_receipt: the transaction digest

Returns:

True if correctly settled, False otherwise

"},{"location":"aea-framework-documentation/api/crypto/ledger_apis/#is_transaction_valid","title":"is_transaction_valid","text":"
@staticmethod\ndef is_transaction_valid(identifier: str, tx: Any, seller: Address,\nclient: Address, tx_nonce: str, amount: int) -> bool\n

Check whether the transaction is valid.

Arguments:

  • identifier: Ledger identifier
  • tx: the transaction
  • seller: the address of the seller.
  • client: the address of the client.
  • tx_nonce: the transaction nonce.
  • amount: the amount we expect to get from the transaction.

Returns:

True if is valid , False otherwise

"},{"location":"aea-framework-documentation/api/crypto/ledger_apis/#generate_tx_nonce","title":"generate_tx_nonce","text":"
@staticmethod\ndef generate_tx_nonce(identifier: str, seller: Address,\nclient: Address) -> str\n

Generate a random str message.

Arguments:

  • identifier: ledger identifier.
  • seller: the address of the seller.
  • client: the address of the client.

Returns:

return the hash in hex.

"},{"location":"aea-framework-documentation/api/crypto/ledger_apis/#recover_message","title":"recover_message","text":"
@staticmethod\ndef recover_message(identifier: str,\nmessage: bytes,\nsignature: str,\nis_deprecated_mode: bool = False) -> Tuple[Address, ...]\n

Recover the addresses from the hash.

Arguments:

  • identifier: ledger identifier.
  • message: the message we expect
  • signature: the transaction signature
  • is_deprecated_mode: if the deprecated signing was used

Returns:

the recovered addresses

"},{"location":"aea-framework-documentation/api/crypto/ledger_apis/#get_hash","title":"get_hash","text":"
@staticmethod\ndef get_hash(identifier: str, message: bytes) -> str\n

Get the hash of a message.

Arguments:

  • identifier: ledger identifier.
  • message: the message to be hashed.

Returns:

the hash of the message.

"},{"location":"aea-framework-documentation/api/crypto/ledger_apis/#is_valid_address","title":"is_valid_address","text":"
@staticmethod\ndef is_valid_address(identifier: str, address: Address) -> bool\n

Check if the address is valid.

Arguments:

  • identifier: ledger identifier.
  • address: the address to validate.

Returns:

whether it is a valid address or not.

"},{"location":"aea-framework-documentation/api/crypto/plugin/","title":"Plugin","text":""},{"location":"aea-framework-documentation/api/crypto/plugin/#aeacryptoplugin","title":"aea.crypto.plugin","text":"

Implementation of plug-in mechanism for cryptos.

"},{"location":"aea-framework-documentation/api/crypto/plugin/#plugin-objects","title":"Plugin Objects","text":"
class Plugin()\n

Class that implements an AEA plugin.

"},{"location":"aea-framework-documentation/api/crypto/plugin/#__init__","title":"__init__","text":"
def __init__(group: str, entry_point: EntryPoint)\n

Initialize the plugin.

Arguments:

  • group: the group the plugin belongs to.
  • entry_point: the entrypoint.

"},{"location":"aea-framework-documentation/api/crypto/plugin/#name","title":"name","text":"
@property\ndef name() -> str\n

Get the plugin identifier.

"},{"location":"aea-framework-documentation/api/crypto/plugin/#group","title":"group","text":"
@property\ndef group() -> str\n

Get the group.

"},{"location":"aea-framework-documentation/api/crypto/plugin/#attr","title":"attr","text":"
@property\ndef attr() -> str\n

Get the class name.

"},{"location":"aea-framework-documentation/api/crypto/plugin/#entry_point_path","title":"entry_point_path","text":"
@property\ndef entry_point_path() -> str\n

Get the entry point path.

"},{"location":"aea-framework-documentation/api/crypto/plugin/#load_all_plugins","title":"load_all_plugins","text":"
def load_all_plugins(is_raising_exception: bool = True) -> None\n

Load all plugins.

"},{"location":"aea-framework-documentation/api/crypto/wallet/","title":"Wallet","text":""},{"location":"aea-framework-documentation/api/crypto/wallet/#aeacryptowallet","title":"aea.crypto.wallet","text":"

Module wrapping all the public and private keys cryptography.

"},{"location":"aea-framework-documentation/api/crypto/wallet/#cryptostore-objects","title":"CryptoStore Objects","text":"
class CryptoStore()\n

Utility class to store and retrieve crypto objects.

"},{"location":"aea-framework-documentation/api/crypto/wallet/#__init__","title":"__init__","text":"
def __init__(crypto_id_to_path: Optional[Dict[str, Optional[str]]] = None,\npassword: Optional[str] = None) -> None\n

Initialize the crypto store.

Arguments:

  • crypto_id_to_path: dictionary from crypto id to an (optional) path to the private key.
  • password: the password to encrypt/decrypt the private key.

"},{"location":"aea-framework-documentation/api/crypto/wallet/#public_keys","title":"public_keys","text":"
@property\ndef public_keys() -> Dict[str, str]\n

Get the public_key dictionary.

"},{"location":"aea-framework-documentation/api/crypto/wallet/#crypto_objects","title":"crypto_objects","text":"
@property\ndef crypto_objects() -> Dict[str, Crypto]\n

Get the crypto objects (key pair).

"},{"location":"aea-framework-documentation/api/crypto/wallet/#addresses","title":"addresses","text":"
@property\ndef addresses() -> Dict[str, str]\n

Get the crypto addresses.

"},{"location":"aea-framework-documentation/api/crypto/wallet/#private_keys","title":"private_keys","text":"
@property\ndef private_keys() -> Dict[str, str]\n

Get the crypto addresses.

"},{"location":"aea-framework-documentation/api/crypto/wallet/#wallet-objects","title":"Wallet Objects","text":"
class Wallet()\n

Container for crypto objects.

The cryptos are separated into two categories:

  • main cryptos: used by the AEA for the economic side (i.e. signing transaction)
  • connection cryptos: exposed to the connection objects for encrypted communication.

"},{"location":"aea-framework-documentation/api/crypto/wallet/#__init___1","title":"__init__","text":"
def __init__(\nprivate_key_paths: Dict[str, Optional[str]],\nconnection_private_key_paths: Optional[Dict[str,\nOptional[str]]] = None,\npassword: Optional[str] = None)\n

Instantiate a wallet object.

Arguments:

  • private_key_paths: the private key paths
  • connection_private_key_paths: the private key paths for the connections.
  • password: the password to encrypt/decrypt the private key.

"},{"location":"aea-framework-documentation/api/crypto/wallet/#public_keys_1","title":"public_keys","text":"
@property\ndef public_keys() -> Dict[str, str]\n

Get the public_key dictionary.

"},{"location":"aea-framework-documentation/api/crypto/wallet/#crypto_objects_1","title":"crypto_objects","text":"
@property\ndef crypto_objects() -> Dict[str, Crypto]\n

Get the crypto objects (key pair).

"},{"location":"aea-framework-documentation/api/crypto/wallet/#addresses_1","title":"addresses","text":"
@property\ndef addresses() -> Dict[str, str]\n

Get the crypto addresses.

"},{"location":"aea-framework-documentation/api/crypto/wallet/#private_keys_1","title":"private_keys","text":"
@property\ndef private_keys() -> Dict[str, str]\n

Get the crypto addresses.

"},{"location":"aea-framework-documentation/api/crypto/wallet/#main_cryptos","title":"main_cryptos","text":"
@property\ndef main_cryptos() -> CryptoStore\n

Get the main crypto store.

"},{"location":"aea-framework-documentation/api/crypto/wallet/#connection_cryptos","title":"connection_cryptos","text":"
@property\ndef connection_cryptos() -> CryptoStore\n

Get the connection crypto store.

"},{"location":"aea-framework-documentation/api/crypto/wallet/#sign_message","title":"sign_message","text":"
def sign_message(crypto_id: str,\nmessage: bytes,\nis_deprecated_mode: bool = False) -> Optional[str]\n

Sign a message.

Arguments:

  • crypto_id: the id of the crypto
  • message: the message to be signed
  • is_deprecated_mode: what signing mode to use

Returns:

the signature of the message

"},{"location":"aea-framework-documentation/api/crypto/wallet/#sign_transaction","title":"sign_transaction","text":"
def sign_transaction(crypto_id: str, transaction: Any) -> Optional[JSONLike]\n

Sign a tx.

Arguments:

  • crypto_id: the id of the crypto
  • transaction: the transaction to be signed

Returns:

the signed tx

"},{"location":"aea-framework-documentation/api/crypto/registries/base/","title":"Base","text":""},{"location":"aea-framework-documentation/api/crypto/registries/base/#aeacryptoregistriesbase","title":"aea.crypto.registries.base","text":"

This module implements the base registry.

"},{"location":"aea-framework-documentation/api/crypto/registries/base/#itemid-objects","title":"ItemId Objects","text":"
class ItemId(RegexConstrainedString)\n

The identifier of an item class.

"},{"location":"aea-framework-documentation/api/crypto/registries/base/#name","title":"name","text":"
@property\ndef name() -> str\n

Get the id name.

"},{"location":"aea-framework-documentation/api/crypto/registries/base/#entrypoint-objects","title":"EntryPoint Objects","text":"
class EntryPoint(Generic[ItemType], RegexConstrainedString)\n

The entry point for a resource.

The regular expression matches the strings in the following format:

path.to.module:className\n

"},{"location":"aea-framework-documentation/api/crypto/registries/base/#__init__","title":"__init__","text":"
def __init__(seq: Union[\"EntryPoint\", str]) -> None\n

Initialize the entrypoint.

"},{"location":"aea-framework-documentation/api/crypto/registries/base/#import_path","title":"import_path","text":"
@property\ndef import_path() -> str\n

Get the import path.

"},{"location":"aea-framework-documentation/api/crypto/registries/base/#class_name","title":"class_name","text":"
@property\ndef class_name() -> str\n

Get the class name.

"},{"location":"aea-framework-documentation/api/crypto/registries/base/#load","title":"load","text":"
def load() -> Type[ItemType]\n

Load the item object.

Returns:

the crypto object, loaded following the spec.

"},{"location":"aea-framework-documentation/api/crypto/registries/base/#itemspec-objects","title":"ItemSpec Objects","text":"
class ItemSpec(Generic[ItemType])\n

A specification for a particular instance of an object.

"},{"location":"aea-framework-documentation/api/crypto/registries/base/#__init___1","title":"__init__","text":"
def __init__(id_: ItemId,\nentry_point: EntryPoint[ItemType],\nclass_kwargs: Optional[Dict[str, Any]] = None,\n**kwargs: Dict) -> None\n

Initialize an item specification.

Arguments:

  • id_: the id associated to this specification
  • entry_point: The Python entry_point of the environment class (e.g. module.name:Class).
  • class_kwargs: keyword arguments to be attached on the class as class variables.
  • kwargs: other custom keyword arguments.

"},{"location":"aea-framework-documentation/api/crypto/registries/base/#make","title":"make","text":"
def make(**kwargs: Any) -> ItemType\n

Instantiate an instance of the item object with appropriate arguments.

Arguments:

  • kwargs: the key word arguments

Returns:

an item

"},{"location":"aea-framework-documentation/api/crypto/registries/base/#get_class","title":"get_class","text":"
def get_class() -> Type[ItemType]\n

Get the class of the item with class variables instantiated.

Returns:

an item class

"},{"location":"aea-framework-documentation/api/crypto/registries/base/#registry-objects","title":"Registry Objects","text":"
class Registry(Generic[ItemType])\n

Registry for generic classes.

"},{"location":"aea-framework-documentation/api/crypto/registries/base/#__init___2","title":"__init__","text":"
def __init__() -> None\n

Initialize the registry.

"},{"location":"aea-framework-documentation/api/crypto/registries/base/#supported_ids","title":"supported_ids","text":"
@property\ndef supported_ids() -> Set[str]\n

Get the supported item ids.

"},{"location":"aea-framework-documentation/api/crypto/registries/base/#register","title":"register","text":"
def register(id_: Union[ItemId, str],\nentry_point: Union[EntryPoint[ItemType], str],\nclass_kwargs: Optional[Dict[str, Any]] = None,\n**kwargs: Any) -> None\n

Register an item type.

Arguments:

  • id_: the identifier for the crypto type.
  • entry_point: the entry point to load the crypto object.
  • class_kwargs: keyword arguments to be attached on the class as class variables.
  • kwargs: arguments to provide to the crypto class.

"},{"location":"aea-framework-documentation/api/crypto/registries/base/#make_1","title":"make","text":"
def make(id_: Union[ItemId, str],\nmodule: Optional[str] = None,\n**kwargs: Any) -> ItemType\n

Create an instance of the associated type item id.

Arguments:

  • id_: the id of the item class. Make sure it has been registered earlier before calling this function.
  • module: dotted path to a module. whether a module should be loaded before creating the object. this argument is useful when the item might not be registered beforehand, and loading the specified module will make the registration. E.g. suppose the call to 'register' for a custom object is located in some_package/init.py. By providing module=\"some_package\", the call to 'register' in such module gets triggered and the make can then find the identifier.
  • kwargs: keyword arguments to be forwarded to the object.

Returns:

the new item instance.

"},{"location":"aea-framework-documentation/api/crypto/registries/base/#make_cls","title":"make_cls","text":"
def make_cls(id_: Union[ItemId, str],\nmodule: Optional[str] = None) -> Type[ItemType]\n

Load a class of the associated type item id.

Arguments:

  • id_: the id of the item class. Make sure it has been registered earlier before calling this function.
  • module: dotted path to a module. whether a module should be loaded before creating the object. this argument is useful when the item might not be registered beforehand, and loading the specified module will make the registration. E.g. suppose the call to 'register' for a custom object is located in some_package/init.py. By providing module=\"some_package\", the call to 'register' in such module gets triggered and the make can then find the identifier.

Returns:

the new item class.

"},{"location":"aea-framework-documentation/api/crypto/registries/base/#has_spec","title":"has_spec","text":"
def has_spec(item_id: ItemId) -> bool\n

Check whether there exist a spec associated with an item id.

Arguments:

  • item_id: the item identifier.

Returns:

True if it is registered, False otherwise.

"},{"location":"aea-framework-documentation/api/decision_maker/base/","title":"Base","text":""},{"location":"aea-framework-documentation/api/decision_maker/base/#aeadecision_makerbase","title":"aea.decision_maker.base","text":"

This module contains the decision maker class.

"},{"location":"aea-framework-documentation/api/decision_maker/base/#ownershipstate-objects","title":"OwnershipState Objects","text":"
class OwnershipState(ABC)\n

Represent the ownership state of an agent (can proxy a ledger).

"},{"location":"aea-framework-documentation/api/decision_maker/base/#set","title":"set","text":"
@abstractmethod\ndef set(**kwargs: Any) -> None\n

Set values on the ownership state.

Arguments:

  • kwargs: the relevant keyword arguments

"},{"location":"aea-framework-documentation/api/decision_maker/base/#apply_delta","title":"apply_delta","text":"
@abstractmethod\ndef apply_delta(**kwargs: Any) -> None\n

Apply a state update to the ownership state.

This method is used to apply a raw state update without a transaction.

Arguments:

  • kwargs: the relevant keyword arguments

"},{"location":"aea-framework-documentation/api/decision_maker/base/#is_initialized","title":"is_initialized","text":"
@property\n@abstractmethod\ndef is_initialized() -> bool\n

Get the initialization status.

"},{"location":"aea-framework-documentation/api/decision_maker/base/#is_affordable_transaction","title":"is_affordable_transaction","text":"
@abstractmethod\ndef is_affordable_transaction(terms: Terms) -> bool\n

Check if the transaction is affordable (and consistent).

Arguments:

  • terms: the transaction terms

Returns:

True if the transaction is legal wrt the current state, false otherwise.

"},{"location":"aea-framework-documentation/api/decision_maker/base/#apply_transactions","title":"apply_transactions","text":"
@abstractmethod\ndef apply_transactions(list_of_terms: List[Terms]) -> \"OwnershipState\"\n

Apply a list of transactions to (a copy of) the current state.

Arguments:

  • list_of_terms: the sequence of transaction terms.

Returns:

the final state.

"},{"location":"aea-framework-documentation/api/decision_maker/base/#__copy__","title":"__copy__","text":"
@abstractmethod\ndef __copy__() -> \"OwnershipState\"\n

Copy the object.

"},{"location":"aea-framework-documentation/api/decision_maker/base/#preferences-objects","title":"Preferences Objects","text":"
class Preferences(ABC)\n

Class to represent the preferences.

"},{"location":"aea-framework-documentation/api/decision_maker/base/#set_1","title":"set","text":"
@abstractmethod\ndef set(**kwargs: Any) -> None\n

Set values on the preferences.

Arguments:

  • kwargs: the relevant key word arguments

"},{"location":"aea-framework-documentation/api/decision_maker/base/#is_initialized_1","title":"is_initialized","text":"
@property\n@abstractmethod\ndef is_initialized() -> bool\n

Get the initialization status.

Returns True if exchange_params_by_currency_id and utility_params_by_good_id are not None.

"},{"location":"aea-framework-documentation/api/decision_maker/base/#marginal_utility","title":"marginal_utility","text":"
@abstractmethod\ndef marginal_utility(ownership_state: OwnershipState, **kwargs: Any) -> float\n

Compute the marginal utility.

Arguments:

  • ownership_state: the ownership state against which to compute the marginal utility.
  • kwargs: optional keyword arguments

Returns:

the marginal utility score

"},{"location":"aea-framework-documentation/api/decision_maker/base/#utility_diff_from_transaction","title":"utility_diff_from_transaction","text":"
@abstractmethod\ndef utility_diff_from_transaction(ownership_state: OwnershipState,\nterms: Terms) -> float\n

Simulate a transaction and get the resulting utility difference (taking into account the fee).

Arguments:

  • ownership_state: the ownership state against which to apply the transaction.
  • terms: the transaction terms.

Returns:

the score.

"},{"location":"aea-framework-documentation/api/decision_maker/base/#__copy___1","title":"__copy__","text":"
@abstractmethod\ndef __copy__() -> \"Preferences\"\n

Copy the object.

"},{"location":"aea-framework-documentation/api/decision_maker/base/#protectedqueue-objects","title":"ProtectedQueue Objects","text":"
class ProtectedQueue(Queue)\n

A wrapper of a queue to protect which object can read from it.

"},{"location":"aea-framework-documentation/api/decision_maker/base/#__init__","title":"__init__","text":"
def __init__(access_code: str) -> None\n

Initialize the protected queue.

Arguments:

  • access_code: the access code to read from the queue

"},{"location":"aea-framework-documentation/api/decision_maker/base/#put","title":"put","text":"
def put(internal_message: Optional[Message],\nblock: bool = True,\ntimeout: Optional[float] = None) -> None\n

Put an internal message on the queue.

If optional args block is true and timeout is None (the default), block if necessary until a free slot is available. If timeout is a positive number, it blocks at most timeout seconds and raises the Full exception if no free slot was available within that time. Otherwise (block is false), put an item on the queue if a free slot is immediately available, else raise the Full exception (timeout is ignored in that case).

Arguments:

  • internal_message: the internal message to put on the queue
  • block: whether to block or not
  • timeout: timeout on block

Raises:

  • None: ValueError, if the item is not an internal message

"},{"location":"aea-framework-documentation/api/decision_maker/base/#put_nowait","title":"put_nowait","text":"
def put_nowait(internal_message: Optional[Message]) -> None\n

Put an internal message on the queue.

Equivalent to put(item, False).

Arguments:

  • internal_message: the internal message to put on the queue

Raises:

  • None: ValueError, if the item is not an internal message

"},{"location":"aea-framework-documentation/api/decision_maker/base/#get","title":"get","text":"
def get(block: bool = True, timeout: Optional[float] = None) -> None\n

Inaccessible get method.

Arguments:

  • block: whether to block or not
  • timeout: timeout on block

Raises:

  • None: ValueError, access not permitted.

"},{"location":"aea-framework-documentation/api/decision_maker/base/#get_nowait","title":"get_nowait","text":"
def get_nowait() -> None\n

Inaccessible get_nowait method.

Raises:

  • None: ValueError, access not permitted.

"},{"location":"aea-framework-documentation/api/decision_maker/base/#protected_get","title":"protected_get","text":"
def protected_get(access_code: str,\nblock: bool = True,\ntimeout: Optional[float] = None) -> Optional[Message]\n

Access protected get method.

Arguments:

  • access_code: the access code
  • block: If optional args block is true and timeout is None (the default), block if necessary until an item is available.
  • timeout: If timeout is a positive number, it blocks at most timeout seconds and raises the Empty exception if no item was available within that time.

Raises:

  • None: ValueError, if caller is not permitted

Returns:

internal message

"},{"location":"aea-framework-documentation/api/decision_maker/base/#decisionmakerhandler-objects","title":"DecisionMakerHandler Objects","text":"
class DecisionMakerHandler(WithLogger, ABC)\n

This class implements the decision maker.

"},{"location":"aea-framework-documentation/api/decision_maker/base/#__init___1","title":"__init__","text":"
def __init__(identity: Identity, wallet: Wallet, config: Dict[str, Any],\n**kwargs: Any) -> None\n

Initialize the decision maker handler.

Arguments:

  • identity: the identity
  • wallet: the wallet
  • config: the user defined configuration of the handler
  • kwargs: the key word arguments

"},{"location":"aea-framework-documentation/api/decision_maker/base/#agent_name","title":"agent_name","text":"
@property\ndef agent_name() -> str\n

Get the agent name.

"},{"location":"aea-framework-documentation/api/decision_maker/base/#identity","title":"identity","text":"
@property\ndef identity() -> Identity\n

Get identity of the agent.

"},{"location":"aea-framework-documentation/api/decision_maker/base/#wallet","title":"wallet","text":"
@property\ndef wallet() -> Wallet\n

Get wallet of the agent.

"},{"location":"aea-framework-documentation/api/decision_maker/base/#config","title":"config","text":"
@property\ndef config() -> Dict[str, Any]\n

Get user defined configuration

"},{"location":"aea-framework-documentation/api/decision_maker/base/#context","title":"context","text":"
@property\ndef context() -> SimpleNamespace\n

Get the context.

"},{"location":"aea-framework-documentation/api/decision_maker/base/#message_out_queue","title":"message_out_queue","text":"
@property\ndef message_out_queue() -> AsyncFriendlyQueue\n

Get (out) queue.

"},{"location":"aea-framework-documentation/api/decision_maker/base/#handle","title":"handle","text":"
@abstractmethod\ndef handle(message: Message) -> None\n

Handle an internal message from the skills.

Arguments:

  • message: the internal message

"},{"location":"aea-framework-documentation/api/decision_maker/base/#decisionmaker-objects","title":"DecisionMaker Objects","text":"
class DecisionMaker(WithLogger)\n

This class implements the decision maker.

"},{"location":"aea-framework-documentation/api/decision_maker/base/#__init___2","title":"__init__","text":"
def __init__(decision_maker_handler: DecisionMakerHandler) -> None\n

Initialize the decision maker.

Arguments:

  • decision_maker_handler: the decision maker handler

"},{"location":"aea-framework-documentation/api/decision_maker/base/#agent_name_1","title":"agent_name","text":"
@property\ndef agent_name() -> str\n

Get the agent name.

"},{"location":"aea-framework-documentation/api/decision_maker/base/#message_in_queue","title":"message_in_queue","text":"
@property\ndef message_in_queue() -> ProtectedQueue\n

Get (in) queue.

"},{"location":"aea-framework-documentation/api/decision_maker/base/#message_out_queue_1","title":"message_out_queue","text":"
@property\ndef message_out_queue() -> AsyncFriendlyQueue\n

Get (out) queue.

"},{"location":"aea-framework-documentation/api/decision_maker/base/#decision_maker_handler","title":"decision_maker_handler","text":"
@property\ndef decision_maker_handler() -> DecisionMakerHandler\n

Get the decision maker handler.

"},{"location":"aea-framework-documentation/api/decision_maker/base/#start","title":"start","text":"
def start() -> None\n

Start the decision maker.

"},{"location":"aea-framework-documentation/api/decision_maker/base/#stop","title":"stop","text":"
def stop() -> None\n

Stop the decision maker.

"},{"location":"aea-framework-documentation/api/decision_maker/base/#execute","title":"execute","text":"
def execute() -> None\n

Execute the decision maker.

Performs the following while not stopped:

  • gets internal messages from the in queue and calls handle() on them

"},{"location":"aea-framework-documentation/api/decision_maker/base/#handle_1","title":"handle","text":"
def handle(message: Message) -> None\n

Handle an internal message from the skills.

Arguments:

  • message: the internal message
"},{"location":"aea-framework-documentation/api/decision_maker/default/","title":"Default","text":""},{"location":"aea-framework-documentation/api/decision_maker/default/#aeadecision_makerdefault","title":"aea.decision_maker.default","text":"

This module contains the decision maker class.

"},{"location":"aea-framework-documentation/api/decision_maker/default/#decisionmakerhandler-objects","title":"DecisionMakerHandler Objects","text":"
class DecisionMakerHandler(BaseDecisionMakerHandler)\n

This class implements the decision maker.

"},{"location":"aea-framework-documentation/api/decision_maker/default/#signingdialogues-objects","title":"SigningDialogues Objects","text":"
class SigningDialogues(BaseSigningDialogues)\n

This class keeps track of all oef_search dialogues.

"},{"location":"aea-framework-documentation/api/decision_maker/default/#__init__","title":"__init__","text":"
def __init__(self_address: Address, **kwargs: Any) -> None\n

Initialize dialogues.

Arguments:

  • self_address: the address of the entity for whom dialogues are maintained
  • kwargs: the keyword arguments

"},{"location":"aea-framework-documentation/api/decision_maker/default/#__init___1","title":"__init__","text":"
def __init__(identity: Identity, wallet: Wallet, config: Dict[str,\nAny]) -> None\n

Initialize the decision maker.

Arguments:

  • identity: the identity
  • wallet: the wallet
  • config: the user defined configuration of the handler

"},{"location":"aea-framework-documentation/api/decision_maker/default/#handle","title":"handle","text":"
def handle(message: Message) -> None\n

Handle an internal message from the skills.

Arguments:

  • message: the internal message
"},{"location":"aea-framework-documentation/api/decision_maker/gop/","title":"GOP","text":""},{"location":"aea-framework-documentation/api/decision_maker/gop/#aeadecision_makergop","title":"aea.decision_maker.gop","text":"

This module contains the decision maker class.

"},{"location":"aea-framework-documentation/api/decision_maker/gop/#goalpursuitreadiness-objects","title":"GoalPursuitReadiness Objects","text":"
class GoalPursuitReadiness()\n

The goal pursuit readiness.

"},{"location":"aea-framework-documentation/api/decision_maker/gop/#status-objects","title":"Status Objects","text":"
class Status(Enum)\n

The enum of the readiness status.

In particular, it can be one of the following:

  • Status.READY: when the agent is ready to pursuit its goal
  • Status.NOT_READY: when the agent is not ready to pursuit its goal

"},{"location":"aea-framework-documentation/api/decision_maker/gop/#__init__","title":"__init__","text":"
def __init__() -> None\n

Instantiate the goal pursuit readiness.

"},{"location":"aea-framework-documentation/api/decision_maker/gop/#is_ready","title":"is_ready","text":"
@property\ndef is_ready() -> bool\n

Get the readiness.

"},{"location":"aea-framework-documentation/api/decision_maker/gop/#update","title":"update","text":"
def update(new_status: Status) -> None\n

Update the goal pursuit readiness.

Arguments:

  • new_status: the new status

"},{"location":"aea-framework-documentation/api/decision_maker/gop/#ownershipstate-objects","title":"OwnershipState Objects","text":"
class OwnershipState(BaseOwnershipState)\n

Represent the ownership state of an agent (can proxy a ledger).

"},{"location":"aea-framework-documentation/api/decision_maker/gop/#__init___1","title":"__init__","text":"
def __init__() -> None\n

Instantiate an ownership state object.

"},{"location":"aea-framework-documentation/api/decision_maker/gop/#set","title":"set","text":"
def set(amount_by_currency_id: CurrencyHoldings = None,\nquantities_by_good_id: GoodHoldings = None,\n**kwargs: Any) -> None\n

Set values on the ownership state.

Arguments:

  • amount_by_currency_id: the currency endowment of the agent in this state.
  • quantities_by_good_id: the good endowment of the agent in this state.
  • kwargs: the keyword arguments.

"},{"location":"aea-framework-documentation/api/decision_maker/gop/#apply_delta","title":"apply_delta","text":"
def apply_delta(delta_amount_by_currency_id: Dict[str, int] = None,\ndelta_quantities_by_good_id: Dict[str, int] = None,\n**kwargs: Any) -> None\n

Apply a state update to the ownership state.

This method is used to apply a raw state update without a transaction.

Arguments:

  • delta_amount_by_currency_id: the delta in the currency amounts
  • delta_quantities_by_good_id: the delta in the quantities by good
  • kwargs: the keyword arguments

"},{"location":"aea-framework-documentation/api/decision_maker/gop/#is_initialized","title":"is_initialized","text":"
@property\ndef is_initialized() -> bool\n

Get the initialization status.

"},{"location":"aea-framework-documentation/api/decision_maker/gop/#amount_by_currency_id","title":"amount_by_currency_id","text":"
@property\ndef amount_by_currency_id() -> CurrencyHoldings\n

Get currency holdings in this state.

"},{"location":"aea-framework-documentation/api/decision_maker/gop/#quantities_by_good_id","title":"quantities_by_good_id","text":"
@property\ndef quantities_by_good_id() -> GoodHoldings\n

Get good holdings in this state.

"},{"location":"aea-framework-documentation/api/decision_maker/gop/#is_affordable_transaction","title":"is_affordable_transaction","text":"
def is_affordable_transaction(terms: Terms) -> bool\n

Check if the transaction is affordable (and consistent).

E.g. check that the agent state has enough money if it is a buyer or enough holdings if it is a seller. Note, the agent is the sender of the transaction message by design.

Arguments:

  • terms: the transaction terms

Returns:

True if the transaction is legal wrt the current state, false otherwise.

"},{"location":"aea-framework-documentation/api/decision_maker/gop/#is_affordable","title":"is_affordable","text":"
def is_affordable(terms: Terms) -> bool\n

Check if the tx is affordable.

Arguments:

  • terms: the transaction terms

Returns:

whether the transaction is affordable or not

"},{"location":"aea-framework-documentation/api/decision_maker/gop/#update_1","title":"update","text":"
def update(terms: Terms) -> None\n

Update the agent state from a transaction.

Arguments:

  • terms: the transaction terms

"},{"location":"aea-framework-documentation/api/decision_maker/gop/#apply_transactions","title":"apply_transactions","text":"
def apply_transactions(list_of_terms: List[Terms]) -> \"OwnershipState\"\n

Apply a list of transactions to (a copy of) the current state.

Arguments:

  • list_of_terms: the sequence of transaction terms.

Returns:

the final state.

"},{"location":"aea-framework-documentation/api/decision_maker/gop/#__copy__","title":"__copy__","text":"
def __copy__() -> \"OwnershipState\"\n

Copy the object.

"},{"location":"aea-framework-documentation/api/decision_maker/gop/#preferences-objects","title":"Preferences Objects","text":"
class Preferences(BasePreferences)\n

Class to represent the preferences.

"},{"location":"aea-framework-documentation/api/decision_maker/gop/#__init___2","title":"__init__","text":"
def __init__() -> None\n

Instantiate an agent preference object.

"},{"location":"aea-framework-documentation/api/decision_maker/gop/#set_1","title":"set","text":"
def set(exchange_params_by_currency_id: ExchangeParams = None,\nutility_params_by_good_id: UtilityParams = None,\n**kwargs: Any) -> None\n

Set values on the preferences.

Arguments:

  • exchange_params_by_currency_id: the exchange params.
  • utility_params_by_good_id: the utility params for every asset.
  • kwargs: the keyword arguments.

"},{"location":"aea-framework-documentation/api/decision_maker/gop/#is_initialized_1","title":"is_initialized","text":"
@property\ndef is_initialized() -> bool\n

Get the initialization status.

Returns:

True if exchange_params_by_currency_id and utility_params_by_good_id are not None.

"},{"location":"aea-framework-documentation/api/decision_maker/gop/#exchange_params_by_currency_id","title":"exchange_params_by_currency_id","text":"
@property\ndef exchange_params_by_currency_id() -> ExchangeParams\n

Get exchange parameter for each currency.

"},{"location":"aea-framework-documentation/api/decision_maker/gop/#utility_params_by_good_id","title":"utility_params_by_good_id","text":"
@property\ndef utility_params_by_good_id() -> UtilityParams\n

Get utility parameter for each good.

"},{"location":"aea-framework-documentation/api/decision_maker/gop/#logarithmic_utility","title":"logarithmic_utility","text":"
def logarithmic_utility(quantities_by_good_id: GoodHoldings) -> float\n

Compute agent's utility given her utility function params and a good bundle.

Arguments:

  • quantities_by_good_id: the good holdings (dictionary) with the identifier (key) and quantity (value) for each good

Returns:

utility value

"},{"location":"aea-framework-documentation/api/decision_maker/gop/#linear_utility","title":"linear_utility","text":"
def linear_utility(amount_by_currency_id: CurrencyHoldings) -> float\n

Compute agent's utility given her utility function params and a currency bundle.

Arguments:

  • amount_by_currency_id: the currency holdings (dictionary) with the identifier (key) and quantity (value) for each currency

Returns:

utility value

"},{"location":"aea-framework-documentation/api/decision_maker/gop/#utility","title":"utility","text":"
def utility(quantities_by_good_id: GoodHoldings,\namount_by_currency_id: CurrencyHoldings) -> float\n

Compute the utility given the good and currency holdings.

Arguments:

  • quantities_by_good_id: the good holdings
  • amount_by_currency_id: the currency holdings

Returns:

the utility value.

"},{"location":"aea-framework-documentation/api/decision_maker/gop/#marginal_utility","title":"marginal_utility","text":"
def marginal_utility(\nownership_state: BaseOwnershipState,\ndelta_quantities_by_good_id: Optional[GoodHoldings] = None,\ndelta_amount_by_currency_id: Optional[CurrencyHoldings] = None,\n**kwargs: Any) -> float\n

Compute the marginal utility.

Arguments:

  • ownership_state: the ownership state against which to compute the marginal utility.
  • delta_quantities_by_good_id: the change in good holdings
  • delta_amount_by_currency_id: the change in money holdings
  • kwargs: the keyword arguments

Returns:

the marginal utility score

"},{"location":"aea-framework-documentation/api/decision_maker/gop/#utility_diff_from_transaction","title":"utility_diff_from_transaction","text":"
def utility_diff_from_transaction(ownership_state: BaseOwnershipState,\nterms: Terms) -> float\n

Simulate a transaction and get the resulting utility difference (taking into account the fee).

Arguments:

  • ownership_state: the ownership state against which to apply the transaction.
  • terms: the transaction terms.

Returns:

the score.

"},{"location":"aea-framework-documentation/api/decision_maker/gop/#is_utility_enhancing","title":"is_utility_enhancing","text":"
def is_utility_enhancing(ownership_state: BaseOwnershipState,\nterms: Terms) -> bool\n

Check if the tx is utility enhancing.

Arguments:

  • ownership_state: the ownership state against which to apply the transaction.
  • terms: the transaction terms

Returns:

whether the transaction is utility enhancing or not

"},{"location":"aea-framework-documentation/api/decision_maker/gop/#__copy___1","title":"__copy__","text":"
def __copy__() -> \"Preferences\"\n

Copy the object.

"},{"location":"aea-framework-documentation/api/decision_maker/gop/#decisionmakerhandler-objects","title":"DecisionMakerHandler Objects","text":"
class DecisionMakerHandler(BaseDecisionMakerHandler)\n

This class implements the decision maker.

"},{"location":"aea-framework-documentation/api/decision_maker/gop/#signingdialogues-objects","title":"SigningDialogues Objects","text":"
class SigningDialogues(BaseSigningDialogues)\n

This class keeps track of all oef_search dialogues.

"},{"location":"aea-framework-documentation/api/decision_maker/gop/#__init___3","title":"__init__","text":"
def __init__(self_address: Address, **kwargs: Any) -> None\n

Initialize dialogues.

Arguments:

  • self_address: the address of the entity for whom dialogues are maintained
  • kwargs: the keyword arguments

"},{"location":"aea-framework-documentation/api/decision_maker/gop/#stateupdatedialogues-objects","title":"StateUpdateDialogues Objects","text":"
class StateUpdateDialogues(BaseStateUpdateDialogues)\n

This class keeps track of all oef_search dialogues.

"},{"location":"aea-framework-documentation/api/decision_maker/gop/#__init___4","title":"__init__","text":"
def __init__(self_address: Address, **kwargs: Any) -> None\n

Initialize dialogues.

Arguments:

  • self_address: the address of the entity for whom dialogues are maintained
  • kwargs: the keyword arguments

"},{"location":"aea-framework-documentation/api/decision_maker/gop/#__init___5","title":"__init__","text":"
def __init__(identity: Identity, wallet: Wallet, config: Dict[str,\nAny]) -> None\n

Initialize the decision maker.

Arguments:

  • identity: the identity
  • wallet: the wallet
  • config: the user defined configuration of the handler

"},{"location":"aea-framework-documentation/api/decision_maker/gop/#handle","title":"handle","text":"
def handle(message: Message) -> None\n

Handle an internal message from the skills.

Arguments:

  • message: the internal message
"},{"location":"aea-framework-documentation/api/error_handler/base/","title":"Base","text":""},{"location":"aea-framework-documentation/api/error_handler/base/#aeaerror_handlerbase","title":"aea.error_handler.base","text":"

This module contains the abstract error handler class.

"},{"location":"aea-framework-documentation/api/error_handler/base/#abstracterrorhandler-objects","title":"AbstractErrorHandler Objects","text":"
class AbstractErrorHandler(ABC)\n

Error handler class for handling problematic envelopes.

"},{"location":"aea-framework-documentation/api/error_handler/base/#__init__","title":"__init__","text":"
def __init__(**kwargs: Any)\n

Instantiate error handler.

"},{"location":"aea-framework-documentation/api/error_handler/base/#config","title":"config","text":"
@property\ndef config() -> Dict[str, Any]\n

Get handler config.

"},{"location":"aea-framework-documentation/api/error_handler/base/#send_unsupported_protocol","title":"send_unsupported_protocol","text":"
@abstractmethod\ndef send_unsupported_protocol(envelope: Envelope, logger: Logger) -> None\n

Handle the received envelope in case the protocol is not supported.

Arguments:

  • envelope: the envelope
  • logger: the logger

Returns:

None

"},{"location":"aea-framework-documentation/api/error_handler/base/#send_decoding_error","title":"send_decoding_error","text":"
@abstractmethod\ndef send_decoding_error(envelope: Envelope, exception: Exception,\nlogger: Logger) -> None\n

Handle a decoding error.

Arguments:

  • envelope: the envelope
  • exception: the exception raised during decoding
  • logger: the logger

Returns:

None

"},{"location":"aea-framework-documentation/api/error_handler/base/#send_no_active_handler","title":"send_no_active_handler","text":"
@abstractmethod\ndef send_no_active_handler(envelope: Envelope, reason: str,\nlogger: Logger) -> None\n

Handle the received envelope in case the handler is not supported.

Arguments:

  • envelope: the envelope
  • reason: the reason for the failure
  • logger: the logger

Returns:

None

"},{"location":"aea-framework-documentation/api/error_handler/default/","title":"Default","text":""},{"location":"aea-framework-documentation/api/error_handler/default/#aeaerror_handlerdefault","title":"aea.error_handler.default","text":"

This module contains the default error handler class.

"},{"location":"aea-framework-documentation/api/error_handler/default/#errorhandler-objects","title":"ErrorHandler Objects","text":"
class ErrorHandler(AbstractErrorHandler)\n

Error handler class for handling problematic envelopes.

"},{"location":"aea-framework-documentation/api/error_handler/default/#__init__","title":"__init__","text":"
def __init__(**kwargs: Any)\n

Instantiate error handler.

"},{"location":"aea-framework-documentation/api/error_handler/default/#send_unsupported_protocol","title":"send_unsupported_protocol","text":"
def send_unsupported_protocol(envelope: Envelope, logger: Logger) -> None\n

Handle the received envelope in case the protocol is not supported.

Arguments:

  • envelope: the envelope
  • logger: the logger

"},{"location":"aea-framework-documentation/api/error_handler/default/#send_decoding_error","title":"send_decoding_error","text":"
def send_decoding_error(envelope: Envelope, exception: Exception,\nlogger: Logger) -> None\n

Handle a decoding error.

Arguments:

  • envelope: the envelope
  • exception: the exception raised during decoding
  • logger: the logger

"},{"location":"aea-framework-documentation/api/error_handler/default/#send_no_active_handler","title":"send_no_active_handler","text":"
def send_no_active_handler(envelope: Envelope, reason: str,\nlogger: Logger) -> None\n

Handle the received envelope in case the handler is not supported.

Arguments:

  • envelope: the envelope
  • reason: the reason for the failure
  • logger: the logger
"},{"location":"aea-framework-documentation/api/helpers/async_friendly_queue/","title":"Async Friendly Queue","text":""},{"location":"aea-framework-documentation/api/helpers/async_friendly_queue/#aeahelpersasync_friendly_queue","title":"aea.helpers.async_friendly_queue","text":"

This module contains the implementation of AsyncFriendlyQueue.

"},{"location":"aea-framework-documentation/api/helpers/async_friendly_queue/#asyncfriendlyqueue-objects","title":"AsyncFriendlyQueue Objects","text":"
class AsyncFriendlyQueue(queue.Queue)\n

queue.Queue with async_get method.

"},{"location":"aea-framework-documentation/api/helpers/async_friendly_queue/#__init__","title":"__init__","text":"
def __init__(*args: Any, **kwargs: Any) -> None\n

Init queue.

"},{"location":"aea-framework-documentation/api/helpers/async_friendly_queue/#put","title":"put","text":"
def put(item: Any, *args: Any, **kwargs: Any) -> None\n

Put an item into the queue.

Arguments:

  • item: item to put in the queue
  • args: similar to queue.Queue.put
  • kwargs: similar to queue.Queue.put

"},{"location":"aea-framework-documentation/api/helpers/async_friendly_queue/#get","title":"get","text":"
def get(*args: Any, **kwargs: Any) -> Any\n

Get an item into the queue.

Arguments:

  • args: similar to queue.Queue.get
  • kwargs: similar to queue.Queue.get

Returns:

similar to queue.Queue.get

"},{"location":"aea-framework-documentation/api/helpers/async_friendly_queue/#async_wait","title":"async_wait","text":"
async def async_wait() -> None\n

Wait an item appears in the queue.

Returns:

None

"},{"location":"aea-framework-documentation/api/helpers/async_friendly_queue/#async_get","title":"async_get","text":"
async def async_get() -> Any\n

Wait and get an item from the queue.

Returns:

item from queue

"},{"location":"aea-framework-documentation/api/helpers/async_utils/","title":"Async Utils","text":""},{"location":"aea-framework-documentation/api/helpers/async_utils/#aeahelpersasync_utils","title":"aea.helpers.async_utils","text":"

This module contains the misc utils for async code.

"},{"location":"aea-framework-documentation/api/helpers/async_utils/#ensure_list","title":"ensure_list","text":"
def ensure_list(value: Any) -> List\n

Return [value] or list(value) if value is a sequence.

"},{"location":"aea-framework-documentation/api/helpers/async_utils/#asyncstate-objects","title":"AsyncState Objects","text":"
class AsyncState()\n

Awaitable state.

"},{"location":"aea-framework-documentation/api/helpers/async_utils/#__init__","title":"__init__","text":"
def __init__(initial_state: Any = None,\nstates_enum: Optional[Container[Any]] = None) -> None\n

Init async state.

Arguments:

  • initial_state: state to set on start.
  • states_enum: container of valid states if not provided state not checked on set.

"},{"location":"aea-framework-documentation/api/helpers/async_utils/#set","title":"set","text":"
def set(state: Any) -> None\n

Set state.

"},{"location":"aea-framework-documentation/api/helpers/async_utils/#add_callback","title":"add_callback","text":"
def add_callback(callback_fn: Callable[[Any], None]) -> None\n

Add callback to track state changes.

Arguments:

  • callback_fn: callable object to be called on state changed.

"},{"location":"aea-framework-documentation/api/helpers/async_utils/#get","title":"get","text":"
def get() -> Any\n

Get state.

"},{"location":"aea-framework-documentation/api/helpers/async_utils/#wait","title":"wait","text":"
async def wait(state_or_states: Union[Any, Sequence[Any]]) -> Tuple[Any, Any]\n

Wait state to be set.

Arguments:

  • state_or_states: state or list of states.

Returns:

tuple of previous state and new state.

"},{"location":"aea-framework-documentation/api/helpers/async_utils/#transit","title":"transit","text":"
@contextmanager\ndef transit(initial: Any = not_set,\nsuccess: Any = not_set,\nfail: Any = not_set) -> Generator\n

Change state context according to success or not.

Arguments:

  • initial: set state on context enter, not_set by default
  • success: set state on context block done, not_set by default
  • fail: set state on context block raises exception, not_set by default

Returns:

generator

"},{"location":"aea-framework-documentation/api/helpers/async_utils/#periodiccaller-objects","title":"PeriodicCaller Objects","text":"
class PeriodicCaller()\n

Schedule a periodic call of callable using event loop.

Used for periodic function run using asyncio.

"},{"location":"aea-framework-documentation/api/helpers/async_utils/#__init___1","title":"__init__","text":"
def __init__(callback: Callable,\nperiod: float,\nstart_at: Optional[datetime.datetime] = None,\nexception_callback: Optional[Callable[[Callable, Exception],\nNone]] = None,\nloop: Optional[AbstractEventLoop] = None) -> None\n

Init periodic caller.

Arguments:

  • callback: function to call periodically
  • period: period in seconds.
  • start_at: optional first call datetime
  • exception_callback: optional handler to call on exception raised.
  • loop: optional asyncio event loop

"},{"location":"aea-framework-documentation/api/helpers/async_utils/#start","title":"start","text":"
def start() -> None\n

Activate period calls.

"},{"location":"aea-framework-documentation/api/helpers/async_utils/#stop","title":"stop","text":"
def stop() -> None\n

Remove from schedule.

"},{"location":"aea-framework-documentation/api/helpers/async_utils/#anotherthreadtask-objects","title":"AnotherThreadTask Objects","text":"
class AnotherThreadTask()\n

Schedule a task to run on the loop in another thread.

Provides better cancel behaviour: on cancel it will wait till cancelled completely.

"},{"location":"aea-framework-documentation/api/helpers/async_utils/#__init___2","title":"__init__","text":"
def __init__(coro: Coroutine[Any, Any, Any], loop: AbstractEventLoop) -> None\n

Init the task.

Arguments:

  • coro: coroutine to schedule
  • loop: an event loop to schedule on.

"},{"location":"aea-framework-documentation/api/helpers/async_utils/#result","title":"result","text":"
def result(timeout: Optional[float] = None) -> Any\n

Wait for coroutine execution result.

Arguments:

  • timeout: optional timeout to wait in seconds.

Returns:

result

"},{"location":"aea-framework-documentation/api/helpers/async_utils/#cancel","title":"cancel","text":"
def cancel() -> None\n

Cancel coroutine task execution in a target loop.

"},{"location":"aea-framework-documentation/api/helpers/async_utils/#done","title":"done","text":"
def done() -> bool\n

Check task is done.

"},{"location":"aea-framework-documentation/api/helpers/async_utils/#threadedasyncrunner-objects","title":"ThreadedAsyncRunner Objects","text":"
class ThreadedAsyncRunner(Thread)\n

Util to run thread with event loop and execute coroutines inside.

"},{"location":"aea-framework-documentation/api/helpers/async_utils/#__init___3","title":"__init__","text":"
def __init__(loop: Optional[AbstractEventLoop] = None) -> None\n

Init threaded runner.

Arguments:

  • loop: optional event loop. is it's running loop, threaded runner will use it.

"},{"location":"aea-framework-documentation/api/helpers/async_utils/#start_1","title":"start","text":"
def start() -> None\n

Start event loop in dedicated thread.

"},{"location":"aea-framework-documentation/api/helpers/async_utils/#run","title":"run","text":"
def run() -> None\n

Run code inside thread.

"},{"location":"aea-framework-documentation/api/helpers/async_utils/#call","title":"call","text":"
def call(coro: Coroutine[Any, Any, Any]) -> Any\n

Run a coroutine inside the event loop.

Arguments:

  • coro: a coroutine to run.

Returns:

task

"},{"location":"aea-framework-documentation/api/helpers/async_utils/#stop_1","title":"stop","text":"
def stop() -> None\n

Stop event loop in thread.

"},{"location":"aea-framework-documentation/api/helpers/async_utils/#runnable-objects","title":"Runnable Objects","text":"
class Runnable(ABC)\n

Abstract Runnable class.

Use to run async task in same event loop or in dedicated thread. Provides: start, stop sync methods to start and stop task Use wait_completed to await task was completed.

"},{"location":"aea-framework-documentation/api/helpers/async_utils/#__init___4","title":"__init__","text":"
def __init__(loop: asyncio.AbstractEventLoop = None,\nthreaded: bool = False) -> None\n

Init runnable.

Arguments:

  • loop: asyncio event loop to use.
  • threaded: bool. start in thread if True.

"},{"location":"aea-framework-documentation/api/helpers/async_utils/#start_2","title":"start","text":"
def start() -> bool\n

Start runnable.

Returns:

bool started or not.

"},{"location":"aea-framework-documentation/api/helpers/async_utils/#is_running","title":"is_running","text":"
@property\ndef is_running() -> bool\n

Get running state.

"},{"location":"aea-framework-documentation/api/helpers/async_utils/#run_1","title":"run","text":"
@abstractmethod\nasync def run() -> Any\n

Implement run logic respectful to CancelError on termination.

"},{"location":"aea-framework-documentation/api/helpers/async_utils/#wait_completed","title":"wait_completed","text":"
def wait_completed(sync: bool = False,\ntimeout: float = None,\nforce_result: bool = False) -> Union[Coroutine, Awaitable]\n

Wait runnable execution completed.

Arguments:

  • sync: bool. blocking wait
  • timeout: float seconds
  • force_result: check result even it was waited.

Returns:

awaitable if sync is False, otherwise None

"},{"location":"aea-framework-documentation/api/helpers/async_utils/#stop_2","title":"stop","text":"
def stop(force: bool = False) -> None\n

Stop runnable.

"},{"location":"aea-framework-documentation/api/helpers/async_utils/#start_and_wait_completed","title":"start_and_wait_completed","text":"
def start_and_wait_completed(*args: Any,\n**kwargs: Any) -> Union[Coroutine, Awaitable]\n

Alias for start and wait methods.

"},{"location":"aea-framework-documentation/api/helpers/base/","title":"Base","text":""},{"location":"aea-framework-documentation/api/helpers/base/#aeahelpersbase","title":"aea.helpers.base","text":"

Miscellaneous helpers.

"},{"location":"aea-framework-documentation/api/helpers/base/#locate","title":"locate","text":"
def locate(path: str) -> Any\n

Locate an object by name or dotted save_path, importing as necessary.

"},{"location":"aea-framework-documentation/api/helpers/base/#load_module","title":"load_module","text":"
def load_module(dotted_path: str, filepath: Path) -> types.ModuleType\n

Load a module.

Arguments:

  • dotted_path: the dotted save_path of the package/module.
  • filepath: the file to the package/module.

Raises:

  • ValueError: if the filepath provided is not a module. # noqa: DAR402
  • Exception: if the execution of the module raises exception. # noqa: DAR402

Returns:

module type

"},{"location":"aea-framework-documentation/api/helpers/base/#load_env_file","title":"load_env_file","text":"
def load_env_file(env_file: str) -> None\n

Load the content of the environment file into the process environment.

Arguments:

  • env_file: save_path to the env file.

"},{"location":"aea-framework-documentation/api/helpers/base/#sigint_crossplatform","title":"sigint_crossplatform","text":"
def sigint_crossplatform(process: subprocess.Popen) -> None\n

Send a SIGINT, cross-platform.

The reason is because the subprocess module doesn't have an API to send a SIGINT-like signal both on Posix and Windows with a single method.

However, a subprocess.Popen class has the method 'send_signal' that gives more flexibility in this terms.

Arguments:

  • process: the process to send the signal to.

"},{"location":"aea-framework-documentation/api/helpers/base/#win_popen_kwargs","title":"win_popen_kwargs","text":"
def win_popen_kwargs() -> dict\n

Return kwargs to start a process in windows with new process group.

Help to handle ctrl c properly. Return empty dict if platform is not win32

Returns:

windows popen kwargs

"},{"location":"aea-framework-documentation/api/helpers/base/#send_control_c","title":"send_control_c","text":"
def send_control_c(process: subprocess.Popen,\nkill_group: bool = False) -> None\n

Send ctrl-C cross-platform to terminate a subprocess.

Arguments:

  • process: the process to send the signal to.
  • kill_group: whether or not to kill group

"},{"location":"aea-framework-documentation/api/helpers/base/#regexconstrainedstring-objects","title":"RegexConstrainedString Objects","text":"
class RegexConstrainedString(UserString)\n

A string that is constrained by a regex.

The default behaviour is to match anything. Subclass this class and change the 'REGEX' class attribute to implement a different behaviour.

"},{"location":"aea-framework-documentation/api/helpers/base/#__init__","title":"__init__","text":"
def __init__(seq: Union[UserString, str]) -> None\n

Initialize a regex constrained string.

"},{"location":"aea-framework-documentation/api/helpers/base/#simpleid-objects","title":"SimpleId Objects","text":"
class SimpleId(RegexConstrainedString)\n

A simple identifier.

The allowed strings are all the strings that: - have at least length 1 - have at most length 128 - the first character must be between a-z,A-Z or underscore - the other characters must be either the above or digits.

Examples of allowed strings:

SimpleId(\"an_identifier\") 'an_identifier'

Examples of not allowed strings:

SimpleId(\"0an_identifier\") Traceback (most recent call last): ... ValueError: Value 0an_identifier does not match the regular expression re.compile('[a-zA-Z_][a-zA-Z0-9_]{0,127}')

SimpleId(\"an identifier\") Traceback (most recent call last): ... ValueError: Value an identifier does not match the regular expression re.compile('[a-zA-Z_][a-zA-Z0-9_]{0,127}')

SimpleId(\"\") Traceback (most recent call last): ... ValueError: Value does not match the regular expression re.compile('[a-zA-Z_][a-zA-Z0-9_]{0,127}')

"},{"location":"aea-framework-documentation/api/helpers/base/#cd","title":"cd","text":"
@contextlib.contextmanager\ndef cd(path: PathLike) -> Generator\n

Change working directory temporarily.

"},{"location":"aea-framework-documentation/api/helpers/base/#get_logger_method","title":"get_logger_method","text":"
def get_logger_method(fn: Callable,\nlogger_method: Union[str, Callable]) -> Callable\n

Get logger method for function.

Get logger in fn definition module or creates logger is module.name. Or return logger_method if it's callable.

Arguments:

  • fn: function to get logger for.
  • logger_method: logger name or callable.

Returns:

callable to write log with

"},{"location":"aea-framework-documentation/api/helpers/base/#try_decorator","title":"try_decorator","text":"
def try_decorator(error_message: str,\ndefault_return: Callable = None,\nlogger_method: Any = \"error\") -> Callable\n

Run function, log and return default value on exception.

Does not support async or coroutines!

Arguments:

  • error_message: message template with one {} for exception
  • default_return: value to return on exception, by default None
  • logger_method: name of the logger method or callable to print logs

Returns:

the callable

"},{"location":"aea-framework-documentation/api/helpers/base/#maxretrieserror-objects","title":"MaxRetriesError Objects","text":"
class MaxRetriesError(Exception)\n

Exception for retry decorator.

"},{"location":"aea-framework-documentation/api/helpers/base/#retry_decorator","title":"retry_decorator","text":"
def retry_decorator(number_of_retries: int,\nerror_message: str,\ndelay: float = 0,\nlogger_method: str = \"error\") -> Callable\n

Run function with several attempts.

Does not support async or coroutines!

Arguments:

  • number_of_retries: amount of attempts
  • error_message: message template with one {} for exception
  • delay: number of seconds to sleep between retries. default 0
  • logger_method: name of the logger method or callable to print logs

Returns:

the callable

"},{"location":"aea-framework-documentation/api/helpers/base/#exception_log_and_reraise","title":"exception_log_and_reraise","text":"
@contextlib.contextmanager\ndef exception_log_and_reraise(log_method: Callable, message: str) -> Generator\n

Run code in context to log and re raise exception.

Arguments:

  • log_method: function to print log
  • message: message template to add error text.

Returns:

the generator

"},{"location":"aea-framework-documentation/api/helpers/base/#recursive_update","title":"recursive_update","text":"
def recursive_update(to_update: Dict,\nnew_values: Dict,\nallow_new_values: bool = False) -> None\n

Update a dictionary by replacing conflicts with the new values.

It does side-effects to the first dictionary.

to_update = dict(a=1, b=2, subdict=dict(subfield1=1)) new_values = dict(b=3, subdict=dict(subfield1=2)) recursive_update(to_update, new_values) to_update {'a': 1, 'b': 3, 'subdict': {'subfield1': 2}}

Arguments:

  • to_update: the dictionary to update.
  • new_values: the dictionary of new values to replace.
  • allow_new_values: whether or not to allow new values.

"},{"location":"aea-framework-documentation/api/helpers/base/#find_topological_order","title":"find_topological_order","text":"
def find_topological_order(adjacency_list: Dict[T, Set[T]]) -> List[T]\n

Compute the topological order of a graph (using Kahn's algorithm).

Arguments:

  • adjacency_list: the adjacency list of the graph.

Raises:

  • ValueError: if the graph contains a cycle.

Returns:

the topological order for the graph (as a sequence of nodes)

"},{"location":"aea-framework-documentation/api/helpers/base/#reachable_nodes","title":"reachable_nodes","text":"
def reachable_nodes(adjacency_list: Dict[T, Set[T]],\nstarting_nodes: Set[T]) -> Dict[T, Set[T]]\n

Find the reachable subgraph induced by a set of starting nodes.

Arguments:

  • adjacency_list: the adjacency list of the full graph.
  • starting_nodes: the starting nodes of the new graph.

Returns:

the adjacency list of the subgraph.

"},{"location":"aea-framework-documentation/api/helpers/base/#cached_property-objects","title":"cached_property Objects","text":"
class cached_property()\n

Cached property from python3.8 functools.

"},{"location":"aea-framework-documentation/api/helpers/base/#__init___1","title":"__init__","text":"
def __init__(func: Callable) -> None\n

Init cached property.

"},{"location":"aea-framework-documentation/api/helpers/base/#__set_name__","title":"__set_name__","text":"
def __set_name__(_: Any, name: Any) -> None\n

Set name.

"},{"location":"aea-framework-documentation/api/helpers/base/#__get__","title":"__get__","text":"
def __get__(instance: Any, _: Optional[Any] = None) -> Any\n

Get instance.

"},{"location":"aea-framework-documentation/api/helpers/base/#ensure_dir","title":"ensure_dir","text":"
def ensure_dir(dir_path: str) -> None\n

Check if dir_path is a directory or create it.

"},{"location":"aea-framework-documentation/api/helpers/base/#dict_to_path_value","title":"dict_to_path_value","text":"
def dict_to_path_value(\ndata: Mapping,\npath: Optional[List] = None) -> Iterable[Tuple[List[str], Any]]\n

Convert dict to sequence of terminal path build of keys and value.

"},{"location":"aea-framework-documentation/api/helpers/base/#parse_datetime_from_str","title":"parse_datetime_from_str","text":"
def parse_datetime_from_str(date_string: str) -> datetime.datetime\n

Parse datetime from string.

"},{"location":"aea-framework-documentation/api/helpers/base/#certrequest-objects","title":"CertRequest Objects","text":"
class CertRequest()\n

Certificate request for proof of representation.

"},{"location":"aea-framework-documentation/api/helpers/base/#__init___2","title":"__init__","text":"
def __init__(public_key: str, identifier: SimpleIdOrStr,\nledger_id: SimpleIdOrStr, not_before: str, not_after: str,\nmessage_format: str, save_path: str) -> None\n

Initialize the certificate request.

Arguments:

  • public_key: the public key, or the key id.
  • identifier: certificate identifier.
  • ledger_id: ledger identifier the request is referring to.
  • not_before: specify the lower bound for certificate validity. If it is a string, it must follow the format: 'YYYY-MM-DD'. It will be interpreted as timezone UTC-0.
  • not_after: specify the lower bound for certificate validity. If it is a string, it must follow the format: 'YYYY-MM-DD'. It will be interpreted as timezone UTC-0.
  • message_format: message format used for signing
  • save_path: the save_path where to save the certificate.

"},{"location":"aea-framework-documentation/api/helpers/base/#public_key","title":"public_key","text":"
@property\ndef public_key() -> Optional[str]\n

Get the public key.

"},{"location":"aea-framework-documentation/api/helpers/base/#ledger_id","title":"ledger_id","text":"
@property\ndef ledger_id() -> str\n

Get the ledger id.

"},{"location":"aea-framework-documentation/api/helpers/base/#key_identifier","title":"key_identifier","text":"
@property\ndef key_identifier() -> Optional[str]\n

Get the key identifier.

"},{"location":"aea-framework-documentation/api/helpers/base/#identifier","title":"identifier","text":"
@property\ndef identifier() -> str\n

Get the identifier.

"},{"location":"aea-framework-documentation/api/helpers/base/#not_before_string","title":"not_before_string","text":"
@property\ndef not_before_string() -> str\n

Get the not_before field as string.

"},{"location":"aea-framework-documentation/api/helpers/base/#not_after_string","title":"not_after_string","text":"
@property\ndef not_after_string() -> str\n

Get the not_after field as string.

"},{"location":"aea-framework-documentation/api/helpers/base/#not_before","title":"not_before","text":"
@property\ndef not_before() -> datetime.datetime\n

Get the not_before field.

"},{"location":"aea-framework-documentation/api/helpers/base/#not_after","title":"not_after","text":"
@property\ndef not_after() -> datetime.datetime\n

Get the not_after field.

"},{"location":"aea-framework-documentation/api/helpers/base/#message_format","title":"message_format","text":"
@property\ndef message_format() -> str\n

Get the message format.

"},{"location":"aea-framework-documentation/api/helpers/base/#save_path","title":"save_path","text":"
@property\ndef save_path() -> Path\n

Get the save path for the certificate.

Note: if the path is not absolute, then the actual save path might depend on the context.

Returns:

the save path

"},{"location":"aea-framework-documentation/api/helpers/base/#get_absolute_save_path","title":"get_absolute_save_path","text":"
def get_absolute_save_path(path_prefix: Optional[PathLike] = None) -> Path\n

Get the absolute save path.

If save_path is an absolute path, then the prefix is ignored. Otherwise, the path prefix is prepended.

Arguments:

  • path_prefix: the (absolute) path to prepend to the save path.

Returns:

the actual save path.

"},{"location":"aea-framework-documentation/api/helpers/base/#public_key_or_identifier","title":"public_key_or_identifier","text":"
@property\ndef public_key_or_identifier() -> str\n

Get the public key or identifier.

"},{"location":"aea-framework-documentation/api/helpers/base/#get_message","title":"get_message","text":"
def get_message(public_key: str) -> bytes\n

Get the message to sign.

"},{"location":"aea-framework-documentation/api/helpers/base/#construct_message","title":"construct_message","text":"
@classmethod\ndef construct_message(cls, public_key: str, identifier: SimpleIdOrStr,\nnot_before_string: str, not_after_string: str,\nmessage_format: str) -> bytes\n

Construct message for singning.

Arguments:

  • public_key: the public key
  • identifier: identifier to be signed
  • not_before_string: signature not valid before
  • not_after_string: signature not valid after
  • message_format: message format used for signing

Returns:

the message

"},{"location":"aea-framework-documentation/api/helpers/base/#get_signature","title":"get_signature","text":"
def get_signature(path_prefix: Optional[PathLike] = None) -> str\n

Get signature from save_path.

Arguments:

  • path_prefix: the path prefix to be prepended to save_path. Defaults to cwd.

Returns:

the signature.

"},{"location":"aea-framework-documentation/api/helpers/base/#json","title":"json","text":"
@property\ndef json() -> Dict\n

Compute the JSON representation.

"},{"location":"aea-framework-documentation/api/helpers/base/#from_json","title":"from_json","text":"
@classmethod\ndef from_json(cls, obj: Dict) -> \"CertRequest\"\n

Compute the JSON representation.

"},{"location":"aea-framework-documentation/api/helpers/base/#__eq__","title":"__eq__","text":"
def __eq__(other: Any) -> bool\n

Check equality.

"},{"location":"aea-framework-documentation/api/helpers/base/#compute_specifier_from_version","title":"compute_specifier_from_version","text":"
def compute_specifier_from_version(version: Version) -> str\n

Compute the specifier set from a version.

version specifier is: >=major.minor.0, <next_major.0.0

Arguments:

  • version: the version

Returns:

the specifier set

"},{"location":"aea-framework-documentation/api/helpers/base/#decorator_with_optional_params","title":"decorator_with_optional_params","text":"
def decorator_with_optional_params(decorator: Callable) -> Callable\n

Make a decorator usable either with or without parameters.

In other words, if a decorator \"mydecorator\" is decorated with this decorator, It can be used both as:

@mydecorator def myfunction(): ...

or as:

@mydecorator(arg1, kwarg1=\"value\") def myfunction(): ...

Arguments:

  • decorator: a decorator callable

Returns:

a decorator callable

"},{"location":"aea-framework-documentation/api/helpers/base/#delete_directory_contents","title":"delete_directory_contents","text":"
def delete_directory_contents(directory: Path) -> None\n

Delete the content of a directory, without deleting it.

"},{"location":"aea-framework-documentation/api/helpers/base/#prepend_if_not_absolute","title":"prepend_if_not_absolute","text":"
def prepend_if_not_absolute(path: PathLike, prefix: PathLike) -> PathLike\n

Prepend a path with a prefix, but only if not absolute

Arguments:

  • path: the path to process.
  • prefix: the path prefix.

Returns:

the same path if absolute, else the prepended path.

"},{"location":"aea-framework-documentation/api/helpers/constants/","title":"Constants","text":""},{"location":"aea-framework-documentation/api/helpers/constants/#aeahelpersconstants","title":"aea.helpers.constants","text":"

Module with helpers constants.

"},{"location":"aea-framework-documentation/api/helpers/env_vars/","title":"Env Vars","text":""},{"location":"aea-framework-documentation/api/helpers/env_vars/#aeahelpersenv_vars","title":"aea.helpers.env_vars","text":"

Implementation of the environment variables support.

"},{"location":"aea-framework-documentation/api/helpers/env_vars/#is_env_variable","title":"is_env_variable","text":"
def is_env_variable(value: Any) -> bool\n

Check is variable string with env variable pattern.

"},{"location":"aea-framework-documentation/api/helpers/env_vars/#replace_with_env_var","title":"replace_with_env_var","text":"
def replace_with_env_var(value: str,\nenv_variables: dict,\ndefault_value: Any = NotSet) -> JSON_TYPES\n

Replace env var with value.

"},{"location":"aea-framework-documentation/api/helpers/env_vars/#apply_env_variables","title":"apply_env_variables","text":"
def apply_env_variables(data: Union[Dict, List[Dict]],\nenv_variables: Mapping[str, Any],\ndefault_value: Any = NotSet) -> JSON_TYPES\n

Create new resulting dict with env variables applied.

"},{"location":"aea-framework-documentation/api/helpers/env_vars/#convert_value_str_to_type","title":"convert_value_str_to_type","text":"
def convert_value_str_to_type(value: str, type_str: str) -> JSON_TYPES\n

Convert value by type name to native python type.

"},{"location":"aea-framework-documentation/api/helpers/exception_policy/","title":"Exception Policy","text":""},{"location":"aea-framework-documentation/api/helpers/exception_policy/#aeahelpersexception_policy","title":"aea.helpers.exception_policy","text":"

This module contains enum of aea exception policies.

"},{"location":"aea-framework-documentation/api/helpers/exception_policy/#exceptionpolicyenum-objects","title":"ExceptionPolicyEnum Objects","text":"
class ExceptionPolicyEnum(Enum)\n

AEA Exception policies.

"},{"location":"aea-framework-documentation/api/helpers/exec_timeout/","title":"Exec Timeout","text":""},{"location":"aea-framework-documentation/api/helpers/exec_timeout/#aeahelpersexec_timeout","title":"aea.helpers.exec_timeout","text":"

Python code execution time limit tools.

"},{"location":"aea-framework-documentation/api/helpers/exec_timeout/#timeoutresult-objects","title":"TimeoutResult Objects","text":"
class TimeoutResult()\n

Result of ExecTimeout context manager.

"},{"location":"aea-framework-documentation/api/helpers/exec_timeout/#__init__","title":"__init__","text":"
def __init__() -> None\n

Init.

"},{"location":"aea-framework-documentation/api/helpers/exec_timeout/#set_cancelled_by_timeout","title":"set_cancelled_by_timeout","text":"
def set_cancelled_by_timeout() -> None\n

Set code was terminated cause timeout.

"},{"location":"aea-framework-documentation/api/helpers/exec_timeout/#is_cancelled_by_timeout","title":"is_cancelled_by_timeout","text":"
def is_cancelled_by_timeout() -> bool\n

Return True if code was terminated by ExecTimeout cause timeout.

Returns:

bool

"},{"location":"aea-framework-documentation/api/helpers/exec_timeout/#timeoutexception-objects","title":"TimeoutException Objects","text":"
class TimeoutException(BaseException)\n

TimeoutException raised by ExecTimeout context managers in thread with limited execution time.

Used internally, does not propagated outside of context manager

"},{"location":"aea-framework-documentation/api/helpers/exec_timeout/#baseexectimeout-objects","title":"BaseExecTimeout Objects","text":"
class BaseExecTimeout(ABC)\n

Base class for implementing context managers to limit python code execution time.

exception_class - is exception type to raise in code controlled in case of timeout.

"},{"location":"aea-framework-documentation/api/helpers/exec_timeout/#__init___1","title":"__init__","text":"
def __init__(timeout: float = 0.0) -> None\n

Init.

Arguments:

  • timeout: number of seconds to execute code before interruption

"},{"location":"aea-framework-documentation/api/helpers/exec_timeout/#__enter__","title":"__enter__","text":"
def __enter__() -> TimeoutResult\n

Enter context manager.

Returns:

TimeoutResult

"},{"location":"aea-framework-documentation/api/helpers/exec_timeout/#__exit__","title":"__exit__","text":"
def __exit__(exc_type: Type[Exception], exc_val: Exception,\nexc_tb: TracebackType) -> None\n

Exit context manager.

Arguments:

  • exc_type: the exception type
  • exc_val: the exception
  • exc_tb: the traceback

"},{"location":"aea-framework-documentation/api/helpers/exec_timeout/#exectimeoutsigalarm-objects","title":"ExecTimeoutSigAlarm Objects","text":"
class ExecTimeoutSigAlarm(BaseExecTimeout)\n

ExecTimeout context manager implementation using signals and SIGALARM.

Does not support threads, have to be used only in main thread.

"},{"location":"aea-framework-documentation/api/helpers/exec_timeout/#exectimeoutthreadguard-objects","title":"ExecTimeoutThreadGuard Objects","text":"
class ExecTimeoutThreadGuard(BaseExecTimeout)\n

ExecTimeout context manager implementation using threads and PyThreadState_SetAsyncExc.

Support threads. Requires supervisor thread start/stop to control execution time control. Possible will be not accurate in case of long c functions used inside code controlled.

"},{"location":"aea-framework-documentation/api/helpers/exec_timeout/#__init___2","title":"__init__","text":"
def __init__(timeout: float = 0.0) -> None\n

Init ExecTimeoutThreadGuard variables.

Arguments:

  • timeout: number of seconds to execute code before interruption

"},{"location":"aea-framework-documentation/api/helpers/exec_timeout/#start","title":"start","text":"
@classmethod\ndef start(cls) -> None\n

Start supervisor thread to check timeouts.

Supervisor starts once but number of start counted.

"},{"location":"aea-framework-documentation/api/helpers/exec_timeout/#stop","title":"stop","text":"
@classmethod\ndef stop(cls, force: bool = False) -> None\n

Stop supervisor thread.

Actual stop performed on force == True or if number of stops == number of starts

Arguments:

  • force: force stop regardless number of start.
"},{"location":"aea-framework-documentation/api/helpers/file_io/","title":"File IO","text":""},{"location":"aea-framework-documentation/api/helpers/file_io/#aeahelpersfile_io","title":"aea.helpers.file_io","text":"

Read to and write from file with envelopes.

"},{"location":"aea-framework-documentation/api/helpers/file_io/#lock_file","title":"lock_file","text":"
@contextmanager\ndef lock_file(file_descriptor: IO[bytes],\nlogger: Logger = _default_logger) -> Generator\n

Lock file in context manager.

Arguments:

  • file_descriptor: file descriptor of file to lock.
  • logger: the logger.

Returns:

generator

"},{"location":"aea-framework-documentation/api/helpers/file_io/#write_envelope","title":"write_envelope","text":"
def write_envelope(envelope: Envelope,\nfile_pointer: IO[bytes],\nseparator: bytes = SEPARATOR,\nlogger: Logger = _default_logger) -> None\n

Write envelope to file.

"},{"location":"aea-framework-documentation/api/helpers/file_io/#write_with_lock","title":"write_with_lock","text":"
def write_with_lock(file_pointer: IO[bytes],\ndata: Union[bytes],\nlogger: Logger = _default_logger) -> None\n

Write bytes to file protected with file lock.

"},{"location":"aea-framework-documentation/api/helpers/file_io/#envelope_from_bytes","title":"envelope_from_bytes","text":"
def envelope_from_bytes(\nbytes_: bytes,\nseparator: bytes = SEPARATOR,\nlogger: Logger = _default_logger) -> Optional[Envelope]\n

Decode bytes to get the envelope.

Arguments:

  • bytes_: the encoded envelope
  • separator: the separator used
  • logger: the logger

Returns:

Envelope

"},{"location":"aea-framework-documentation/api/helpers/file_lock/","title":"File Lock","text":""},{"location":"aea-framework-documentation/api/helpers/file_lock/#aeahelpersfile_lock","title":"aea.helpers.file_lock","text":"

Patch of 'fnctl' to make it compatible with Windows.

"},{"location":"aea-framework-documentation/api/helpers/http_requests/","title":"HttpRequests","text":""},{"location":"aea-framework-documentation/api/helpers/http_requests/#aeahelpershttp_requests","title":"aea.helpers.http_requests","text":"

Wrapper over requests library.

"},{"location":"aea-framework-documentation/api/helpers/http_requests/#add_default_timeout","title":"add_default_timeout","text":"
def add_default_timeout(fn: Callable, timeout: float) -> Callable\n

Add default timeout for requests methods.

"},{"location":"aea-framework-documentation/api/helpers/install_dependency/","title":"Install Dependency","text":""},{"location":"aea-framework-documentation/api/helpers/install_dependency/#aeahelpersinstall_dependency","title":"aea.helpers.install_dependency","text":"

Helper to install python dependencies.

"},{"location":"aea-framework-documentation/api/helpers/install_dependency/#install_dependency","title":"install_dependency","text":"
def install_dependency(dependency_name: str,\ndependency: Dependency,\nlogger: Logger,\ninstall_timeout: float = 300) -> None\n

Install python dependency to the current python environment.

Arguments:

  • dependency_name: name of the python package
  • dependency: Dependency specification
  • logger: the logger
  • install_timeout: timeout to wait pip to install

"},{"location":"aea-framework-documentation/api/helpers/install_dependency/#install_dependencies","title":"install_dependencies","text":"
def install_dependencies(dependencies: List[Dependency],\nlogger: Logger,\ninstall_timeout: float = 300) -> None\n

Install python dependencies to the current python environment.

Arguments:

  • dependencies: dict of dependency name and specification
  • logger: the logger
  • install_timeout: timeout to wait pip to install

"},{"location":"aea-framework-documentation/api/helpers/install_dependency/#call_pip","title":"call_pip","text":"
def call_pip(pip_args: List[str],\ntimeout: float = 300,\nretry: bool = False) -> None\n

Run pip install command.

Arguments:

  • pip_args: list strings of the command
  • timeout: timeout to wait pip to install
  • retry: bool, try one more time if command failed

"},{"location":"aea-framework-documentation/api/helpers/install_dependency/#run_install_subprocess","title":"run_install_subprocess","text":"
def run_install_subprocess(install_command: List[str],\ninstall_timeout: float = 300) -> int\n

Try executing install command.

Arguments:

  • install_command: list strings of the command
  • install_timeout: timeout to wait pip to install

Returns:

the return code of the subprocess

"},{"location":"aea-framework-documentation/api/helpers/io/","title":"IO","text":""},{"location":"aea-framework-documentation/api/helpers/io/#aeahelpersio","title":"aea.helpers.io","text":""},{"location":"aea-framework-documentation/api/helpers/logging/","title":"Logging","text":""},{"location":"aea-framework-documentation/api/helpers/logging/#aeahelperslogging","title":"aea.helpers.logging","text":"

Logging helpers.

"},{"location":"aea-framework-documentation/api/helpers/logging/#get_logger","title":"get_logger","text":"
def get_logger(module_path: str, agent_name: str) -> Logger\n

Get the logger based on a module path and agent name.

"},{"location":"aea-framework-documentation/api/helpers/logging/#agentloggeradapter-objects","title":"AgentLoggerAdapter Objects","text":"
class AgentLoggerAdapter(LoggerAdapter)\n

This class is a logger adapter that prepends the agent name to log messages.

"},{"location":"aea-framework-documentation/api/helpers/logging/#__init__","title":"__init__","text":"
def __init__(logger: Logger, agent_name: str) -> None\n

Initialize the logger adapter.

Arguments:

  • logger: the logger.
  • agent_name: the agent name.

"},{"location":"aea-framework-documentation/api/helpers/logging/#process","title":"process","text":"
def process(\nmsg: Any,\nkwargs: MutableMapping[str,\nAny]) -> Tuple[Any, MutableMapping[str, Any]]\n

Prepend the agent name to every log message.

"},{"location":"aea-framework-documentation/api/helpers/logging/#withlogger-objects","title":"WithLogger Objects","text":"
class WithLogger()\n

Interface to endow subclasses with a logger.

"},{"location":"aea-framework-documentation/api/helpers/logging/#__init___1","title":"__init__","text":"
def __init__(logger: Optional[Logger] = None,\ndefault_logger_name: str = \"aea\") -> None\n

Initialize the logger.

Arguments:

  • logger: the logger object.
  • default_logger_name: the default logger name, if a logger is not provided.

"},{"location":"aea-framework-documentation/api/helpers/logging/#logger","title":"logger","text":"
@property\ndef logger() -> Logger\n

Get the component logger.

"},{"location":"aea-framework-documentation/api/helpers/logging/#logger_1","title":"logger","text":"
@logger.setter\ndef logger(logger: Optional[Logger]) -> None\n

Set the logger.

"},{"location":"aea-framework-documentation/api/helpers/multiple_executor/","title":"MultipleExecutor","text":""},{"location":"aea-framework-documentation/api/helpers/multiple_executor/#aeahelpersmultiple_executor","title":"aea.helpers.multiple_executor","text":"

This module contains the helpers to run multiple stoppable tasks in different modes: async, threaded, multiprocess .

"},{"location":"aea-framework-documentation/api/helpers/multiple_executor/#executorexceptionpolicies-objects","title":"ExecutorExceptionPolicies Objects","text":"
class ExecutorExceptionPolicies(Enum)\n

Runner exception policy modes.

"},{"location":"aea-framework-documentation/api/helpers/multiple_executor/#abstractexecutortask-objects","title":"AbstractExecutorTask Objects","text":"
class AbstractExecutorTask(ABC)\n

Abstract task class to create Task classes.

"},{"location":"aea-framework-documentation/api/helpers/multiple_executor/#__init__","title":"__init__","text":"
def __init__() -> None\n

Init task.

"},{"location":"aea-framework-documentation/api/helpers/multiple_executor/#future","title":"future","text":"
@property\ndef future() -> Optional[TaskAwaitable]\n

Return awaitable to get result of task execution.

"},{"location":"aea-framework-documentation/api/helpers/multiple_executor/#future_1","title":"future","text":"
@future.setter\ndef future(future: TaskAwaitable) -> None\n

Set awaitable to get result of task execution.

"},{"location":"aea-framework-documentation/api/helpers/multiple_executor/#start","title":"start","text":"
@abstractmethod\ndef start() -> Tuple[Callable, Sequence[Any]]\n

Implement start task function here.

"},{"location":"aea-framework-documentation/api/helpers/multiple_executor/#stop","title":"stop","text":"
@abstractmethod\ndef stop() -> None\n

Implement stop task function here.

"},{"location":"aea-framework-documentation/api/helpers/multiple_executor/#create_async_task","title":"create_async_task","text":"
@abstractmethod\ndef create_async_task(loop: AbstractEventLoop) -> TaskAwaitable\n

Create asyncio task for task run in asyncio loop.

Arguments:

  • loop: the event loop

Returns:

task to run in asyncio loop.

"},{"location":"aea-framework-documentation/api/helpers/multiple_executor/#id","title":"id","text":"
@property\ndef id() -> Any\n

Return task id.

"},{"location":"aea-framework-documentation/api/helpers/multiple_executor/#failed","title":"failed","text":"
@property\ndef failed() -> bool\n

Return was exception failed or not.

If it's running it's not failed.

Returns:

bool

"},{"location":"aea-framework-documentation/api/helpers/multiple_executor/#abstractmultiprocessexecutortask-objects","title":"AbstractMultiprocessExecutorTask Objects","text":"
class AbstractMultiprocessExecutorTask(AbstractExecutorTask)\n

Task for multiprocess executor.

"},{"location":"aea-framework-documentation/api/helpers/multiple_executor/#start_1","title":"start","text":"
@abstractmethod\ndef start() -> Tuple[Callable, Sequence[Any]]\n

Return function and arguments to call within subprocess.

"},{"location":"aea-framework-documentation/api/helpers/multiple_executor/#create_async_task_1","title":"create_async_task","text":"
def create_async_task(loop: AbstractEventLoop) -> TaskAwaitable\n

Create asyncio task for task run in asyncio loop.

Raise error, cause async mode is not supported, cause this task for multiprocess executor only.

Arguments:

  • loop: the event loop

Raises:

  • ValueError: async task construction not possible

"},{"location":"aea-framework-documentation/api/helpers/multiple_executor/#abstractmultipleexecutor-objects","title":"AbstractMultipleExecutor Objects","text":"
class AbstractMultipleExecutor(ABC)\n

Abstract class to create multiple executors classes.

"},{"location":"aea-framework-documentation/api/helpers/multiple_executor/#__init___1","title":"__init__","text":"
def __init__(\ntasks: Sequence[AbstractExecutorTask],\ntask_fail_policy: ExecutorExceptionPolicies = ExecutorExceptionPolicies.\npropagate\n) -> None\n

Init executor.

Arguments:

  • tasks: sequence of AbstractExecutorTask instances to run.
  • task_fail_policy: the exception policy of all the tasks

"},{"location":"aea-framework-documentation/api/helpers/multiple_executor/#is_running","title":"is_running","text":"
@property\ndef is_running() -> bool\n

Return running state of the executor.

"},{"location":"aea-framework-documentation/api/helpers/multiple_executor/#start_2","title":"start","text":"
def start() -> None\n

Start tasks.

"},{"location":"aea-framework-documentation/api/helpers/multiple_executor/#stop_1","title":"stop","text":"
def stop() -> None\n

Stop tasks.

"},{"location":"aea-framework-documentation/api/helpers/multiple_executor/#num_failed","title":"num_failed","text":"
@property\ndef num_failed() -> int\n

Return number of failed tasks.

"},{"location":"aea-framework-documentation/api/helpers/multiple_executor/#failed_tasks","title":"failed_tasks","text":"
@property\ndef failed_tasks() -> Sequence[AbstractExecutorTask]\n

Return sequence failed tasks.

"},{"location":"aea-framework-documentation/api/helpers/multiple_executor/#not_failed_tasks","title":"not_failed_tasks","text":"
@property\ndef not_failed_tasks() -> Sequence[AbstractExecutorTask]\n

Return sequence successful tasks.

"},{"location":"aea-framework-documentation/api/helpers/multiple_executor/#threadexecutor-objects","title":"ThreadExecutor Objects","text":"
class ThreadExecutor(AbstractMultipleExecutor)\n

Thread based executor to run multiple agents in threads.

"},{"location":"aea-framework-documentation/api/helpers/multiple_executor/#processexecutor-objects","title":"ProcessExecutor Objects","text":"
class ProcessExecutor(ThreadExecutor)\n

Subprocess based executor to run multiple agents in threads.

"},{"location":"aea-framework-documentation/api/helpers/multiple_executor/#asyncexecutor-objects","title":"AsyncExecutor Objects","text":"
class AsyncExecutor(AbstractMultipleExecutor)\n

Thread based executor to run multiple agents in threads.

"},{"location":"aea-framework-documentation/api/helpers/multiple_executor/#abstractmultiplerunner-objects","title":"AbstractMultipleRunner Objects","text":"
class AbstractMultipleRunner()\n

Abstract multiple runner to create classes to launch tasks with selected mode.

"},{"location":"aea-framework-documentation/api/helpers/multiple_executor/#__init___2","title":"__init__","text":"
def __init__(\nmode: str,\nfail_policy: ExecutorExceptionPolicies = ExecutorExceptionPolicies.\npropagate\n) -> None\n

Init with selected executor mode.

Arguments:

  • mode: one of supported executor modes
  • fail_policy: one of ExecutorExceptionPolicies to be used with Executor

"},{"location":"aea-framework-documentation/api/helpers/multiple_executor/#is_running_1","title":"is_running","text":"
@property\ndef is_running() -> bool\n

Return state of the executor.

"},{"location":"aea-framework-documentation/api/helpers/multiple_executor/#start_3","title":"start","text":"
def start(threaded: bool = False) -> None\n

Run agents.

Arguments:

  • threaded: run in dedicated thread without blocking current thread.

"},{"location":"aea-framework-documentation/api/helpers/multiple_executor/#stop_2","title":"stop","text":"
def stop(timeout: Optional[float] = None) -> None\n

Stop agents.

Arguments:

  • timeout: timeout in seconds to wait thread stopped, only if started in thread mode.

"},{"location":"aea-framework-documentation/api/helpers/multiple_executor/#num_failed_1","title":"num_failed","text":"
@property\ndef num_failed() -> int\n

Return number of failed tasks.

"},{"location":"aea-framework-documentation/api/helpers/multiple_executor/#failed_1","title":"failed","text":"
@property\ndef failed() -> Sequence[Task]\n

Return sequence failed tasks.

"},{"location":"aea-framework-documentation/api/helpers/multiple_executor/#not_failed","title":"not_failed","text":"
@property\ndef not_failed() -> Sequence[Task]\n

Return sequence successful tasks.

"},{"location":"aea-framework-documentation/api/helpers/multiple_executor/#try_join_thread","title":"try_join_thread","text":"
def try_join_thread() -> None\n

Try to join thread if running in thread mode.

"},{"location":"aea-framework-documentation/api/helpers/pipe/","title":"Pipe","text":""},{"location":"aea-framework-documentation/api/helpers/pipe/#aeahelperspipe","title":"aea.helpers.pipe","text":"

Portable pipe implementation for Linux, MacOS, and Windows.

"},{"location":"aea-framework-documentation/api/helpers/pipe/#ipcchannelclient-objects","title":"IPCChannelClient Objects","text":"
class IPCChannelClient(ABC)\n

Multi-platform interprocess communication channel for the client side.

"},{"location":"aea-framework-documentation/api/helpers/pipe/#connect","title":"connect","text":"
@abstractmethod\nasync def connect(timeout: float = PIPE_CONN_TIMEOUT) -> bool\n

Connect to communication channel

Arguments:

  • timeout: timeout for other end to connect

Returns:

connection status

"},{"location":"aea-framework-documentation/api/helpers/pipe/#write","title":"write","text":"
@abstractmethod\nasync def write(data: bytes) -> None\n

Write data bytes to the other end of the channel

Will first write the size than the actual data

Arguments:

  • data: bytes to write

"},{"location":"aea-framework-documentation/api/helpers/pipe/#read","title":"read","text":"
@abstractmethod\nasync def read() -> Optional[bytes]\n

Read bytes from the other end of the channel

Will first read the size than the actual data

Returns:

read bytes

"},{"location":"aea-framework-documentation/api/helpers/pipe/#close","title":"close","text":"
@abstractmethod\nasync def close() -> None\n

Close the communication channel.

"},{"location":"aea-framework-documentation/api/helpers/pipe/#ipcchannel-objects","title":"IPCChannel Objects","text":"
class IPCChannel(IPCChannelClient)\n

Multi-platform interprocess communication channel.

"},{"location":"aea-framework-documentation/api/helpers/pipe/#in_path","title":"in_path","text":"
@property\n@abstractmethod\ndef in_path() -> str\n

Rendezvous point for incoming communication.

Returns:

path

"},{"location":"aea-framework-documentation/api/helpers/pipe/#out_path","title":"out_path","text":"
@property\n@abstractmethod\ndef out_path() -> str\n

Rendezvous point for outgoing communication.

Returns:

path

"},{"location":"aea-framework-documentation/api/helpers/pipe/#posixnamedpipeprotocol-objects","title":"PosixNamedPipeProtocol Objects","text":"
class PosixNamedPipeProtocol()\n

Posix named pipes async wrapper communication protocol.

"},{"location":"aea-framework-documentation/api/helpers/pipe/#__init__","title":"__init__","text":"
def __init__(in_path: str,\nout_path: str,\nlogger: logging.Logger = _default_logger,\nloop: Optional[AbstractEventLoop] = None) -> None\n

Initialize a new posix named pipe.

Arguments:

  • in_path: rendezvous point for incoming data
  • out_path: rendezvous point for outgoing data
  • logger: the logger
  • loop: the event loop

"},{"location":"aea-framework-documentation/api/helpers/pipe/#connect_1","title":"connect","text":"
async def connect(timeout: float = PIPE_CONN_TIMEOUT) -> bool\n

Connect to the other end of the pipe

Arguments:

  • timeout: timeout before failing

Returns:

connection success

"},{"location":"aea-framework-documentation/api/helpers/pipe/#write_1","title":"write","text":"
async def write(data: bytes) -> None\n

Write to pipe.

Arguments:

  • data: bytes to write to pipe

"},{"location":"aea-framework-documentation/api/helpers/pipe/#read_1","title":"read","text":"
async def read() -> Optional[bytes]\n

Read from pipe.

Returns:

read bytes

"},{"location":"aea-framework-documentation/api/helpers/pipe/#close_1","title":"close","text":"
async def close() -> None\n

Disconnect pipe.

"},{"location":"aea-framework-documentation/api/helpers/pipe/#tcpsocketprotocol-objects","title":"TCPSocketProtocol Objects","text":"
class TCPSocketProtocol()\n

TCP socket communication protocol.

"},{"location":"aea-framework-documentation/api/helpers/pipe/#__init___1","title":"__init__","text":"
def __init__(reader: asyncio.StreamReader,\nwriter: asyncio.StreamWriter,\nlogger: logging.Logger = _default_logger,\nloop: Optional[AbstractEventLoop] = None) -> None\n

Initialize the tcp socket protocol.

Arguments:

  • reader: established asyncio reader
  • writer: established asyncio writer
  • logger: the logger
  • loop: the event loop

"},{"location":"aea-framework-documentation/api/helpers/pipe/#writer","title":"writer","text":"
@property\ndef writer() -> StreamWriter\n

Get a writer associated with protocol.

"},{"location":"aea-framework-documentation/api/helpers/pipe/#write_2","title":"write","text":"
async def write(data: bytes) -> None\n

Write to socket.

Arguments:

  • data: bytes to write

"},{"location":"aea-framework-documentation/api/helpers/pipe/#read_2","title":"read","text":"
async def read() -> Optional[bytes]\n

Read from socket.

Returns:

read bytes

"},{"location":"aea-framework-documentation/api/helpers/pipe/#close_2","title":"close","text":"
async def close() -> None\n

Disconnect socket.

"},{"location":"aea-framework-documentation/api/helpers/pipe/#tcpsocketchannel-objects","title":"TCPSocketChannel Objects","text":"
class TCPSocketChannel(IPCChannel)\n

Interprocess communication channel implementation using tcp sockets.

"},{"location":"aea-framework-documentation/api/helpers/pipe/#__init___2","title":"__init__","text":"
def __init__(logger: logging.Logger = _default_logger,\nloop: Optional[AbstractEventLoop] = None) -> None\n

Initialize tcp socket interprocess communication channel.

"},{"location":"aea-framework-documentation/api/helpers/pipe/#connect_2","title":"connect","text":"
async def connect(timeout: float = PIPE_CONN_TIMEOUT) -> bool\n

Setup communication channel and wait for other end to connect.

Arguments:

  • timeout: timeout for the connection to be established

Returns:

connection status

"},{"location":"aea-framework-documentation/api/helpers/pipe/#write_3","title":"write","text":"
async def write(data: bytes) -> None\n

Write to channel.

Arguments:

  • data: bytes to write

"},{"location":"aea-framework-documentation/api/helpers/pipe/#read_3","title":"read","text":"
async def read() -> Optional[bytes]\n

Read from channel.

Returns:

read bytes

"},{"location":"aea-framework-documentation/api/helpers/pipe/#close_3","title":"close","text":"
async def close() -> None\n

Disconnect from channel and clean it up.

"},{"location":"aea-framework-documentation/api/helpers/pipe/#in_path_1","title":"in_path","text":"
@property\ndef in_path() -> str\n

Rendezvous point for incoming communication.

"},{"location":"aea-framework-documentation/api/helpers/pipe/#out_path_1","title":"out_path","text":"
@property\ndef out_path() -> str\n

Rendezvous point for outgoing communication.

"},{"location":"aea-framework-documentation/api/helpers/pipe/#posixnamedpipechannel-objects","title":"PosixNamedPipeChannel Objects","text":"
class PosixNamedPipeChannel(IPCChannel)\n

Interprocess communication channel implementation using Posix named pipes.

"},{"location":"aea-framework-documentation/api/helpers/pipe/#__init___3","title":"__init__","text":"
def __init__(logger: logging.Logger = _default_logger,\nloop: Optional[AbstractEventLoop] = None) -> None\n

Initialize posix named pipe interprocess communication channel.

"},{"location":"aea-framework-documentation/api/helpers/pipe/#connect_3","title":"connect","text":"
async def connect(timeout: float = PIPE_CONN_TIMEOUT) -> bool\n

Setup communication channel and wait for other end to connect.

Arguments:

  • timeout: timeout for connection to be established

Returns:

bool, indicating success

"},{"location":"aea-framework-documentation/api/helpers/pipe/#write_4","title":"write","text":"
async def write(data: bytes) -> None\n

Write to the channel.

Arguments:

  • data: data to write to channel

"},{"location":"aea-framework-documentation/api/helpers/pipe/#read_4","title":"read","text":"
async def read() -> Optional[bytes]\n

Read from the channel.

Returns:

read bytes

"},{"location":"aea-framework-documentation/api/helpers/pipe/#close_4","title":"close","text":"
async def close() -> None\n

Close the channel and clean it up.

"},{"location":"aea-framework-documentation/api/helpers/pipe/#in_path_2","title":"in_path","text":"
@property\ndef in_path() -> str\n

Rendezvous point for incoming communication.

"},{"location":"aea-framework-documentation/api/helpers/pipe/#out_path_2","title":"out_path","text":"
@property\ndef out_path() -> str\n

Rendezvous point for outgoing communication.

"},{"location":"aea-framework-documentation/api/helpers/pipe/#tcpsocketchannelclient-objects","title":"TCPSocketChannelClient Objects","text":"
class TCPSocketChannelClient(IPCChannelClient)\n

Interprocess communication channel client using tcp sockets.

"},{"location":"aea-framework-documentation/api/helpers/pipe/#__init___4","title":"__init__","text":"
def __init__(in_path: str,\nout_path: str,\nlogger: logging.Logger = _default_logger,\nloop: Optional[AbstractEventLoop] = None) -> None\n

Initialize a tcp socket communication channel client.

Arguments:

  • in_path: rendezvous point for incoming data
  • out_path: rendezvous point for outgoing data
  • logger: the logger
  • loop: the event loop

"},{"location":"aea-framework-documentation/api/helpers/pipe/#connect_4","title":"connect","text":"
async def connect(timeout: float = PIPE_CONN_TIMEOUT) -> bool\n

Connect to the other end of the communication channel.

Arguments:

  • timeout: timeout for connection to be established

Returns:

connection status

"},{"location":"aea-framework-documentation/api/helpers/pipe/#write_5","title":"write","text":"
async def write(data: bytes) -> None\n

Write data to channel.

Arguments:

  • data: bytes to write

"},{"location":"aea-framework-documentation/api/helpers/pipe/#read_5","title":"read","text":"
async def read() -> Optional[bytes]\n

Read data from channel.

Returns:

read bytes

"},{"location":"aea-framework-documentation/api/helpers/pipe/#close_5","title":"close","text":"
async def close() -> None\n

Disconnect from communication channel.

"},{"location":"aea-framework-documentation/api/helpers/pipe/#posixnamedpipechannelclient-objects","title":"PosixNamedPipeChannelClient Objects","text":"
class PosixNamedPipeChannelClient(IPCChannelClient)\n

Interprocess communication channel client using Posix named pipes.

"},{"location":"aea-framework-documentation/api/helpers/pipe/#__init___5","title":"__init__","text":"
def __init__(in_path: str,\nout_path: str,\nlogger: logging.Logger = _default_logger,\nloop: Optional[AbstractEventLoop] = None) -> None\n

Initialize a posix named pipe communication channel client.

Arguments:

  • in_path: rendezvous point for incoming data
  • out_path: rendezvous point for outgoing data
  • logger: the logger
  • loop: the event loop

"},{"location":"aea-framework-documentation/api/helpers/pipe/#connect_5","title":"connect","text":"
async def connect(timeout: float = PIPE_CONN_TIMEOUT) -> bool\n

Connect to the other end of the communication channel.

Arguments:

  • timeout: timeout for connection to be established

Returns:

connection status

"},{"location":"aea-framework-documentation/api/helpers/pipe/#write_6","title":"write","text":"
async def write(data: bytes) -> None\n

Write data to channel.

Arguments:

  • data: bytes to write

"},{"location":"aea-framework-documentation/api/helpers/pipe/#read_6","title":"read","text":"
async def read() -> Optional[bytes]\n

Read data from channel.

Returns:

read bytes

"},{"location":"aea-framework-documentation/api/helpers/pipe/#close_6","title":"close","text":"
async def close() -> None\n

Disconnect from communication channel.

"},{"location":"aea-framework-documentation/api/helpers/pipe/#make_ipc_channel","title":"make_ipc_channel","text":"
def make_ipc_channel(logger: logging.Logger = _default_logger,\nloop: Optional[AbstractEventLoop] = None) -> IPCChannel\n

Build a portable bidirectional InterProcess Communication channel

Arguments:

  • logger: the logger
  • loop: the loop

Returns:

IPCChannel

"},{"location":"aea-framework-documentation/api/helpers/pipe/#make_ipc_channel_client","title":"make_ipc_channel_client","text":"
def make_ipc_channel_client(\nin_path: str,\nout_path: str,\nlogger: logging.Logger = _default_logger,\nloop: Optional[AbstractEventLoop] = None) -> IPCChannelClient\n

Build a portable bidirectional InterProcess Communication client channel

Arguments:

  • in_path: rendezvous point for incoming communication
  • out_path: rendezvous point for outgoing outgoing
  • logger: the logger
  • loop: the loop

Returns:

IPCChannel

"},{"location":"aea-framework-documentation/api/helpers/profiling/","title":"Profiling","text":""},{"location":"aea-framework-documentation/api/helpers/profiling/#aeahelpersprofiling","title":"aea.helpers.profiling","text":"

Implementation of background profiling daemon.

"},{"location":"aea-framework-documentation/api/helpers/profiling/#profiling-objects","title":"Profiling Objects","text":"
class Profiling(Runnable)\n

Profiling service.

"},{"location":"aea-framework-documentation/api/helpers/profiling/#__init__","title":"__init__","text":"
def __init__(\nperiod: int = 0,\nobjects_instances_to_count: List[Type] = None,\nobjects_created_to_count: List[Type] = None,\noutput_function: Callable[[str], None] = lambda x: print(x, flush=True)\n) -> None\n

Init profiler.

Arguments:

  • period: delay between profiling output in seconds.
  • objects_instances_to_count: object to count
  • objects_created_to_count: object created to count
  • output_function: function to display output, one str argument.

"},{"location":"aea-framework-documentation/api/helpers/profiling/#set_counters","title":"set_counters","text":"
def set_counters() -> None\n

Modify obj.new to count objects created created.

"},{"location":"aea-framework-documentation/api/helpers/profiling/#run","title":"run","text":"
async def run() -> None\n

Run profiling.

"},{"location":"aea-framework-documentation/api/helpers/profiling/#output_profile_data","title":"output_profile_data","text":"
def output_profile_data() -> None\n

Render profiling data and call output_function.

"},{"location":"aea-framework-documentation/api/helpers/profiling/#get_profile_data","title":"get_profile_data","text":"
def get_profile_data() -> Dict\n

Get profiling data dict.

"},{"location":"aea-framework-documentation/api/helpers/profiling/#get_objects_instances","title":"get_objects_instances","text":"
def get_objects_instances() -> Dict\n

Return dict with counted object instances present now.

"},{"location":"aea-framework-documentation/api/helpers/profiling/#get_objecst_created","title":"get_objecst_created","text":"
def get_objecst_created() -> Dict\n

Return dict with counted object instances created.

"},{"location":"aea-framework-documentation/api/helpers/serializers/","title":"Serializers","text":""},{"location":"aea-framework-documentation/api/helpers/serializers/#aeahelpersserializers","title":"aea.helpers.serializers","text":"

This module contains Serializers that can be used for custom types.

"},{"location":"aea-framework-documentation/api/helpers/serializers/#dictprotobufstructserializer-objects","title":"DictProtobufStructSerializer Objects","text":"
class DictProtobufStructSerializer()\n

Serialize python dictionaries of type DictType = Dict[str, ValueType] recursively conserving their dynamic type, using google.protobuf.Struct

ValueType = PrimitiveType | DictType | List[ValueType]] PrimitiveType = bool | int | float | str | bytes

"},{"location":"aea-framework-documentation/api/helpers/serializers/#encode","title":"encode","text":"
@classmethod\ndef encode(cls, dictionary: Dict[str, Any]) -> bytes\n

Serialize compatible dictionary to bytes.

Copies entire dictionary in the process.

Arguments:

  • dictionary: the dictionary to serialize

Returns:

serialized bytes string

"},{"location":"aea-framework-documentation/api/helpers/serializers/#decode","title":"decode","text":"
@classmethod\ndef decode(cls, buffer: bytes) -> Dict[str, Any]\n

Deserialize a compatible dictionary

"},{"location":"aea-framework-documentation/api/helpers/sym_link/","title":"Sym Link","text":""},{"location":"aea-framework-documentation/api/helpers/sym_link/#aeahelperssym_link","title":"aea.helpers.sym_link","text":"

Sym link implementation for Linux, MacOS, and Windows.

"},{"location":"aea-framework-documentation/api/helpers/sym_link/#make_symlink","title":"make_symlink","text":"
def make_symlink(link_name: str, target: str) -> None\n

Make a symbolic link, cross platform.

Arguments:

  • link_name: the link name.
  • target: the target.

"},{"location":"aea-framework-documentation/api/helpers/sym_link/#cd","title":"cd","text":"
@contextlib.contextmanager\ndef cd(path: Path) -> Generator\n

Change directory with context manager.

"},{"location":"aea-framework-documentation/api/helpers/sym_link/#create_symlink","title":"create_symlink","text":"
def create_symlink(link_path: Path, target_path: Path, root_path: Path) -> int\n

Change directory and call the cross-platform script.

The working directory must be the parent of the symbolic link name when executing 'create_symlink_crossplatform.sh'. Hence, we need to translate target_path into the relative path from the symbolic link directory to the target directory.

So: 1) from link_path, extract the number of jumps to the parent directory in order to reach the repository root directory, and chain many \"../\" paths. 2) from target_path, compute the relative path to the root 3) relative_target_path is just the concatenation of the results from step (1) and (2).

For instance, given - link_path: './directory_1//symbolic_link - target_path: './directory_2/target_path

we want to compute: - link_path: 'symbolic_link' (just the last bit) - relative_target_path: '../../directory_1/target_path'

The resulting command on UNIX systems will be:

cd directory_1 && ln -s ../../directory_1/target_path symbolic_link\n

Arguments:

  • link_path: the source path
  • target_path: the target path
  • root_path: the root path

Returns:

exit code

"},{"location":"aea-framework-documentation/api/helpers/win32/","title":"Win32","text":""},{"location":"aea-framework-documentation/api/helpers/win32/#aeahelperswin32","title":"aea.helpers.win32","text":"

Helpers for Windows.

"},{"location":"aea-framework-documentation/api/helpers/win32/#enable_ctrl_c_support","title":"enable_ctrl_c_support","text":"
def enable_ctrl_c_support() -> None\n

Enable ctrl+c support for aea.cli command to be tested on windows platform.

"},{"location":"aea-framework-documentation/api/helpers/yaml_utils/","title":"YamlUtils","text":""},{"location":"aea-framework-documentation/api/helpers/yaml_utils/#aeahelpersyaml_utils","title":"aea.helpers.yaml_utils","text":"

Helper functions related to YAML loading/dumping.

"},{"location":"aea-framework-documentation/api/helpers/yaml_utils/#_aeayamlloader-objects","title":"_AEAYamlLoader Objects","text":"
class _AEAYamlLoader(yaml.SafeLoader)\n

Custom yaml.SafeLoader for the AEA framework.

It extends the default SafeLoader in two ways: - loads YAML configurations while remembering the order of the fields; - resolves the environment variables at loading time.

This class is for internal usage only; please use the public functions of the module 'yaml_load' and 'yaml_load_all'.

"},{"location":"aea-framework-documentation/api/helpers/yaml_utils/#__init__","title":"__init__","text":"
def __init__(*args: Any, **kwargs: Any) -> None\n

Initialize the AEAYamlLoader.

It adds a YAML Loader constructor to use 'OderedDict' to load the files.

Arguments:

  • args: the positional arguments.
  • kwargs: the keyword arguments.

"},{"location":"aea-framework-documentation/api/helpers/yaml_utils/#_aeayamldumper-objects","title":"_AEAYamlDumper Objects","text":"
class _AEAYamlDumper(yaml.SafeDumper)\n

Custom yaml.SafeDumper for the AEA framework.

It extends the default SafeDumper so to dump YAML configurations while following the order of the fields.

This class is for internal usage only; please use the public functions of the module 'yaml_dump' and 'yaml_dump_all'.

"},{"location":"aea-framework-documentation/api/helpers/yaml_utils/#__init___1","title":"__init__","text":"
def __init__(*args: Any, **kwargs: Any) -> None\n

Initialize the AEAYamlDumper.

It adds a YAML Dumper representer to use 'OderedDict' to dump the files.

Arguments:

  • args: the positional arguments.
  • kwargs: the keyword arguments.

"},{"location":"aea-framework-documentation/api/helpers/yaml_utils/#yaml_load","title":"yaml_load","text":"
def yaml_load(stream: TextIO) -> Dict[str, Any]\n

Load a yaml from a file pointer in an ordered way.

Arguments:

  • stream: file pointer to the input file.

Returns:

the dictionary object with the YAML file content.

"},{"location":"aea-framework-documentation/api/helpers/yaml_utils/#yaml_load_all","title":"yaml_load_all","text":"
def yaml_load_all(stream: TextIO) -> List[Dict[str, Any]]\n

Load a multi-paged yaml from a file pointer in an ordered way.

Arguments:

  • stream: file pointer to the input file.

Returns:

the list of dictionary objects with the (multi-paged) YAML file content.

"},{"location":"aea-framework-documentation/api/helpers/yaml_utils/#yaml_dump","title":"yaml_dump","text":"
def yaml_dump(data: Dict, stream: Optional[TextIO] = None) -> None\n

Dump YAML data to a yaml file in an ordered way.

Arguments:

  • data: the data to write.
  • stream: (optional) the file to write on.

"},{"location":"aea-framework-documentation/api/helpers/yaml_utils/#yaml_dump_all","title":"yaml_dump_all","text":"
def yaml_dump_all(data: Sequence[Dict],\nstream: Optional[TextIO] = None) -> None\n

Dump YAML data to a yaml file in an ordered way.

Arguments:

  • data: the data to write.
  • stream: (optional) the file to write on.
"},{"location":"aea-framework-documentation/api/helpers/acn/agent_record/","title":"Agent Record","text":""},{"location":"aea-framework-documentation/api/helpers/acn/agent_record/#aeahelpersacnagent_record","title":"aea.helpers.acn.agent_record","text":"

This module contains types and helpers for ACN Proof-of-Representation.

"},{"location":"aea-framework-documentation/api/helpers/acn/agent_record/#agentrecord-objects","title":"AgentRecord Objects","text":"
class AgentRecord()\n

Agent Proof-of-Representation to representative.

"},{"location":"aea-framework-documentation/api/helpers/acn/agent_record/#__init__","title":"__init__","text":"
def __init__(address: str, representative_public_key: str,\nidentifier: SimpleIdOrStr, ledger_id: SimpleIdOrStr,\nnot_before: str, not_after: str, message_format: str,\nsignature: str) -> None\n

Initialize the AgentRecord

Arguments:

  • address: agent address
  • representative_public_key: representative's public key
  • identifier: certificate identifier.
  • ledger_id: ledger identifier the request is referring to.
  • not_before: specify the lower bound for certificate validity. If it is a string, it must follow the format: 'YYYY-MM-DD'. It will be interpreted as timezone UTC-0.
  • not_after: specify the lower bound for certificate validity. If it is a string, it must follow the format: 'YYYY-MM-DD'. It will be interpreted as timezone UTC-0.
  • message_format: message format used for signing
  • signature: proof-of-representation of this AgentRecord

"},{"location":"aea-framework-documentation/api/helpers/acn/agent_record/#address","title":"address","text":"
@property\ndef address() -> str\n

Get agent address

"},{"location":"aea-framework-documentation/api/helpers/acn/agent_record/#public_key","title":"public_key","text":"
@property\ndef public_key() -> str\n

Get agent public key

"},{"location":"aea-framework-documentation/api/helpers/acn/agent_record/#representative_public_key","title":"representative_public_key","text":"
@property\ndef representative_public_key() -> str\n

Get agent representative's public key

"},{"location":"aea-framework-documentation/api/helpers/acn/agent_record/#signature","title":"signature","text":"
@property\ndef signature() -> str\n

Get record signature

"},{"location":"aea-framework-documentation/api/helpers/acn/agent_record/#message","title":"message","text":"
@property\ndef message() -> bytes\n

Get the message.

"},{"location":"aea-framework-documentation/api/helpers/acn/agent_record/#identifier","title":"identifier","text":"
@property\ndef identifier() -> SimpleIdOrStr\n

Get the identifier.

"},{"location":"aea-framework-documentation/api/helpers/acn/agent_record/#ledger_id","title":"ledger_id","text":"
@property\ndef ledger_id() -> SimpleIdOrStr\n

Get ledger id.

"},{"location":"aea-framework-documentation/api/helpers/acn/agent_record/#not_before","title":"not_before","text":"
@property\ndef not_before() -> str\n

Get the not_before field.

"},{"location":"aea-framework-documentation/api/helpers/acn/agent_record/#not_after","title":"not_after","text":"
@property\ndef not_after() -> str\n

Get the not_after field.

"},{"location":"aea-framework-documentation/api/helpers/acn/agent_record/#message_format","title":"message_format","text":"
@property\ndef message_format() -> str\n

Get the message format.

"},{"location":"aea-framework-documentation/api/helpers/acn/agent_record/#__str__","title":"__str__","text":"
def __str__() -> str\n

Get string representation.

"},{"location":"aea-framework-documentation/api/helpers/acn/agent_record/#from_cert_request","title":"from_cert_request","text":"
@classmethod\ndef from_cert_request(cls,\ncert_request: CertRequest,\naddress: str,\nrepresentative_public_key: str,\ndata_dir: Optional[PathLike] = None) -> \"AgentRecord\"\n

Get agent record from cert request.

"},{"location":"aea-framework-documentation/api/helpers/acn/uri/","title":"URI","text":""},{"location":"aea-framework-documentation/api/helpers/acn/uri/#aeahelpersacnuri","title":"aea.helpers.acn.uri","text":"

This module contains types and helpers for libp2p connections Uris.

"},{"location":"aea-framework-documentation/api/helpers/acn/uri/#uri-objects","title":"Uri Objects","text":"
class Uri()\n

Holds a node address in format \"host:port\".

"},{"location":"aea-framework-documentation/api/helpers/acn/uri/#__init__","title":"__init__","text":"
def __init__(uri: Optional[str] = None,\nhost: Optional[str] = None,\nport: Optional[int] = None) -> None\n

Initialise Uri.

"},{"location":"aea-framework-documentation/api/helpers/acn/uri/#__str__","title":"__str__","text":"
def __str__() -> str\n

Get string representation.

"},{"location":"aea-framework-documentation/api/helpers/acn/uri/#__repr__","title":"__repr__","text":"
def __repr__() -> str\n

Get object representation.

"},{"location":"aea-framework-documentation/api/helpers/acn/uri/#host","title":"host","text":"
@property\ndef host() -> str\n

Get host.

"},{"location":"aea-framework-documentation/api/helpers/acn/uri/#port","title":"port","text":"
@property\ndef port() -> int\n

Get port.

"},{"location":"aea-framework-documentation/api/helpers/ipfs/base/","title":"Base","text":""},{"location":"aea-framework-documentation/api/helpers/ipfs/base/#aeahelpersipfsbase","title":"aea.helpers.ipfs.base","text":"

This module contains helper methods and classes for the 'aea' package.

"},{"location":"aea-framework-documentation/api/helpers/ipfs/base/#chunks","title":"chunks","text":"
def chunks(data: Sized, size: int) -> Generator\n

Yield successivesize chunks from data.

"},{"location":"aea-framework-documentation/api/helpers/ipfs/base/#ipfshashonly-objects","title":"IPFSHashOnly Objects","text":"
class IPFSHashOnly()\n

A helper class which allows construction of an IPFS hash without interacting with an IPFS daemon.

"},{"location":"aea-framework-documentation/api/helpers/ipfs/base/#get","title":"get","text":"
def get(file_path: str) -> str\n

Get the IPFS hash for a single file.

Arguments:

  • file_path: the file path

Returns:

the ipfs hash

"},{"location":"aea-framework-documentation/api/helpers/ipfs/utils/","title":"Utils","text":""},{"location":"aea-framework-documentation/api/helpers/ipfs/utils/#aeahelpersipfsutils","title":"aea.helpers.ipfs.utils","text":"

This module contains utility methods for ipfs helpers.

"},{"location":"aea-framework-documentation/api/helpers/multiaddr/base/","title":"Base","text":""},{"location":"aea-framework-documentation/api/helpers/multiaddr/base/#aeahelpersmultiaddrbase","title":"aea.helpers.multiaddr.base","text":"

This module contains multiaddress class.

"},{"location":"aea-framework-documentation/api/helpers/multiaddr/base/#multiaddr-objects","title":"MultiAddr Objects","text":"
class MultiAddr()\n

Protocol Labs' Multiaddress representation of a network address.

"},{"location":"aea-framework-documentation/api/helpers/multiaddr/base/#__init__","title":"__init__","text":"
def __init__(host: str,\nport: int,\npublic_key: Optional[str] = None,\nmultihash_id: Optional[str] = None) -> None\n

Initialize a multiaddress.

Arguments:

  • host: ip host of the address
  • port: port number of the address
  • public_key: hex encoded public key. Must conform to Bitcoin EC encoding standard for Secp256k1
  • multihash_id: a multihash of the public key

"},{"location":"aea-framework-documentation/api/helpers/multiaddr/base/#compute_peerid","title":"compute_peerid","text":"
@staticmethod\ndef compute_peerid(public_key: str) -> str\n

Compute the peer id from a public key.

In particular, compute the base58 representation of libp2p PeerID from Bitcoin EC encoded Secp256k1 public key.

Arguments:

  • public_key: the public key.

Returns:

the peer id.

"},{"location":"aea-framework-documentation/api/helpers/multiaddr/base/#from_string","title":"from_string","text":"
@classmethod\ndef from_string(cls, maddr: str) -> \"MultiAddr\"\n

Construct a MultiAddr object from its string format

Arguments:

  • maddr: multiaddress string

Returns:

multiaddress object

"},{"location":"aea-framework-documentation/api/helpers/multiaddr/base/#public_key","title":"public_key","text":"
@property\ndef public_key() -> str\n

Get the public key.

"},{"location":"aea-framework-documentation/api/helpers/multiaddr/base/#peer_id","title":"peer_id","text":"
@property\ndef peer_id() -> str\n

Get the peer id.

"},{"location":"aea-framework-documentation/api/helpers/multiaddr/base/#host","title":"host","text":"
@property\ndef host() -> str\n

Get the peer host.

"},{"location":"aea-framework-documentation/api/helpers/multiaddr/base/#port","title":"port","text":"
@property\ndef port() -> int\n

Get the peer port.

"},{"location":"aea-framework-documentation/api/helpers/multiaddr/base/#format","title":"format","text":"
def format() -> str\n

Canonical representation of a multiaddress.

"},{"location":"aea-framework-documentation/api/helpers/multiaddr/base/#__str__","title":"__str__","text":"
def __str__() -> str\n

Default string representation of a multiaddress.

"},{"location":"aea-framework-documentation/api/helpers/preference_representations/base/","title":"Base","text":""},{"location":"aea-framework-documentation/api/helpers/preference_representations/base/#aeahelperspreference_representationsbase","title":"aea.helpers.preference_representations.base","text":"

Preference representation helpers.

"},{"location":"aea-framework-documentation/api/helpers/preference_representations/base/#logarithmic_utility","title":"logarithmic_utility","text":"
def logarithmic_utility(utility_params_by_good_id: Dict[str, float],\nquantities_by_good_id: Dict[str, int],\nquantity_shift: int = 100) -> float\n

Compute agent's utility given her utility function params and a good bundle.

Arguments:

  • utility_params_by_good_id: utility params by good identifier
  • quantities_by_good_id: quantities by good identifier
  • quantity_shift: a non-negative factor to shift the quantities in the utility function (to ensure the natural logarithm can be used on the entire range of quantities)

Returns:

utility value

"},{"location":"aea-framework-documentation/api/helpers/preference_representations/base/#linear_utility","title":"linear_utility","text":"
def linear_utility(exchange_params_by_currency_id: Dict[str, float],\nbalance_by_currency_id: Dict[str, int]) -> float\n

Compute agent's utility given her utility function params and a good bundle.

Arguments:

  • exchange_params_by_currency_id: exchange params by currency
  • balance_by_currency_id: balance by currency

Returns:

utility value

"},{"location":"aea-framework-documentation/api/helpers/search/generic/","title":"Generic","text":""},{"location":"aea-framework-documentation/api/helpers/search/generic/#aeahelperssearchgeneric","title":"aea.helpers.search.generic","text":"

This module contains a generic data model.

"},{"location":"aea-framework-documentation/api/helpers/search/generic/#genericdatamodel-objects","title":"GenericDataModel Objects","text":"
class GenericDataModel(DataModel)\n

Generic data model.

"},{"location":"aea-framework-documentation/api/helpers/search/generic/#__init__","title":"__init__","text":"
def __init__(data_model_name: str, data_model_attributes: Dict[str,\nAny]) -> None\n

Initialise the dataModel.

"},{"location":"aea-framework-documentation/api/helpers/storage/generic_storage/","title":"GenericStorage","text":""},{"location":"aea-framework-documentation/api/helpers/storage/generic_storage/#aeahelpersstoragegeneric_storage","title":"aea.helpers.storage.generic_storage","text":"

This module contains the storage implementation.

"},{"location":"aea-framework-documentation/api/helpers/storage/generic_storage/#asynccollection-objects","title":"AsyncCollection Objects","text":"
class AsyncCollection()\n

Async collection.

"},{"location":"aea-framework-documentation/api/helpers/storage/generic_storage/#__init__","title":"__init__","text":"
def __init__(storage_backend: AbstractStorageBackend,\ncollection_name: str) -> None\n

Init collection object.

Arguments:

  • storage_backend: storage backed to use.
  • collection_name: str

"},{"location":"aea-framework-documentation/api/helpers/storage/generic_storage/#put","title":"put","text":"
async def put(object_id: str, object_body: JSON_TYPES) -> None\n

Put object into collection.

Arguments:

  • object_id: str object id
  • object_body: python dict, json compatible.

Returns:

None

"},{"location":"aea-framework-documentation/api/helpers/storage/generic_storage/#get","title":"get","text":"
async def get(object_id: str) -> Optional[JSON_TYPES]\n

Get object from the collection.

Arguments:

  • object_id: str object id

Returns:

dict if object exists in collection otherwise None

"},{"location":"aea-framework-documentation/api/helpers/storage/generic_storage/#remove","title":"remove","text":"
async def remove(object_id: str) -> None\n

Remove object from the collection.

Arguments:

  • object_id: str object id

Returns:

None

"},{"location":"aea-framework-documentation/api/helpers/storage/generic_storage/#find","title":"find","text":"
async def find(field: str, equals: EQUALS_TYPE) -> List[OBJECT_ID_AND_BODY]\n

Get objects from the collection by filtering by field value.

Arguments:

  • field: field name to search: example \"parent.field\"
  • equals: value field should be equal to

Returns:

None

"},{"location":"aea-framework-documentation/api/helpers/storage/generic_storage/#list","title":"list","text":"
async def list() -> List[OBJECT_ID_AND_BODY]\n

List all objects with keys from the collection.

Returns:

Tuple of objects keys, bodies.

"},{"location":"aea-framework-documentation/api/helpers/storage/generic_storage/#synccollection-objects","title":"SyncCollection Objects","text":"
class SyncCollection()\n

Async collection.

"},{"location":"aea-framework-documentation/api/helpers/storage/generic_storage/#__init___1","title":"__init__","text":"
def __init__(async_collection_coro: Coroutine,\nloop: asyncio.AbstractEventLoop) -> None\n

Init collection object.

Arguments:

  • async_collection_coro: coroutine returns async collection.
  • loop: abstract event loop where storage is running.

"},{"location":"aea-framework-documentation/api/helpers/storage/generic_storage/#put_1","title":"put","text":"
def put(object_id: str, object_body: JSON_TYPES) -> None\n

Put object into collection.

Arguments:

  • object_id: str object id
  • object_body: python dict, json compatible.

Returns:

None

"},{"location":"aea-framework-documentation/api/helpers/storage/generic_storage/#get_1","title":"get","text":"
def get(object_id: str) -> Optional[JSON_TYPES]\n

Get object from the collection.

Arguments:

  • object_id: str object id

Returns:

dict if object exists in collection otherwise None

"},{"location":"aea-framework-documentation/api/helpers/storage/generic_storage/#remove_1","title":"remove","text":"
def remove(object_id: str) -> None\n

Remove object from the collection.

Arguments:

  • object_id: str object id

Returns:

None

"},{"location":"aea-framework-documentation/api/helpers/storage/generic_storage/#find_1","title":"find","text":"
def find(field: str, equals: EQUALS_TYPE) -> List[OBJECT_ID_AND_BODY]\n

Get objects from the collection by filtering by field value.

Arguments:

  • field: field name to search: example \"parent.field\"
  • equals: value field should be equal to

Returns:

List of object bodies

"},{"location":"aea-framework-documentation/api/helpers/storage/generic_storage/#list_1","title":"list","text":"
def list() -> List[OBJECT_ID_AND_BODY]\n

List all objects with keys from the collection.

Returns:

Tuple of objects keys, bodies.

"},{"location":"aea-framework-documentation/api/helpers/storage/generic_storage/#storage-objects","title":"Storage Objects","text":"
class Storage(Runnable)\n

Generic storage.

"},{"location":"aea-framework-documentation/api/helpers/storage/generic_storage/#__init___2","title":"__init__","text":"
def __init__(storage_uri: str,\nloop: asyncio.AbstractEventLoop = None,\nthreaded: bool = False) -> None\n

Init storage.

Arguments:

  • storage_uri: configuration string for storage.
  • loop: asyncio event loop to use.
  • threaded: bool. start in thread if True.

"},{"location":"aea-framework-documentation/api/helpers/storage/generic_storage/#wait_connected","title":"wait_connected","text":"
async def wait_connected() -> None\n

Wait generic storage is connected.

"},{"location":"aea-framework-documentation/api/helpers/storage/generic_storage/#is_connected","title":"is_connected","text":"
@property\ndef is_connected() -> bool\n

Get running state of the storage.

"},{"location":"aea-framework-documentation/api/helpers/storage/generic_storage/#run","title":"run","text":"
async def run() -> None\n

Connect storage.

"},{"location":"aea-framework-documentation/api/helpers/storage/generic_storage/#get_collection","title":"get_collection","text":"
async def get_collection(collection_name: str) -> AsyncCollection\n

Get async collection.

"},{"location":"aea-framework-documentation/api/helpers/storage/generic_storage/#get_sync_collection","title":"get_sync_collection","text":"
def get_sync_collection(collection_name: str) -> SyncCollection\n

Get sync collection.

"},{"location":"aea-framework-documentation/api/helpers/storage/generic_storage/#__repr__","title":"__repr__","text":"
def __repr__() -> str\n

Get string representation of the storage.

"},{"location":"aea-framework-documentation/api/helpers/storage/backends/base/","title":"Base","text":""},{"location":"aea-framework-documentation/api/helpers/storage/backends/base/#aeahelpersstoragebackendsbase","title":"aea.helpers.storage.backends.base","text":"

This module contains storage abstract backend class.

"},{"location":"aea-framework-documentation/api/helpers/storage/backends/base/#abstractstoragebackend-objects","title":"AbstractStorageBackend Objects","text":"
class AbstractStorageBackend(ABC)\n

Abstract base class for storage backend.

"},{"location":"aea-framework-documentation/api/helpers/storage/backends/base/#__init__","title":"__init__","text":"
def __init__(uri: str) -> None\n

Init backend.

"},{"location":"aea-framework-documentation/api/helpers/storage/backends/base/#connect","title":"connect","text":"
@abstractmethod\nasync def connect() -> None\n

Connect to backend.

"},{"location":"aea-framework-documentation/api/helpers/storage/backends/base/#disconnect","title":"disconnect","text":"
@abstractmethod\nasync def disconnect() -> None\n

Disconnect the backend.

"},{"location":"aea-framework-documentation/api/helpers/storage/backends/base/#ensure_collection","title":"ensure_collection","text":"
@abstractmethod\nasync def ensure_collection(collection_name: str) -> None\n

Create collection if not exits.

Arguments:

  • collection_name: str.

Returns:

None

"},{"location":"aea-framework-documentation/api/helpers/storage/backends/base/#put","title":"put","text":"
@abstractmethod\nasync def put(collection_name: str, object_id: str,\nobject_body: JSON_TYPES) -> None\n

Put object into collection.

Arguments:

  • collection_name: str.
  • object_id: str object id
  • object_body: python dict, json compatible.

Returns:

None

"},{"location":"aea-framework-documentation/api/helpers/storage/backends/base/#get","title":"get","text":"
@abstractmethod\nasync def get(collection_name: str, object_id: str) -> Optional[JSON_TYPES]\n

Get object from the collection.

Arguments:

  • collection_name: str.
  • object_id: str object id

Returns:

dict if object exists in collection otherwise None

"},{"location":"aea-framework-documentation/api/helpers/storage/backends/base/#remove","title":"remove","text":"
@abstractmethod\nasync def remove(collection_name: str, object_id: str) -> None\n

Remove object from the collection.

Arguments:

  • collection_name: str.
  • object_id: str object id

Returns:

None

"},{"location":"aea-framework-documentation/api/helpers/storage/backends/base/#find","title":"find","text":"
@abstractmethod\nasync def find(collection_name: str, field: str,\nequals: EQUALS_TYPE) -> List[OBJECT_ID_AND_BODY]\n

Get objects from the collection by filtering by field value.

Arguments:

  • collection_name: str.
  • field: field name to search: example \"parent.field\"
  • equals: value field should be equal to

Returns:

list of objects bodies

"},{"location":"aea-framework-documentation/api/helpers/storage/backends/base/#list","title":"list","text":"
@abstractmethod\nasync def list(collection_name: str) -> List[OBJECT_ID_AND_BODY]\n

List all objects with keys from the collection.

Arguments:

  • collection_name: str.

Returns:

Tuple of objects keys, bodies.

"},{"location":"aea-framework-documentation/api/helpers/storage/backends/sqlite/","title":"Sqlite","text":""},{"location":"aea-framework-documentation/api/helpers/storage/backends/sqlite/#aeahelpersstoragebackendssqlite","title":"aea.helpers.storage.backends.sqlite","text":"

This module contains sqlite storage backend implementation.

"},{"location":"aea-framework-documentation/api/helpers/storage/backends/sqlite/#sqlitestoragebackend-objects","title":"SqliteStorageBackend Objects","text":"
class SqliteStorageBackend(AbstractStorageBackend)\n

Sqlite storage backend.

"},{"location":"aea-framework-documentation/api/helpers/storage/backends/sqlite/#__init__","title":"__init__","text":"
def __init__(uri: str) -> None\n

Init backend.

"},{"location":"aea-framework-documentation/api/helpers/storage/backends/sqlite/#connect","title":"connect","text":"
async def connect() -> None\n

Connect to backend.

"},{"location":"aea-framework-documentation/api/helpers/storage/backends/sqlite/#disconnect","title":"disconnect","text":"
async def disconnect() -> None\n

Disconnect the backend.

"},{"location":"aea-framework-documentation/api/helpers/storage/backends/sqlite/#ensure_collection","title":"ensure_collection","text":"
async def ensure_collection(collection_name: str) -> None\n

Create collection if not exits.

Arguments:

  • collection_name: name of the collection.

"},{"location":"aea-framework-documentation/api/helpers/storage/backends/sqlite/#put","title":"put","text":"
async def put(collection_name: str, object_id: str,\nobject_body: JSON_TYPES) -> None\n

Put object into collection.

Arguments:

  • collection_name: str.
  • object_id: str object id
  • object_body: python dict, json compatible.

"},{"location":"aea-framework-documentation/api/helpers/storage/backends/sqlite/#get","title":"get","text":"
async def get(collection_name: str, object_id: str) -> Optional[JSON_TYPES]\n

Get object from the collection.

Arguments:

  • collection_name: str.
  • object_id: str object id

Returns:

dict if object exists in collection otherwise None

"},{"location":"aea-framework-documentation/api/helpers/storage/backends/sqlite/#remove","title":"remove","text":"
async def remove(collection_name: str, object_id: str) -> None\n

Remove object from the collection.

Arguments:

  • collection_name: str.
  • object_id: str object id

"},{"location":"aea-framework-documentation/api/helpers/storage/backends/sqlite/#find","title":"find","text":"
async def find(collection_name: str, field: str,\nequals: EQUALS_TYPE) -> List[OBJECT_ID_AND_BODY]\n

Get objects from the collection by filtering by field value.

Arguments:

  • collection_name: str.
  • field: field name to search: example \"parent.field\"
  • equals: value field should be equal to

Returns:

list of object ids and body

"},{"location":"aea-framework-documentation/api/helpers/storage/backends/sqlite/#list","title":"list","text":"
async def list(collection_name: str) -> List[OBJECT_ID_AND_BODY]\n

List all objects with keys from the collection.

Arguments:

  • collection_name: str.

Returns:

Tuple of objects keys, bodies.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/","title":"Base","text":""},{"location":"aea-framework-documentation/api/helpers/transaction/base/#aeahelperstransactionbase","title":"aea.helpers.transaction.base","text":"

This module contains terms related classes.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#rawtransaction-objects","title":"RawTransaction Objects","text":"
class RawTransaction()\n

This class represents an instance of RawTransaction.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#__init__","title":"__init__","text":"
def __init__(ledger_id: str, body: JSONLike) -> None\n

Initialise an instance of RawTransaction.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#ledger_id","title":"ledger_id","text":"
@property\ndef ledger_id() -> str\n

Get the id of the ledger on which the terms are to be settled.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#body","title":"body","text":"
@property\ndef body() -> JSONLike\n

Get the body.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#encode","title":"encode","text":"
@staticmethod\ndef encode(raw_transaction_protobuf_object: Any,\nraw_transaction_object: \"RawTransaction\") -> None\n

Encode an instance of this class into the protocol buffer object.

The protocol buffer object in the raw_transaction_protobuf_object argument must be matched with the instance of this class in the 'raw_transaction_object' argument.

Arguments:

  • raw_transaction_protobuf_object: the protocol buffer object whose type corresponds with this class.
  • raw_transaction_object: an instance of this class to be encoded in the protocol buffer object.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#decode","title":"decode","text":"
@classmethod\ndef decode(cls, raw_transaction_protobuf_object: Any) -> \"RawTransaction\"\n

Decode a protocol buffer object that corresponds with this class into an instance of this class.

A new instance of this class must be created that matches the protocol buffer object in the 'raw_transaction_protobuf_object' argument.

Arguments:

  • raw_transaction_protobuf_object: the protocol buffer object whose type corresponds with this class.

Returns:

A new instance of this class that matches the protocol buffer object in the 'raw_transaction_protobuf_object' argument.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#__eq__","title":"__eq__","text":"
def __eq__(other: Any) -> bool\n

Check equality.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#__str__","title":"__str__","text":"
def __str__() -> str\n

Get string representation.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#rawmessage-objects","title":"RawMessage Objects","text":"
class RawMessage()\n

This class represents an instance of RawMessage.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#__init___1","title":"__init__","text":"
def __init__(ledger_id: str,\nbody: bytes,\nis_deprecated_mode: bool = False) -> None\n

Initialise an instance of RawMessage.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#ledger_id_1","title":"ledger_id","text":"
@property\ndef ledger_id() -> str\n

Get the id of the ledger on which the terms are to be settled.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#body_1","title":"body","text":"
@property\ndef body() -> bytes\n

Get the body.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#is_deprecated_mode","title":"is_deprecated_mode","text":"
@property\ndef is_deprecated_mode() -> bool\n

Get the is_deprecated_mode.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#encode_1","title":"encode","text":"
@staticmethod\ndef encode(raw_message_protobuf_object: Any,\nraw_message_object: \"RawMessage\") -> None\n

Encode an instance of this class into the protocol buffer object.

The protocol buffer object in the raw_message_protobuf_object argument must be matched with the instance of this class in the 'raw_message_object' argument.

Arguments:

  • raw_message_protobuf_object: the protocol buffer object whose type corresponds with this class.
  • raw_message_object: an instance of this class to be encoded in the protocol buffer object.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#decode_1","title":"decode","text":"
@classmethod\ndef decode(cls, raw_message_protobuf_object: Any) -> \"RawMessage\"\n

Decode a protocol buffer object that corresponds with this class into an instance of this class.

A new instance of this class must be created that matches the protocol buffer object in the 'raw_message_protobuf_object' argument.

Arguments:

  • raw_message_protobuf_object: the protocol buffer object whose type corresponds with this class.

Returns:

A new instance of this class that matches the protocol buffer object in the 'raw_message_protobuf_object' argument.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#__eq___1","title":"__eq__","text":"
def __eq__(other: Any) -> bool\n

Check equality.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#__str___1","title":"__str__","text":"
def __str__() -> str\n

Get string representation.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#signedtransaction-objects","title":"SignedTransaction Objects","text":"
class SignedTransaction()\n

This class represents an instance of SignedTransaction.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#__init___2","title":"__init__","text":"
def __init__(ledger_id: str, body: JSONLike) -> None\n

Initialise an instance of SignedTransaction.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#ledger_id_2","title":"ledger_id","text":"
@property\ndef ledger_id() -> str\n

Get the id of the ledger on which the terms are to be settled.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#body_2","title":"body","text":"
@property\ndef body() -> JSONLike\n

Get the body.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#encode_2","title":"encode","text":"
@staticmethod\ndef encode(signed_transaction_protobuf_object: Any,\nsigned_transaction_object: \"SignedTransaction\") -> None\n

Encode an instance of this class into the protocol buffer object.

The protocol buffer object in the signed_transaction_protobuf_object argument must be matched with the instance of this class in the 'signed_transaction_object' argument.

Arguments:

  • signed_transaction_protobuf_object: the protocol buffer object whose type corresponds with this class.
  • signed_transaction_object: an instance of this class to be encoded in the protocol buffer object.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#decode_2","title":"decode","text":"
@classmethod\ndef decode(cls,\nsigned_transaction_protobuf_object: Any) -> \"SignedTransaction\"\n

Decode a protocol buffer object that corresponds with this class into an instance of this class.

A new instance of this class must be created that matches the protocol buffer object in the 'signed_transaction_protobuf_object' argument.

Arguments:

  • signed_transaction_protobuf_object: the protocol buffer object whose type corresponds with this class.

Returns:

A new instance of this class that matches the protocol buffer object in the 'signed_transaction_protobuf_object' argument.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#__eq___2","title":"__eq__","text":"
def __eq__(other: Any) -> bool\n

Check equality.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#__str___2","title":"__str__","text":"
def __str__() -> str\n

Get string representation.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#signedmessage-objects","title":"SignedMessage Objects","text":"
class SignedMessage()\n

This class represents an instance of RawMessage.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#__init___3","title":"__init__","text":"
def __init__(ledger_id: str,\nbody: str,\nis_deprecated_mode: bool = False) -> None\n

Initialise an instance of SignedMessage.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#ledger_id_3","title":"ledger_id","text":"
@property\ndef ledger_id() -> str\n

Get the id of the ledger on which the terms are to be settled.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#body_3","title":"body","text":"
@property\ndef body() -> str\n

Get the body.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#is_deprecated_mode_1","title":"is_deprecated_mode","text":"
@property\ndef is_deprecated_mode() -> bool\n

Get the is_deprecated_mode.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#encode_3","title":"encode","text":"
@staticmethod\ndef encode(signed_message_protobuf_object: Any,\nsigned_message_object: \"SignedMessage\") -> None\n

Encode an instance of this class into the protocol buffer object.

The protocol buffer object in the signed_message_protobuf_object argument must be matched with the instance of this class in the 'signed_message_object' argument.

Arguments:

  • signed_message_protobuf_object: the protocol buffer object whose type corresponds with this class.
  • signed_message_object: an instance of this class to be encoded in the protocol buffer object.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#decode_3","title":"decode","text":"
@classmethod\ndef decode(cls, signed_message_protobuf_object: Any) -> \"SignedMessage\"\n

Decode a protocol buffer object that corresponds with this class into an instance of this class.

A new instance of this class must be created that matches the protocol buffer object in the 'signed_message_protobuf_object' argument.

Arguments:

  • signed_message_protobuf_object: the protocol buffer object whose type corresponds with this class.

Returns:

A new instance of this class that matches the protocol buffer object in the 'signed_message_protobuf_object' argument.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#__eq___3","title":"__eq__","text":"
def __eq__(other: Any) -> bool\n

Check equality.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#__str___3","title":"__str__","text":"
def __str__() -> str\n

Get string representation.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#state-objects","title":"State Objects","text":"
class State()\n

This class represents an instance of State.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#__init___4","title":"__init__","text":"
def __init__(ledger_id: str, body: JSONLike) -> None\n

Initialise an instance of State.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#ledger_id_4","title":"ledger_id","text":"
@property\ndef ledger_id() -> str\n

Get the id of the ledger on which the terms are to be settled.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#body_4","title":"body","text":"
@property\ndef body() -> JSONLike\n

Get the body.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#encode_4","title":"encode","text":"
@staticmethod\ndef encode(state_protobuf_object: Any, state_object: \"State\") -> None\n

Encode an instance of this class into the protocol buffer object.

The protocol buffer object in the state_protobuf_object argument must be matched with the instance of this class in the 'state_object' argument.

Arguments:

  • state_protobuf_object: the protocol buffer object whose type corresponds with this class.
  • state_object: an instance of this class to be encoded in the protocol buffer object.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#decode_4","title":"decode","text":"
@classmethod\ndef decode(cls, state_protobuf_object: Any) -> \"State\"\n

Decode a protocol buffer object that corresponds with this class into an instance of this class.

A new instance of this class must be created that matches the protocol buffer object in the 'state_protobuf_object' argument.

Arguments:

  • state_protobuf_object: the protocol buffer object whose type corresponds with this class.

Returns:

A new instance of this class that matches the protocol buffer object in the 'state_protobuf_object' argument.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#__eq___4","title":"__eq__","text":"
def __eq__(other: Any) -> bool\n

Check equality.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#__str___4","title":"__str__","text":"
def __str__() -> str\n

Get string representation.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#terms-objects","title":"Terms Objects","text":"
class Terms()\n

Class to represent the terms of a multi-currency & multi-token ledger transaction.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#__init___5","title":"__init__","text":"
def __init__(ledger_id: str,\nsender_address: Address,\ncounterparty_address: Address,\namount_by_currency_id: Dict[str, int],\nquantities_by_good_id: Dict[str, int],\nnonce: str,\nis_sender_payable_tx_fee: bool = True,\nfee_by_currency_id: Optional[Dict[str, int]] = None,\nis_strict: bool = False,\n**kwargs: Any) -> None\n

Instantiate terms of a transaction.

Arguments:

  • ledger_id: the ledger on which the terms are to be settled.
  • sender_address: the sender address of the transaction.
  • counterparty_address: the counterparty address of the transaction.
  • amount_by_currency_id: the amount by the currency of the transaction.
  • quantities_by_good_id: a map from good id to the quantity of that good involved in the transaction.
  • nonce: nonce to be included in transaction to discriminate otherwise identical transactions.
  • is_sender_payable_tx_fee: whether the sender or counterparty pays the tx fee.
  • fee_by_currency_id: the fee associated with the transaction.
  • is_strict: whether or not terms must have quantities and amounts of opposite signs.
  • kwargs: keyword arguments

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#id","title":"id","text":"
@property\ndef id() -> str\n

Get hash of the terms.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#sender_hash","title":"sender_hash","text":"
@property\ndef sender_hash() -> str\n

Get the sender hash.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#counterparty_hash","title":"counterparty_hash","text":"
@property\ndef counterparty_hash() -> str\n

Get the sender hash.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#ledger_id_5","title":"ledger_id","text":"
@property\ndef ledger_id() -> str\n

Get the id of the ledger on which the terms are to be settled.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#sender_address","title":"sender_address","text":"
@property\ndef sender_address() -> Address\n

Get the sender address.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#counterparty_address","title":"counterparty_address","text":"
@property\ndef counterparty_address() -> Address\n

Get the counterparty address.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#counterparty_address_1","title":"counterparty_address","text":"
@counterparty_address.setter\ndef counterparty_address(counterparty_address: Address) -> None\n

Set the counterparty address.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#amount_by_currency_id","title":"amount_by_currency_id","text":"
@property\ndef amount_by_currency_id() -> Dict[str, int]\n

Get the amount by currency id.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#is_sender_payable_tx_fee","title":"is_sender_payable_tx_fee","text":"
@property\ndef is_sender_payable_tx_fee() -> bool\n

Bool indicating whether the tx fee is paid by sender or counterparty.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#is_single_currency","title":"is_single_currency","text":"
@property\ndef is_single_currency() -> bool\n

Check whether a single currency is used for payment.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#is_empty_currency","title":"is_empty_currency","text":"
@property\ndef is_empty_currency() -> bool\n

Check whether a single currency is used for payment.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#currency_id","title":"currency_id","text":"
@property\ndef currency_id() -> str\n

Get the amount the sender must pay.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#sender_payable_amount","title":"sender_payable_amount","text":"
@property\ndef sender_payable_amount() -> int\n

Get the amount the sender must pay.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#sender_payable_amount_incl_fee","title":"sender_payable_amount_incl_fee","text":"
@property\ndef sender_payable_amount_incl_fee() -> int\n

Get the amount the sender must pay inclusive fee.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#counterparty_payable_amount","title":"counterparty_payable_amount","text":"
@property\ndef counterparty_payable_amount() -> int\n

Get the amount the counterparty must pay.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#counterparty_payable_amount_incl_fee","title":"counterparty_payable_amount_incl_fee","text":"
@property\ndef counterparty_payable_amount_incl_fee() -> int\n

Get the amount the counterparty must pay.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#quantities_by_good_id","title":"quantities_by_good_id","text":"
@property\ndef quantities_by_good_id() -> Dict[str, int]\n

Get the quantities by good id.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#good_ids","title":"good_ids","text":"
@property\ndef good_ids() -> List[str]\n

Get the (ordered) good ids.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#sender_supplied_quantities","title":"sender_supplied_quantities","text":"
@property\ndef sender_supplied_quantities() -> List[int]\n

Get the (ordered) quantities supplied by the sender.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#counterparty_supplied_quantities","title":"counterparty_supplied_quantities","text":"
@property\ndef counterparty_supplied_quantities() -> List[int]\n

Get the (ordered) quantities supplied by the counterparty.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#nonce","title":"nonce","text":"
@property\ndef nonce() -> str\n

Get the nonce.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#has_fee","title":"has_fee","text":"
@property\ndef has_fee() -> bool\n

Check if fee is set.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#fee","title":"fee","text":"
@property\ndef fee() -> int\n

Get the fee.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#sender_fee","title":"sender_fee","text":"
@property\ndef sender_fee() -> int\n

Get the sender fee.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#counterparty_fee","title":"counterparty_fee","text":"
@property\ndef counterparty_fee() -> int\n

Get the counterparty fee.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#fee_by_currency_id","title":"fee_by_currency_id","text":"
@property\ndef fee_by_currency_id() -> Dict[str, int]\n

Get fee by currency.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#kwargs","title":"kwargs","text":"
@property\ndef kwargs() -> JSONLike\n

Get the kwargs.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#is_strict","title":"is_strict","text":"
@property\ndef is_strict() -> bool\n

Get is_strict.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#get_hash","title":"get_hash","text":"
@staticmethod\ndef get_hash(ledger_id: str, sender_address: str, counterparty_address: str,\ngood_ids: List[str], sender_supplied_quantities: List[int],\ncounterparty_supplied_quantities: List[int],\nsender_payable_amount: int, counterparty_payable_amount: int,\nnonce: str) -> str\n

Generate a hash from transaction information.

Arguments:

  • ledger_id: the ledger id
  • sender_address: the sender address
  • counterparty_address: the counterparty address
  • good_ids: the list of good ids
  • sender_supplied_quantities: the quantities supplied by the sender (must all be positive)
  • counterparty_supplied_quantities: the quantities supplied by the counterparty (must all be positive)
  • sender_payable_amount: the amount payable by the sender
  • counterparty_payable_amount: the amount payable by the counterparty
  • nonce: the nonce of the transaction

Returns:

the hash

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#encode_5","title":"encode","text":"
@staticmethod\ndef encode(terms_protobuf_object: Any, terms_object: \"Terms\") -> None\n

Encode an instance of this class into the protocol buffer object.

The protocol buffer object in the terms_protobuf_object argument must be matched with the instance of this class in the 'terms_object' argument.

Arguments:

  • terms_protobuf_object: the protocol buffer object whose type corresponds with this class.
  • terms_object: an instance of this class to be encoded in the protocol buffer object.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#decode_5","title":"decode","text":"
@classmethod\ndef decode(cls, terms_protobuf_object: Any) -> \"Terms\"\n

Decode a protocol buffer object that corresponds with this class into an instance of this class.

A new instance of this class must be created that matches the protocol buffer object in the 'terms_protobuf_object' argument.

Arguments:

  • terms_protobuf_object: the protocol buffer object whose type corresponds with this class.

Returns:

A new instance of this class that matches the protocol buffer object in the 'terms_protobuf_object' argument.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#__eq___5","title":"__eq__","text":"
def __eq__(other: Any) -> bool\n

Check equality.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#__str___5","title":"__str__","text":"
def __str__() -> str\n

Get string representation.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#transactiondigest-objects","title":"TransactionDigest Objects","text":"
class TransactionDigest()\n

This class represents an instance of TransactionDigest.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#__init___6","title":"__init__","text":"
def __init__(ledger_id: str, body: str) -> None\n

Initialise an instance of TransactionDigest.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#ledger_id_6","title":"ledger_id","text":"
@property\ndef ledger_id() -> str\n

Get the id of the ledger on which the terms are to be settled.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#body_5","title":"body","text":"
@property\ndef body() -> str\n

Get the receipt.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#encode_6","title":"encode","text":"
@staticmethod\ndef encode(transaction_digest_protobuf_object: Any,\ntransaction_digest_object: \"TransactionDigest\") -> None\n

Encode an instance of this class into the protocol buffer object.

The protocol buffer object in the transaction_digest_protobuf_object argument must be matched with the instance of this class in the 'transaction_digest_object' argument.

Arguments:

  • transaction_digest_protobuf_object: the protocol buffer object whose type corresponds with this class.
  • transaction_digest_object: an instance of this class to be encoded in the protocol buffer object.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#decode_6","title":"decode","text":"
@classmethod\ndef decode(cls,\ntransaction_digest_protobuf_object: Any) -> \"TransactionDigest\"\n

Decode a protocol buffer object that corresponds with this class into an instance of this class.

A new instance of this class must be created that matches the protocol buffer object in the 'transaction_digest_protobuf_object' argument.

Arguments:

  • transaction_digest_protobuf_object: the protocol buffer object whose type corresponds with this class.

Returns:

A new instance of this class that matches the protocol buffer object in the 'transaction_digest_protobuf_object' argument.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#__eq___6","title":"__eq__","text":"
def __eq__(other: Any) -> bool\n

Check equality.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#__str___6","title":"__str__","text":"
def __str__() -> str\n

Get string representation.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#transactionreceipt-objects","title":"TransactionReceipt Objects","text":"
class TransactionReceipt()\n

This class represents an instance of TransactionReceipt.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#__init___7","title":"__init__","text":"
def __init__(ledger_id: str, receipt: JSONLike, transaction: JSONLike) -> None\n

Initialise an instance of TransactionReceipt.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#ledger_id_7","title":"ledger_id","text":"
@property\ndef ledger_id() -> str\n

Get the id of the ledger on which the terms are to be settled.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#receipt","title":"receipt","text":"
@property\ndef receipt() -> JSONLike\n

Get the receipt.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#transaction","title":"transaction","text":"
@property\ndef transaction() -> JSONLike\n

Get the transaction.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#encode_7","title":"encode","text":"
@staticmethod\ndef encode(transaction_receipt_protobuf_object: Any,\ntransaction_receipt_object: \"TransactionReceipt\") -> None\n

Encode an instance of this class into the protocol buffer object.

The protocol buffer object in the transaction_receipt_protobuf_object argument must be matched with the instance of this class in the 'transaction_receipt_object' argument.

Arguments:

  • transaction_receipt_protobuf_object: the protocol buffer object whose type corresponds with this class.
  • transaction_receipt_object: an instance of this class to be encoded in the protocol buffer object.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#decode_7","title":"decode","text":"
@classmethod\ndef decode(cls,\ntransaction_receipt_protobuf_object: Any) -> \"TransactionReceipt\"\n

Decode a protocol buffer object that corresponds with this class into an instance of this class.

A new instance of this class must be created that matches the protocol buffer object in the 'transaction_receipt_protobuf_object' argument.

Arguments:

  • transaction_receipt_protobuf_object: the protocol buffer object whose type corresponds with this class.

Returns:

A new instance of this class that matches the protocol buffer object in the 'transaction_receipt_protobuf_object' argument.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#__eq___7","title":"__eq__","text":"
def __eq__(other: Any) -> bool\n

Check equality.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#__str___7","title":"__str__","text":"
def __str__() -> str\n

Get string representation.

"},{"location":"aea-framework-documentation/api/identity/base/","title":"Identity","text":""},{"location":"aea-framework-documentation/api/identity/base/#aeaidentitybase","title":"aea.identity.base","text":"

This module contains the identity class.

"},{"location":"aea-framework-documentation/api/identity/base/#identity-objects","title":"Identity Objects","text":"
class Identity()\n

The identity holds the public elements identifying an agent.

It includes:

  • the agent name
  • the addresses, a map from address identifier to address (can be a single key-value pair)

"},{"location":"aea-framework-documentation/api/identity/base/#__init__","title":"__init__","text":"
def __init__(name: SimpleIdOrStr,\naddress: Optional[str] = None,\npublic_key: Optional[str] = None,\naddresses: Optional[Dict[str, Address]] = None,\npublic_keys: Optional[Dict[str, str]] = None,\ndefault_address_key: str = DEFAULT_LEDGER) -> None\n

Instantiate the identity.

Arguments:

  • name: the name of the agent.
  • address: the default address of the agent.
  • public_key: the public key of the agent.
  • addresses: the addresses of the agent.
  • public_keys: the public keys of the agent.
  • default_address_key: the key for the default address.

"},{"location":"aea-framework-documentation/api/identity/base/#default_address_key","title":"default_address_key","text":"
@property\ndef default_address_key() -> str\n

Get the default address key.

"},{"location":"aea-framework-documentation/api/identity/base/#name","title":"name","text":"
@property\ndef name() -> str\n

Get the agent name.

"},{"location":"aea-framework-documentation/api/identity/base/#addresses","title":"addresses","text":"
@property\ndef addresses() -> Dict[str, Address]\n

Get the addresses.

"},{"location":"aea-framework-documentation/api/identity/base/#address","title":"address","text":"
@property\ndef address() -> Address\n

Get the default address.

"},{"location":"aea-framework-documentation/api/identity/base/#public_keys","title":"public_keys","text":"
@property\ndef public_keys() -> Dict[str, str]\n

Get the public keys.

"},{"location":"aea-framework-documentation/api/identity/base/#public_key","title":"public_key","text":"
@property\ndef public_key() -> str\n

Get the default public key.

"},{"location":"aea-framework-documentation/api/mail/base/","title":"Mail","text":""},{"location":"aea-framework-documentation/api/mail/base/#aeamailbase","title":"aea.mail.base","text":"

Mail module abstract base classes.

"},{"location":"aea-framework-documentation/api/mail/base/#uri-objects","title":"URI Objects","text":"
class URI()\n

URI following RFC3986.

"},{"location":"aea-framework-documentation/api/mail/base/#__init__","title":"__init__","text":"
def __init__(uri_raw: str) -> None\n

Initialize the URI.

Must follow: https://tools.ietf.org/html/rfc3986.html

Arguments:

  • uri_raw: the raw form uri

"},{"location":"aea-framework-documentation/api/mail/base/#scheme","title":"scheme","text":"
@property\ndef scheme() -> str\n

Get the scheme.

"},{"location":"aea-framework-documentation/api/mail/base/#netloc","title":"netloc","text":"
@property\ndef netloc() -> str\n

Get the netloc.

"},{"location":"aea-framework-documentation/api/mail/base/#path","title":"path","text":"
@property\ndef path() -> str\n

Get the path.

"},{"location":"aea-framework-documentation/api/mail/base/#params","title":"params","text":"
@property\ndef params() -> str\n

Get the params.

"},{"location":"aea-framework-documentation/api/mail/base/#query","title":"query","text":"
@property\ndef query() -> str\n

Get the query.

"},{"location":"aea-framework-documentation/api/mail/base/#fragment","title":"fragment","text":"
@property\ndef fragment() -> str\n

Get the fragment.

"},{"location":"aea-framework-documentation/api/mail/base/#username","title":"username","text":"
@property\ndef username() -> Optional[str]\n

Get the username.

"},{"location":"aea-framework-documentation/api/mail/base/#password","title":"password","text":"
@property\ndef password() -> Optional[str]\n

Get the password.

"},{"location":"aea-framework-documentation/api/mail/base/#host","title":"host","text":"
@property\ndef host() -> Optional[str]\n

Get the host.

"},{"location":"aea-framework-documentation/api/mail/base/#port","title":"port","text":"
@property\ndef port() -> Optional[int]\n

Get the port.

"},{"location":"aea-framework-documentation/api/mail/base/#__str__","title":"__str__","text":"
def __str__() -> str\n

Get string representation.

"},{"location":"aea-framework-documentation/api/mail/base/#__eq__","title":"__eq__","text":"
def __eq__(other: Any) -> bool\n

Compare with another object.

"},{"location":"aea-framework-documentation/api/mail/base/#envelopecontext-objects","title":"EnvelopeContext Objects","text":"
class EnvelopeContext()\n

Contains context information of an envelope.

"},{"location":"aea-framework-documentation/api/mail/base/#__init___1","title":"__init__","text":"
def __init__(connection_id: Optional[PublicId] = None,\nuri: Optional[URI] = None) -> None\n

Initialize the envelope context.

Arguments:

  • connection_id: the connection id used for routing the outgoing envelope in the multiplexer.
  • uri: the URI sent with the envelope.

"},{"location":"aea-framework-documentation/api/mail/base/#uri","title":"uri","text":"
@property\ndef uri() -> Optional[URI]\n

Get the URI.

"},{"location":"aea-framework-documentation/api/mail/base/#connection_id","title":"connection_id","text":"
@property\ndef connection_id() -> Optional[PublicId]\n

Get the connection id to route the envelope.

"},{"location":"aea-framework-documentation/api/mail/base/#connection_id_1","title":"connection_id","text":"
@connection_id.setter\ndef connection_id(connection_id: PublicId) -> None\n

Set the 'via' connection id.

"},{"location":"aea-framework-documentation/api/mail/base/#__str___1","title":"__str__","text":"
def __str__() -> str\n

Get the string representation.

"},{"location":"aea-framework-documentation/api/mail/base/#__eq___1","title":"__eq__","text":"
def __eq__(other: Any) -> bool\n

Compare with another object.

"},{"location":"aea-framework-documentation/api/mail/base/#aeaconnectionerror-objects","title":"AEAConnectionError Objects","text":"
class AEAConnectionError(Exception)\n

Exception class for connection errors.

"},{"location":"aea-framework-documentation/api/mail/base/#empty-objects","title":"Empty Objects","text":"
class Empty(Exception)\n

Exception for when the inbox is empty.

"},{"location":"aea-framework-documentation/api/mail/base/#envelopeserializer-objects","title":"EnvelopeSerializer Objects","text":"
class EnvelopeSerializer(ABC)\n

Abstract class to specify the serialization layer for the envelope.

"},{"location":"aea-framework-documentation/api/mail/base/#encode","title":"encode","text":"
@abstractmethod\ndef encode(envelope: \"Envelope\") -> bytes\n

Encode the envelope.

Arguments:

  • envelope: the envelope to encode

Returns:

the encoded envelope

"},{"location":"aea-framework-documentation/api/mail/base/#decode","title":"decode","text":"
@abstractmethod\ndef decode(envelope_bytes: bytes) -> \"Envelope\"\n

Decode the envelope.

Arguments:

  • envelope_bytes: the encoded envelope

Returns:

the envelope

"},{"location":"aea-framework-documentation/api/mail/base/#protobufenvelopeserializer-objects","title":"ProtobufEnvelopeSerializer Objects","text":"
class ProtobufEnvelopeSerializer(EnvelopeSerializer)\n

Envelope serializer using Protobuf.

"},{"location":"aea-framework-documentation/api/mail/base/#encode_1","title":"encode","text":"
def encode(envelope: \"Envelope\") -> bytes\n

Encode the envelope.

Arguments:

  • envelope: the envelope to encode

Returns:

the encoded envelope

"},{"location":"aea-framework-documentation/api/mail/base/#decode_1","title":"decode","text":"
def decode(envelope_bytes: bytes) -> \"Envelope\"\n

Decode the envelope.

The default serializer doesn't decode the message field.

Arguments:

  • envelope_bytes: the encoded envelope

Returns:

the envelope

"},{"location":"aea-framework-documentation/api/mail/base/#envelope-objects","title":"Envelope Objects","text":"
class Envelope()\n

The top level message class for agent to agent communication.

"},{"location":"aea-framework-documentation/api/mail/base/#__init___2","title":"__init__","text":"
def __init__(to: Address,\nsender: Address,\nmessage: Union[Message, bytes],\ncontext: Optional[EnvelopeContext] = None,\nprotocol_specification_id: Optional[PublicId] = None) -> None\n

Initialize a Message object.

Arguments:

  • to: the address of the receiver.
  • sender: the address of the sender.
  • message: the protocol-specific message.
  • context: the optional envelope context.
  • protocol_specification_id: the protocol specification id (wire id).

"},{"location":"aea-framework-documentation/api/mail/base/#to","title":"to","text":"
@property\ndef to() -> Address\n

Get address of receiver.

"},{"location":"aea-framework-documentation/api/mail/base/#to_1","title":"to","text":"
@to.setter\ndef to(to: Address) -> None\n

Set address of receiver.

"},{"location":"aea-framework-documentation/api/mail/base/#sender","title":"sender","text":"
@property\ndef sender() -> Address\n

Get address of sender.

"},{"location":"aea-framework-documentation/api/mail/base/#sender_1","title":"sender","text":"
@sender.setter\ndef sender(sender: Address) -> None\n

Set address of sender.

"},{"location":"aea-framework-documentation/api/mail/base/#protocol_specification_id","title":"protocol_specification_id","text":"
@property\ndef protocol_specification_id() -> PublicId\n

Get protocol_specification_id.

"},{"location":"aea-framework-documentation/api/mail/base/#message","title":"message","text":"
@property\ndef message() -> Union[Message, bytes]\n

Get the protocol-specific message.

"},{"location":"aea-framework-documentation/api/mail/base/#message_1","title":"message","text":"
@message.setter\ndef message(message: Union[Message, bytes]) -> None\n

Set the protocol-specific message.

"},{"location":"aea-framework-documentation/api/mail/base/#message_bytes","title":"message_bytes","text":"
@property\ndef message_bytes() -> bytes\n

Get the protocol-specific message.

"},{"location":"aea-framework-documentation/api/mail/base/#context","title":"context","text":"
@property\ndef context() -> Optional[EnvelopeContext]\n

Get the envelope context.

"},{"location":"aea-framework-documentation/api/mail/base/#to_as_public_id","title":"to_as_public_id","text":"
@property\ndef to_as_public_id() -> Optional[PublicId]\n

Get to as public id.

"},{"location":"aea-framework-documentation/api/mail/base/#is_sender_public_id","title":"is_sender_public_id","text":"
@property\ndef is_sender_public_id() -> bool\n

Check if sender is a public id.

"},{"location":"aea-framework-documentation/api/mail/base/#is_to_public_id","title":"is_to_public_id","text":"
@property\ndef is_to_public_id() -> bool\n

Check if to is a public id.

"},{"location":"aea-framework-documentation/api/mail/base/#is_component_to_component_message","title":"is_component_to_component_message","text":"
@property\ndef is_component_to_component_message() -> bool\n

Whether or not the message contained is component to component.

"},{"location":"aea-framework-documentation/api/mail/base/#__eq___2","title":"__eq__","text":"
def __eq__(other: Any) -> bool\n

Compare with another object.

"},{"location":"aea-framework-documentation/api/mail/base/#encode_2","title":"encode","text":"
def encode(serializer: Optional[EnvelopeSerializer] = None) -> bytes\n

Encode the envelope.

Arguments:

  • serializer: the serializer that implements the encoding procedure.

Returns:

the encoded envelope.

"},{"location":"aea-framework-documentation/api/mail/base/#decode_2","title":"decode","text":"
@classmethod\ndef decode(cls,\nenvelope_bytes: bytes,\nserializer: Optional[EnvelopeSerializer] = None) -> \"Envelope\"\n

Decode the envelope.

Arguments:

  • envelope_bytes: the bytes to be decoded.
  • serializer: the serializer that implements the decoding procedure.

Returns:

the decoded envelope.

"},{"location":"aea-framework-documentation/api/mail/base/#__str___2","title":"__str__","text":"
def __str__() -> str\n

Get the string representation of an envelope.

"},{"location":"aea-framework-documentation/api/manager/manager/","title":"Manager","text":""},{"location":"aea-framework-documentation/api/manager/manager/#aeamanagermanager","title":"aea.manager.manager","text":"

This module contains the implementation of AEA agents manager.

"},{"location":"aea-framework-documentation/api/manager/manager/#projectnotfounderror-objects","title":"ProjectNotFoundError Objects","text":"
class ProjectNotFoundError(ValueError)\n

Project not found exception.

"},{"location":"aea-framework-documentation/api/manager/manager/#projectcheckerror-objects","title":"ProjectCheckError Objects","text":"
class ProjectCheckError(ValueError)\n

Project check error exception.

"},{"location":"aea-framework-documentation/api/manager/manager/#__init__","title":"__init__","text":"
def __init__(msg: str, source_exception: Exception)\n

Init exception.

"},{"location":"aea-framework-documentation/api/manager/manager/#projectpackageconsistencycheckerror-objects","title":"ProjectPackageConsistencyCheckError Objects","text":"
class ProjectPackageConsistencyCheckError(ValueError)\n

Check consistency of package versions against already added project.

"},{"location":"aea-framework-documentation/api/manager/manager/#__init___1","title":"__init__","text":"
def __init__(agent_project_id: PublicId,\nconflicting_packages: List[Tuple[PackageIdPrefix, str, str,\nSet[PublicId]]])\n

Initialize the exception.

Arguments:

  • agent_project_id: the agent project id whose addition has failed.
  • conflicting_packages: the conflicting packages.

"},{"location":"aea-framework-documentation/api/manager/manager/#baseagentruntask-objects","title":"BaseAgentRunTask Objects","text":"
class BaseAgentRunTask(ABC)\n

Base abstract class for agent run tasks.

"},{"location":"aea-framework-documentation/api/manager/manager/#start","title":"start","text":"
@abstractmethod\ndef start() -> None\n

Start task.

"},{"location":"aea-framework-documentation/api/manager/manager/#wait","title":"wait","text":"
@abstractmethod\ndef wait() -> asyncio.Future\n

Return future to wait task completed.

"},{"location":"aea-framework-documentation/api/manager/manager/#stop","title":"stop","text":"
@abstractmethod\ndef stop() -> None\n

Stop task.

"},{"location":"aea-framework-documentation/api/manager/manager/#is_running","title":"is_running","text":"
@property\n@abstractmethod\ndef is_running() -> bool\n

Return is task running.

"},{"location":"aea-framework-documentation/api/manager/manager/#agentrunasynctask-objects","title":"AgentRunAsyncTask Objects","text":"
class AgentRunAsyncTask(BaseAgentRunTask)\n

Async task wrapper for agent.

"},{"location":"aea-framework-documentation/api/manager/manager/#__init___2","title":"__init__","text":"
def __init__(agent: AEA, loop: asyncio.AbstractEventLoop) -> None\n

Init task with agent alias and loop.

"},{"location":"aea-framework-documentation/api/manager/manager/#create_run_loop","title":"create_run_loop","text":"
def create_run_loop() -> None\n

Create run loop.

"},{"location":"aea-framework-documentation/api/manager/manager/#start_1","title":"start","text":"
def start() -> None\n

Start task.

"},{"location":"aea-framework-documentation/api/manager/manager/#wait_1","title":"wait","text":"
def wait() -> asyncio.Future\n

Return future to wait task completed.

"},{"location":"aea-framework-documentation/api/manager/manager/#stop_1","title":"stop","text":"
def stop() -> None\n

Stop task.

"},{"location":"aea-framework-documentation/api/manager/manager/#run","title":"run","text":"
async def run() -> None\n

Run task body.

"},{"location":"aea-framework-documentation/api/manager/manager/#is_running_1","title":"is_running","text":"
@property\ndef is_running() -> bool\n

Return is task running.

"},{"location":"aea-framework-documentation/api/manager/manager/#agentrunthreadtask-objects","title":"AgentRunThreadTask Objects","text":"
class AgentRunThreadTask(AgentRunAsyncTask)\n

Threaded wrapper to run agent.

"},{"location":"aea-framework-documentation/api/manager/manager/#__init___3","title":"__init__","text":"
def __init__(agent: AEA, loop: asyncio.AbstractEventLoop) -> None\n

Init task with agent alias and loop.

"},{"location":"aea-framework-documentation/api/manager/manager/#create_run_loop_1","title":"create_run_loop","text":"
def create_run_loop() -> None\n

Create run loop.

"},{"location":"aea-framework-documentation/api/manager/manager/#start_2","title":"start","text":"
def start() -> None\n

Run task in a dedicated thread.

"},{"location":"aea-framework-documentation/api/manager/manager/#stop_2","title":"stop","text":"
def stop() -> None\n

Stop the task.

"},{"location":"aea-framework-documentation/api/manager/manager/#agentrunprocesstask-objects","title":"AgentRunProcessTask Objects","text":"
class AgentRunProcessTask(BaseAgentRunTask)\n

Subprocess wrapper to run agent.

"},{"location":"aea-framework-documentation/api/manager/manager/#__init___4","title":"__init__","text":"
def __init__(agent_alias: AgentAlias, loop: asyncio.AbstractEventLoop) -> None\n

Init task with agent alias and loop.

"},{"location":"aea-framework-documentation/api/manager/manager/#start_3","title":"start","text":"
def start() -> None\n

Run task in a dedicated process.

"},{"location":"aea-framework-documentation/api/manager/manager/#wait_2","title":"wait","text":"
def wait() -> asyncio.Future\n

Return future to wait task completed.

"},{"location":"aea-framework-documentation/api/manager/manager/#stop_3","title":"stop","text":"
def stop() -> None\n

Stop the task.

"},{"location":"aea-framework-documentation/api/manager/manager/#is_running_2","title":"is_running","text":"
@property\ndef is_running() -> bool\n

Is agent running.

"},{"location":"aea-framework-documentation/api/manager/manager/#multiagentmanager-objects","title":"MultiAgentManager Objects","text":"
class MultiAgentManager()\n

Multi agents manager.

"},{"location":"aea-framework-documentation/api/manager/manager/#__init___5","title":"__init__","text":"
def __init__(working_dir: str,\nmode: str = \"async\",\nregistry_path: str = DEFAULT_REGISTRY_NAME,\nauto_add_remove_project: bool = False,\npassword: Optional[str] = None) -> None\n

Initialize manager.

Arguments:

  • working_dir: directory to store base agents.
  • mode: str. async or threaded
  • registry_path: str. path to the local packages registry
  • auto_add_remove_project: bool. add/remove project on the first agent add/last agent remove
  • password: the password to encrypt/decrypt the private key.

"},{"location":"aea-framework-documentation/api/manager/manager/#data_dir","title":"data_dir","text":"
@property\ndef data_dir() -> str\n

Get the certs directory.

"},{"location":"aea-framework-documentation/api/manager/manager/#get_data_dir_of_agent","title":"get_data_dir_of_agent","text":"
def get_data_dir_of_agent(agent_name: str) -> str\n

Get the data directory of a specific agent.

"},{"location":"aea-framework-documentation/api/manager/manager/#is_running_3","title":"is_running","text":"
@property\ndef is_running() -> bool\n

Is manager running.

"},{"location":"aea-framework-documentation/api/manager/manager/#dict_state","title":"dict_state","text":"
@property\ndef dict_state() -> Dict[str, Any]\n

Create MultiAgentManager dist state.

"},{"location":"aea-framework-documentation/api/manager/manager/#projects","title":"projects","text":"
@property\ndef projects() -> Dict[PublicId, Project]\n

Get all projects.

"},{"location":"aea-framework-documentation/api/manager/manager/#add_error_callback","title":"add_error_callback","text":"
def add_error_callback(\nerror_callback: Callable[[str, BaseException],\nNone]) -> \"MultiAgentManager\"\n

Add error callback to call on error raised.

"},{"location":"aea-framework-documentation/api/manager/manager/#start_manager","title":"start_manager","text":"
def start_manager(local: bool = False,\nremote: bool = False) -> \"MultiAgentManager\"\n

Start manager.

If local = False and remote = False, then the packages are fetched in mixed mode (i.e. first try from local registry, and then from remote registry in case of failure).

Arguments:

  • local: whether or not to fetch from local registry.
  • remote: whether or not to fetch from remote registry.

Returns:

the MultiAgentManager instance.

"},{"location":"aea-framework-documentation/api/manager/manager/#last_start_status","title":"last_start_status","text":"
@property\ndef last_start_status() -> Tuple[bool, Dict[PublicId, List[Dict]], List[Tuple[\nPublicId, List[Dict], Exception]], ]\n

Get status of the last agents start loading state.

"},{"location":"aea-framework-documentation/api/manager/manager/#stop_manager","title":"stop_manager","text":"
def stop_manager(cleanup: bool = True,\nsave: bool = False) -> \"MultiAgentManager\"\n

Stop manager.

Stops all running agents and stop agent.

Arguments:

  • cleanup: bool is cleanup on stop.
  • save: bool is save state to file on stop.

Returns:

None

"},{"location":"aea-framework-documentation/api/manager/manager/#add_project","title":"add_project","text":"
def add_project(public_id: PublicId,\nlocal: bool = False,\nremote: bool = False,\nrestore: bool = False) -> \"MultiAgentManager\"\n

Fetch agent project and all dependencies to working_dir.

If local = False and remote = False, then the packages are fetched in mixed mode (i.e. first try from local registry, and then from remote registry in case of failure).

Arguments:

  • public_id: the public if of the agent project.
  • local: whether or not to fetch from local registry.
  • remote: whether or not to fetch from remote registry.
  • restore: bool flag for restoring already fetched agent.

Returns:

self

"},{"location":"aea-framework-documentation/api/manager/manager/#remove_project","title":"remove_project","text":"
def remove_project(public_id: PublicId,\nkeep_files: bool = False) -> \"MultiAgentManager\"\n

Remove agent project.

"},{"location":"aea-framework-documentation/api/manager/manager/#list_projects","title":"list_projects","text":"
def list_projects() -> List[PublicId]\n

List all agents projects added.

Returns:

list of public ids of projects

"},{"location":"aea-framework-documentation/api/manager/manager/#add_agent","title":"add_agent","text":"
def add_agent(public_id: PublicId,\nagent_name: Optional[str] = None,\nagent_overrides: Optional[dict] = None,\ncomponent_overrides: Optional[List[dict]] = None,\nlocal: bool = False,\nremote: bool = False,\nrestore: bool = False) -> \"MultiAgentManager\"\n

Create new agent configuration based on project with config overrides applied.

Alias is stored in memory only!

Arguments:

  • public_id: base agent project public id
  • agent_name: unique name for the agent
  • agent_overrides: overrides for agent config.
  • component_overrides: overrides for component section.
  • local: whether or not to fetch from local registry.
  • remote: whether or not to fetch from remote registry.
  • restore: bool flag for restoring already fetched agent.

Returns:

self

"},{"location":"aea-framework-documentation/api/manager/manager/#add_agent_with_config","title":"add_agent_with_config","text":"
def add_agent_with_config(\npublic_id: PublicId,\nconfig: List[dict],\nagent_name: Optional[str] = None) -> \"MultiAgentManager\"\n

Create new agent configuration based on project with config provided.

Alias is stored in memory only!

Arguments:

  • public_id: base agent project public id
  • agent_name: unique name for the agent
  • config: agent config (used for agent re-creation).

Returns:

manager

"},{"location":"aea-framework-documentation/api/manager/manager/#get_agent_overridables","title":"get_agent_overridables","text":"
def get_agent_overridables(agent_name: str) -> Tuple[Dict, List[Dict]]\n

Get agent config overridables.

Arguments:

  • agent_name: str

Returns:

Tuple of agent overridables dict and and list of component overridables dict.

"},{"location":"aea-framework-documentation/api/manager/manager/#set_agent_overrides","title":"set_agent_overrides","text":"
def set_agent_overrides(\nagent_name: str, agent_overides: Optional[Dict],\ncomponents_overrides: Optional[List[Dict]]) -> \"MultiAgentManager\"\n

Set agent overrides.

Arguments:

  • agent_name: str
  • agent_overides: optional dict of agent config overrides
  • components_overrides: optional list of dict of components overrides

Returns:

self

"},{"location":"aea-framework-documentation/api/manager/manager/#list_agents_info","title":"list_agents_info","text":"
def list_agents_info() -> List[Dict[str, Any]]\n

List agents detailed info.

Returns:

list of dicts that represents agent info: public_id, name, is_running.

"},{"location":"aea-framework-documentation/api/manager/manager/#list_agents","title":"list_agents","text":"
def list_agents(running_only: bool = False) -> List[str]\n

List all agents.

Arguments:

  • running_only: returns only running if set to True

Returns:

list of agents names

"},{"location":"aea-framework-documentation/api/manager/manager/#remove_agent","title":"remove_agent","text":"
def remove_agent(\nagent_name: str,\nskip_project_auto_remove: bool = False) -> \"MultiAgentManager\"\n

Remove agent alias definition from registry.

Arguments:

  • agent_name: agent name to remove
  • skip_project_auto_remove: disable auto project remove on last agent removed.

Returns:

None

"},{"location":"aea-framework-documentation/api/manager/manager/#start_agent","title":"start_agent","text":"
def start_agent(agent_name: str) -> \"MultiAgentManager\"\n

Start selected agent.

Arguments:

  • agent_name: agent name to start

Returns:

None

"},{"location":"aea-framework-documentation/api/manager/manager/#start_all_agents","title":"start_all_agents","text":"
def start_all_agents() -> \"MultiAgentManager\"\n

Start all not started agents.

Returns:

None

"},{"location":"aea-framework-documentation/api/manager/manager/#stop_agent","title":"stop_agent","text":"
def stop_agent(agent_name: str) -> \"MultiAgentManager\"\n

Stop running agent.

Arguments:

  • agent_name: agent name to stop

Returns:

self

"},{"location":"aea-framework-documentation/api/manager/manager/#stop_all_agents","title":"stop_all_agents","text":"
def stop_all_agents() -> \"MultiAgentManager\"\n

Stop all agents running.

Returns:

self

"},{"location":"aea-framework-documentation/api/manager/manager/#stop_agents","title":"stop_agents","text":"
def stop_agents(agent_names: List[str]) -> \"MultiAgentManager\"\n

Stop specified agents.

Arguments:

  • agent_names: names of agents

Returns:

self

"},{"location":"aea-framework-documentation/api/manager/manager/#start_agents","title":"start_agents","text":"
def start_agents(agent_names: List[str]) -> \"MultiAgentManager\"\n

Stop specified agents.

Arguments:

  • agent_names: names of agents

Returns:

self

"},{"location":"aea-framework-documentation/api/manager/manager/#get_agent_alias","title":"get_agent_alias","text":"
def get_agent_alias(agent_name: str) -> AgentAlias\n

Return details about agent alias definition.

Arguments:

  • agent_name: name of agent

Returns:

AgentAlias

"},{"location":"aea-framework-documentation/api/manager/project/","title":"Project","text":""},{"location":"aea-framework-documentation/api/manager/project/#aeamanagerproject","title":"aea.manager.project","text":"

This module contains the implementation of AEA agents project configuration.

"},{"location":"aea-framework-documentation/api/manager/project/#_base-objects","title":"_Base Objects","text":"
class _Base()\n

Base class to share some methods.

"},{"location":"aea-framework-documentation/api/manager/project/#builder","title":"builder","text":"
@property\ndef builder() -> AEABuilder\n

Get AEABuilder instance.

"},{"location":"aea-framework-documentation/api/manager/project/#install_pypi_dependencies","title":"install_pypi_dependencies","text":"
def install_pypi_dependencies() -> None\n

Install python dependencies for the project.

"},{"location":"aea-framework-documentation/api/manager/project/#project-objects","title":"Project Objects","text":"
class Project(_Base)\n

Agent project representation.

"},{"location":"aea-framework-documentation/api/manager/project/#__init__","title":"__init__","text":"
def __init__(public_id: PublicId, path: str) -> None\n

Init project with public_id and project's path.

"},{"location":"aea-framework-documentation/api/manager/project/#build","title":"build","text":"
def build() -> None\n

Call all build entry points.

"},{"location":"aea-framework-documentation/api/manager/project/#load","title":"load","text":"
@classmethod\ndef load(cls,\nworking_dir: str,\npublic_id: PublicId,\nis_local: bool = False,\nis_remote: bool = False,\nis_restore: bool = False,\ncli_verbosity: str = \"INFO\",\nregistry_path: str = DEFAULT_REGISTRY_NAME,\nskip_consistency_check: bool = False,\nskip_aea_validation: bool = False) -> \"Project\"\n

Load project with given public_id to working_dir.

If local = False and remote = False, then the packages are fetched in mixed mode (i.e. first try from local registry, and then from remote registry in case of failure).

Arguments:

  • working_dir: the working directory
  • public_id: the public id
  • is_local: whether to fetch from local
  • is_remote: whether to fetch from remote
  • is_restore: whether to restore or not
  • cli_verbosity: the logging verbosity of the CLI
  • registry_path: the path to the registry locally
  • skip_consistency_check: consistency checks flag
  • skip_aea_validation: aea validation flag

Returns:

project

"},{"location":"aea-framework-documentation/api/manager/project/#remove","title":"remove","text":"
def remove() -> None\n

Remove project, do cleanup.

"},{"location":"aea-framework-documentation/api/manager/project/#agent_config","title":"agent_config","text":"
@property\ndef agent_config() -> AgentConfig\n

Get the agent configuration.

"},{"location":"aea-framework-documentation/api/manager/project/#builder_1","title":"builder","text":"
@property\ndef builder() -> AEABuilder\n

Get builder instance.

"},{"location":"aea-framework-documentation/api/manager/project/#check","title":"check","text":"
def check() -> None\n

Check we can still construct an AEA from the project with builder.build.

"},{"location":"aea-framework-documentation/api/manager/project/#agentalias-objects","title":"AgentAlias Objects","text":"
class AgentAlias(_Base)\n

Agent alias representation.

"},{"location":"aea-framework-documentation/api/manager/project/#__init___1","title":"__init__","text":"
def __init__(project: Project,\nagent_name: str,\ndata_dir: str,\npassword: Optional[str] = None)\n

Init agent alias with project, config, name, agent, builder.

"},{"location":"aea-framework-documentation/api/manager/project/#set_agent_config_from_data","title":"set_agent_config_from_data","text":"
def set_agent_config_from_data(json_data: List[Dict]) -> None\n

Set agent config instance constructed from json data.

Arguments:

  • json_data: agent config json data

"},{"location":"aea-framework-documentation/api/manager/project/#builder_2","title":"builder","text":"
@property\ndef builder() -> AEABuilder\n

Get builder instance.

"},{"location":"aea-framework-documentation/api/manager/project/#agent_config_1","title":"agent_config","text":"
@property\ndef agent_config() -> AgentConfig\n

Get agent config.

"},{"location":"aea-framework-documentation/api/manager/project/#remove_from_project","title":"remove_from_project","text":"
def remove_from_project() -> None\n

Remove agent alias from project.

"},{"location":"aea-framework-documentation/api/manager/project/#dict","title":"dict","text":"
@property\ndef dict() -> Dict[str, Any]\n

Convert AgentAlias to dict.

"},{"location":"aea-framework-documentation/api/manager/project/#config_json","title":"config_json","text":"
@property\ndef config_json() -> List[Dict]\n

Get agent config json data.

"},{"location":"aea-framework-documentation/api/manager/project/#get_aea_instance","title":"get_aea_instance","text":"
def get_aea_instance() -> AEA\n

Build new aea instance.

"},{"location":"aea-framework-documentation/api/manager/project/#issue_certificates","title":"issue_certificates","text":"
def issue_certificates() -> None\n

Issue the certificates for this agent.

"},{"location":"aea-framework-documentation/api/manager/project/#set_overrides","title":"set_overrides","text":"
def set_overrides(agent_overrides: Optional[Dict] = None,\ncomponent_overrides: Optional[List[Dict]] = None) -> None\n

Set override for this agent alias's config.

"},{"location":"aea-framework-documentation/api/manager/project/#agent_config_manager","title":"agent_config_manager","text":"
@property\ndef agent_config_manager() -> AgentConfigManager\n

Get agent configuration manager instance for the config.

"},{"location":"aea-framework-documentation/api/manager/project/#get_overridables","title":"get_overridables","text":"
def get_overridables() -> Tuple[Dict, List[Dict]]\n

Get all overridables for this agent alias's config.

"},{"location":"aea-framework-documentation/api/manager/project/#get_addresses","title":"get_addresses","text":"
def get_addresses() -> Dict[str, str]\n

Get addresses from private keys.

Returns:

dict with crypto id str as key and address str as value

"},{"location":"aea-framework-documentation/api/manager/project/#get_connections_addresses","title":"get_connections_addresses","text":"
def get_connections_addresses() -> Dict[str, str]\n

Get connections addresses from connections private keys.

Returns:

dict with crypto id str as key and address str as value

"},{"location":"aea-framework-documentation/api/manager/utils/","title":"Utils","text":""},{"location":"aea-framework-documentation/api/manager/utils/#aeamanagerutils","title":"aea.manager.utils","text":"

Multiagent manager utils.

"},{"location":"aea-framework-documentation/api/manager/utils/#get_lib_path","title":"get_lib_path","text":"
def get_lib_path(env_dir: str) -> str\n

Get librarty path for env dir.

"},{"location":"aea-framework-documentation/api/manager/utils/#make_venv","title":"make_venv","text":"
def make_venv(env_dir: str, set_env: bool = False) -> None\n

Make venv and update variable to use it.

Arguments:

  • env_dir: str, path for new env dir
  • set_env: bool. use evn within this python process (update, sys.executable and sys.path)

"},{"location":"aea-framework-documentation/api/manager/utils/#project_install_and_build","title":"project_install_and_build","text":"
def project_install_and_build(project: Project) -> None\n

Install project dependencies and build required components.

"},{"location":"aea-framework-documentation/api/manager/utils/#get_venv_dir_for_project","title":"get_venv_dir_for_project","text":"
def get_venv_dir_for_project(project: Project) -> str\n

Get virtual env directory for project specified.

"},{"location":"aea-framework-documentation/api/manager/utils/#project_check","title":"project_check","text":"
def project_check(project: Project) -> None\n

Perform project loads well.

"},{"location":"aea-framework-documentation/api/manager/utils/#run_in_venv","title":"run_in_venv","text":"
def run_in_venv(env_dir: str, fn: Callable, timeout: float, *args: Any) -> Any\n

Run python function in a dedicated process with virtual env specified.

"},{"location":"aea-framework-documentation/api/plugins/aea_cli_ipfs/core/","title":"API","text":""},{"location":"aea-framework-documentation/api/plugins/aea_cli_ipfs/core/#pluginsaea-cli-ipfsaea_cli_ipfscore","title":"plugins.aea-cli-ipfs.aea_cli_ipfs.core","text":"

Core components for ipfs cli command.

"},{"location":"aea-framework-documentation/api/plugins/aea_cli_ipfs/core/#ipfs","title":"ipfs","text":"
@click.group()\n@click.pass_context\ndef ipfs(click_context: click.Context) -> None\n

IPFS Commands

"},{"location":"aea-framework-documentation/api/plugins/aea_cli_ipfs/core/#process_result","title":"process_result","text":"
@ipfs.result_callback()\n@click.pass_context\ndef process_result(click_context: click.Context, *_: Any) -> None\n

Tear down command group.

"},{"location":"aea-framework-documentation/api/plugins/aea_cli_ipfs/core/#add","title":"add","text":"
@ipfs.command()\n@click.argument(\n\"dir_path\",\ntype=click.Path(exists=True,\ndir_okay=True,\nfile_okay=False,\nresolve_path=True,\nreadable=True),\nrequired=False,\n)\n@click.option(\"-p\", \"--publish\", is_flag=True)\n@click.option(\"--no-pin\", is_flag=True)\n@click.pass_context\ndef add(click_context: click.Context,\ndir_path: Optional[str],\npublish: bool = False,\nno_pin: bool = False) -> None\n

Add directory to ipfs, if not directory specified the current one will be added.

"},{"location":"aea-framework-documentation/api/plugins/aea_cli_ipfs/core/#remove","title":"remove","text":"
@ipfs.command()\n@click.argument(\n\"hash_\",\nmetavar=\"hash\",\ntype=str,\nrequired=True,\n)\n@click.pass_context\ndef remove(click_context: click.Context, hash_: str) -> None\n

Remove a directory from ipfs by it's hash.

"},{"location":"aea-framework-documentation/api/plugins/aea_cli_ipfs/core/#download","title":"download","text":"
@ipfs.command()\n@click.argument(\n\"hash_\",\nmetavar=\"hash\",\ntype=str,\nrequired=True,\n)\n@click.argument(\n\"target_dir\",\ntype=click.Path(dir_okay=True, file_okay=False, resolve_path=True),\nrequired=False,\n)\n@click.pass_context\ndef download(click_context: click.Context, hash_: str,\ntarget_dir: Optional[str]) -> None\n

Download directory by it's hash, if not target directory specified will use current one.

"},{"location":"aea-framework-documentation/api/plugins/aea_cli_ipfs/ipfs_utils/","title":"Utils","text":""},{"location":"aea-framework-documentation/api/plugins/aea_cli_ipfs/ipfs_utils/#pluginsaea-cli-ipfsaea_cli_ipfsipfs_utils","title":"plugins.aea-cli-ipfs.aea_cli_ipfs.ipfs_utils","text":"

Ipfs utils for ipfs cli command.

"},{"location":"aea-framework-documentation/api/plugins/aea_cli_ipfs/ipfs_utils/#ipfsdaemon-objects","title":"IPFSDaemon Objects","text":"
class IPFSDaemon()\n

Set up the IPFS daemon.

Raises:

  • Exception: if IPFS is not installed.

"},{"location":"aea-framework-documentation/api/plugins/aea_cli_ipfs/ipfs_utils/#__init__","title":"__init__","text":"
def __init__() -> None\n

Initialise IPFS daemon.

"},{"location":"aea-framework-documentation/api/plugins/aea_cli_ipfs/ipfs_utils/#is_started","title":"is_started","text":"
def is_started() -> bool\n

Check daemon was started.

"},{"location":"aea-framework-documentation/api/plugins/aea_cli_ipfs/ipfs_utils/#start","title":"start","text":"
def start() -> None\n

Run the ipfs daemon.

"},{"location":"aea-framework-documentation/api/plugins/aea_cli_ipfs/ipfs_utils/#stop","title":"stop","text":"
def stop() -> None\n

Terminate the ipfs daemon.

"},{"location":"aea-framework-documentation/api/plugins/aea_cli_ipfs/ipfs_utils/#baseipfstoolexception-objects","title":"BaseIPFSToolException Objects","text":"
class BaseIPFSToolException(Exception)\n

Base ipfs tool exception.

"},{"location":"aea-framework-documentation/api/plugins/aea_cli_ipfs/ipfs_utils/#removeerror-objects","title":"RemoveError Objects","text":"
class RemoveError(BaseIPFSToolException)\n

Exception on remove.

"},{"location":"aea-framework-documentation/api/plugins/aea_cli_ipfs/ipfs_utils/#publisherror-objects","title":"PublishError Objects","text":"
class PublishError(BaseIPFSToolException)\n

Exception on publish.

"},{"location":"aea-framework-documentation/api/plugins/aea_cli_ipfs/ipfs_utils/#nodeerror-objects","title":"NodeError Objects","text":"
class NodeError(BaseIPFSToolException)\n

Exception for node connection check.

"},{"location":"aea-framework-documentation/api/plugins/aea_cli_ipfs/ipfs_utils/#downloaderror-objects","title":"DownloadError Objects","text":"
class DownloadError(BaseIPFSToolException)\n

Exception on download failed.

"},{"location":"aea-framework-documentation/api/plugins/aea_cli_ipfs/ipfs_utils/#ipfstool-objects","title":"IPFSTool Objects","text":"
class IPFSTool()\n

IPFS tool to add, publish, remove, download directories.

"},{"location":"aea-framework-documentation/api/plugins/aea_cli_ipfs/ipfs_utils/#__init___1","title":"__init__","text":"
def __init__(client_options: Optional[Dict] = None)\n

Init tool.

Arguments:

  • client_options: dict, options for ipfshttpclient instance.

"},{"location":"aea-framework-documentation/api/plugins/aea_cli_ipfs/ipfs_utils/#add","title":"add","text":"
def add(dir_path: str, pin: bool = True) -> Tuple[str, str, List]\n

Add directory to ipfs.

It wraps into directory.

Arguments:

  • dir_path: str, path to dir to publish
  • pin: bool, pin object or not

Returns:

dir name published, hash, list of items processed

"},{"location":"aea-framework-documentation/api/plugins/aea_cli_ipfs/ipfs_utils/#remove","title":"remove","text":"
def remove(hash_id: str) -> Dict\n

Remove dir added by it's hash.

Arguments:

  • hash_id: str. hash of dir to remove

Returns:

dict with unlinked items.

"},{"location":"aea-framework-documentation/api/plugins/aea_cli_ipfs/ipfs_utils/#download","title":"download","text":"
def download(hash_id: str, target_dir: str, fix_path: bool = True) -> None\n

Download dir by it's hash.

Arguments:

  • hash_id: str. hash of file to download
  • target_dir: str. directory to place downloaded
  • fix_path: bool. default True. on download don't wrap result in to hash_id directory.

"},{"location":"aea-framework-documentation/api/plugins/aea_cli_ipfs/ipfs_utils/#publish","title":"publish","text":"
def publish(hash_id: str) -> Dict\n

Publish directory by it's hash id.

Arguments:

  • hash_id: hash of the directory to publish.

Returns:

dict of names it was publish for.

"},{"location":"aea-framework-documentation/api/plugins/aea_cli_ipfs/ipfs_utils/#chec_ipfs_node_running","title":"chec_ipfs_node_running","text":"
def chec_ipfs_node_running() -> None\n

Check ipfs node running.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/","title":"API","text":""},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#pluginsaea-ledger-cosmosaea_ledger_cosmoscosmos","title":"plugins.aea-ledger-cosmos.aea_ledger_cosmos.cosmos","text":"

Cosmos module wrapping the public and private key cryptography and ledger api.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#dataencrypt-objects","title":"DataEncrypt Objects","text":"
class DataEncrypt()\n

Class to encrypt/decrypt data strings with password provided.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#encrypt","title":"encrypt","text":"
@classmethod\ndef encrypt(cls, data: bytes, password: str) -> bytes\n

Encrypt data with password.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#bytes_encode","title":"bytes_encode","text":"
@staticmethod\ndef bytes_encode(data: bytes) -> str\n

Encode bytes to ascii friendly string.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#bytes_decode","title":"bytes_decode","text":"
@staticmethod\ndef bytes_decode(data: str) -> bytes\n

Decode ascii friendly string to bytes.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#decrypt","title":"decrypt","text":"
@classmethod\ndef decrypt(cls, encrypted_data: bytes, password: str) -> bytes\n

Decrypt data with password provided.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#cosmoshelper-objects","title":"CosmosHelper Objects","text":"
class CosmosHelper(Helper)\n

Helper class usable as Mixin for CosmosApi or as standalone class.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#is_transaction_settled","title":"is_transaction_settled","text":"
@staticmethod\ndef is_transaction_settled(tx_receipt: JSONLike) -> bool\n

Check whether a transaction is settled or not.

Arguments:

  • tx_receipt: the receipt of the transaction.

Returns:

True if the transaction has been settled, False o/w.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#get_code_id","title":"get_code_id","text":"
@classmethod\ndef get_code_id(cls, tx_receipt: JSONLike) -> Optional[int]\n

Retrieve the code_id from a transaction receipt.

Arguments:

  • tx_receipt: the receipt of the transaction.

Returns:

the code id, if present

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#get_event_attributes","title":"get_event_attributes","text":"
@staticmethod\ndef get_event_attributes(tx_receipt: JSONLike) -> Dict\n

Retrieve events attributes from tx receipt.

Arguments:

  • tx_receipt: the receipt of the transaction.

Returns:

dict

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#get_contract_address","title":"get_contract_address","text":"
@classmethod\ndef get_contract_address(cls, tx_receipt: JSONLike) -> Optional[str]\n

Retrieve the contract_address from a transaction receipt.

Arguments:

  • tx_receipt: the receipt of the transaction.

Returns:

the contract address, if present

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#is_transaction_valid","title":"is_transaction_valid","text":"
@staticmethod\ndef is_transaction_valid(tx: JSONLike, seller: Address, client: Address,\ntx_nonce: str, amount: int) -> bool\n

Check whether a transaction is valid or not.

Arguments:

  • tx: the transaction.
  • seller: the address of the seller.
  • client: the address of the client.
  • tx_nonce: the transaction nonce.
  • amount: the amount we expect to get from the transaction.

Returns:

True if the random_message is equals to tx['input']

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#generate_tx_nonce","title":"generate_tx_nonce","text":"
@staticmethod\ndef generate_tx_nonce(seller: Address, client: Address) -> str\n

Generate a unique hash to distinguish transactions with the same terms.

Arguments:

  • seller: the address of the seller.
  • client: the address of the client.

Returns:

return the hash in hex.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#get_address_from_public_key","title":"get_address_from_public_key","text":"
@classmethod\ndef get_address_from_public_key(cls, public_key: str) -> str\n

Get the address from the public key.

Arguments:

  • public_key: the public key

Returns:

str

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#recover_message","title":"recover_message","text":"
@classmethod\ndef recover_message(cls,\nmessage: bytes,\nsignature: str,\nis_deprecated_mode: bool = False) -> Tuple[Address, ...]\n

Recover the addresses from the hash.

Arguments:

  • message: the message we expect
  • signature: the transaction signature
  • is_deprecated_mode: if the deprecated signing was used

Returns:

the recovered addresses

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#recover_public_keys_from_message","title":"recover_public_keys_from_message","text":"
@classmethod\ndef recover_public_keys_from_message(\ncls,\nmessage: bytes,\nsignature: str,\nis_deprecated_mode: bool = False) -> Tuple[str, ...]\n

Get the public key used to produce the signature of the message

Arguments:

  • message: raw bytes used to produce signature
  • signature: signature of the message
  • is_deprecated_mode: if the deprecated signing was used

Returns:

the recovered public keys

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#get_hash","title":"get_hash","text":"
@staticmethod\ndef get_hash(message: bytes) -> str\n

Get the hash of a message.

Arguments:

  • message: the message to be hashed.

Returns:

the hash of the message.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#is_valid_address","title":"is_valid_address","text":"
@classmethod\ndef is_valid_address(cls, address: Address) -> bool\n

Check if the address is valid.

Arguments:

  • address: the address to validate

Returns:

whether address is valid or not

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#load_contract_interface","title":"load_contract_interface","text":"
@classmethod\ndef load_contract_interface(cls, file_path: Path) -> Dict[str, str]\n

Load contract interface.

Arguments:

  • file_path: the file path to the interface

Returns:

the interface

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#cosmoscrypto-objects","title":"CosmosCrypto Objects","text":"
class CosmosCrypto(Crypto[SigningKey])\n

Class wrapping the Account Generation from Ethereum ledger.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#__init__","title":"__init__","text":"
def __init__(private_key_path: Optional[str] = None,\npassword: Optional[str] = None) -> None\n

Instantiate an ethereum crypto object.

Arguments:

  • private_key_path: the private key path of the agent
  • password: the password to encrypt/decrypt the private key.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#private_key","title":"private_key","text":"
@property\ndef private_key() -> str\n

Return a private key.

Returns:

a private key string

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#public_key","title":"public_key","text":"
@property\ndef public_key() -> str\n

Return a public key in hex format.

Returns:

a public key string in hex format

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#address","title":"address","text":"
@property\ndef address() -> str\n

Return the address for the key pair.

Returns:

a display_address str

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#load_private_key_from_path","title":"load_private_key_from_path","text":"
@classmethod\ndef load_private_key_from_path(cls,\nfile_name: str,\npassword: Optional[str] = None) -> SigningKey\n

Load a private key in hex format from a file.

Arguments:

  • file_name: the path to the hex file.
  • password: the password to encrypt/decrypt the private key.

Returns:

the Entity.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#sign_message","title":"sign_message","text":"
def sign_message(message: bytes, is_deprecated_mode: bool = False) -> str\n

Sign a message in bytes string form.

Arguments:

  • message: the message to be signed
  • is_deprecated_mode: if the deprecated signing is used

Returns:

signature of the message in string form

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#sign_transaction","title":"sign_transaction","text":"
def sign_transaction(transaction: JSONLike) -> JSONLike\n

Sign a transaction in bytes string form.

Arguments:

  • transaction: the transaction to be signed

Returns:

signed transaction

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#generate_private_key","title":"generate_private_key","text":"
@classmethod\ndef generate_private_key(cls) -> SigningKey\n

Generate a key pair for cosmos network.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#encrypt_1","title":"encrypt","text":"
def encrypt(password: str) -> str\n

Encrypt the private key and return in json.

Arguments:

  • password: the password to decrypt.

Returns:

json string containing encrypted private key.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#decrypt_1","title":"decrypt","text":"
@classmethod\ndef decrypt(cls, keyfile_json: str, password: str) -> str\n

Decrypt the private key and return in raw form.

Arguments:

  • keyfile_json: json string containing encrypted private key.
  • password: the password to decrypt.

Returns:

the raw private key.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#_cosmosapi-objects","title":"_CosmosApi Objects","text":"
class _CosmosApi(LedgerApi)\n

Class to interact with the Cosmos SDK via a HTTP APIs.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#__init___1","title":"__init__","text":"
def __init__(**kwargs: Any) -> None\n

Initialize the Cosmos ledger APIs.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#api","title":"api","text":"
@property\ndef api() -> Any\n

Get the underlying API object.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#get_balance","title":"get_balance","text":"
def get_balance(address: Address) -> Optional[int]\n

Get the balance of a given account.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#get_state","title":"get_state","text":"
def get_state(callable_name: str, *args: Any,\n**kwargs: Any) -> Optional[JSONLike]\n

Call a specified function on the ledger API.

Based on the cosmos REST API specification, which takes a path (strings separated by '/'). The convention here is to define the root of the path (txs, blocks, etc.) as the callable_name and the rest of the path as args.

Arguments:

  • callable_name: name of the callable
  • args: positional arguments
  • kwargs: keyword arguments

Returns:

the transaction dictionary

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#get_deploy_transaction","title":"get_deploy_transaction","text":"
def get_deploy_transaction(contract_interface: Dict[str, str],\ndeployer_address: Address,\n**kwargs: Any) -> Optional[JSONLike]\n

Get the transaction to deploy the smart contract.

Dispatches to _get_storage_transaction and _get_init_transaction based on kwargs.

Arguments:

  • contract_interface: the contract interface.
  • deployer_address: The address that will deploy the contract.
  • kwargs: keyword arguments.

Returns:

the transaction dictionary.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#get_handle_transaction","title":"get_handle_transaction","text":"
def get_handle_transaction(\nsender_address: Address,\ncontract_address: Address,\nhandle_msg: Any,\namount: int,\ntx_fee: int,\ndenom: Optional[str] = None,\ngas: int = DEFAULT_GAS_AMOUNT,\nmemo: str = \"\",\nchain_id: Optional[str] = None,\naccount_number: Optional[int] = None,\nsequence: Optional[int] = None,\ntx_fee_denom: Optional[str] = None) -> Optional[JSONLike]\n

Create a CosmWasm HandleMsg transaction.

Arguments:

  • sender_address: the sender address of the message initiator.
  • contract_address: the address of the smart contract.
  • handle_msg: HandleMsg in JSON format.
  • amount: Funds amount sent with transaction.
  • tx_fee: the tx fee accepted.
  • denom: the name of the denomination of the contract funds
  • gas: Maximum amount of gas to be used on executing command.
  • memo: any string comment.
  • chain_id: the Chain ID of the CosmWasm transaction. Default is 1 (i.e. mainnet).
  • account_number: Account number
  • sequence: Sequence
  • tx_fee_denom: Denomination of tx_fee, identical with denom param when None

Returns:

the unsigned CosmWasm HandleMsg

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#execute_contract_query","title":"execute_contract_query","text":"
def execute_contract_query(contract_address: Address,\nquery_msg: JSONLike) -> Optional[JSONLike]\n

Execute a CosmWasm QueryMsg. QueryMsg doesn't require signing.

Arguments:

  • contract_address: the address of the smart contract.
  • query_msg: QueryMsg in JSON format.

Returns:

the message receipt

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#get_transfer_transaction","title":"get_transfer_transaction","text":"
def get_transfer_transaction(sender_address: Address,\ndestination_address: Address,\namount: int,\ntx_fee: int,\ntx_nonce: str,\ndenom: Optional[str] = None,\ngas: int = DEFAULT_GAS_AMOUNT,\nmemo: str = \"\",\nchain_id: Optional[str] = None,\naccount_number: Optional[int] = None,\nsequence: Optional[int] = None,\ntx_fee_denom: Optional[str] = None,\n**kwargs: Any) -> Optional[JSONLike]\n

Submit a transfer transaction to the ledger.

Arguments:

  • sender_address: the sender address of the payer.
  • destination_address: the destination address of the payee.
  • amount: the amount of wealth to be transferred.
  • tx_fee: the transaction fee.
  • tx_nonce: verifies the authenticity of the tx
  • denom: the denomination of tx fee and amount
  • gas: the gas used.
  • memo: memo to include in tx.
  • chain_id: the chain ID of the transaction.
  • account_number: Account number
  • sequence: Sequence
  • tx_fee_denom: Denomination of tx_fee, identical with denom param when None
  • kwargs: keyword arguments.

Returns:

the transfer transaction

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#get_packed_exec_msg","title":"get_packed_exec_msg","text":"
def get_packed_exec_msg(sender_address: Address,\ncontract_address: str,\nmsg: JSONLike,\nfunds: int = 0,\ndenom: Optional[str] = None) -> ProtoAny\n

Create and pack MsgExecuteContract

Arguments:

  • sender_address: Address of sender
  • contract_address: Address of contract
  • msg: Paramaters to be passed to smart contract
  • funds: Funds to be sent to smart contract
  • denom: the denomination of funds

Returns:

Packed MsgExecuteContract

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#get_packed_send_msg","title":"get_packed_send_msg","text":"
def get_packed_send_msg(from_address: Address,\nto_address: Address,\namount: int,\ndenom: Optional[str] = None) -> ProtoAny\n

Generate and pack MsgSend

Arguments:

  • from_address: Address of sender
  • to_address: Address of recipient
  • amount: amount of coins to be sent
  • denom: the denomination of and amount

Returns:

packer ProtoAny type message

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#get_multi_transaction","title":"get_multi_transaction","text":"
def get_multi_transaction(from_addresses: List[str],\npub_keys: Optional[List[bytes]],\nmsgs: List[ProtoAny],\ngas: int,\ntx_fee: int = 0,\nmemo: str = \"\",\nchain_id: Optional[str] = None,\ndenom: Optional[str] = None,\ntx_fee_denom: Optional[str] = None) -> JSONLike\n

Generate transaction with multiple messages

Arguments:

  • from_addresses: Addresses of signers
  • pub_keys: Public keys of signers
  • msgs: Messages to be included in transaction
  • gas: the gas used.
  • tx_fee: the transaction fee.
  • memo: memo to include in tx.
  • chain_id: the chain ID of the transaction.
  • denom: the denomination of tx fee
  • tx_fee_denom: Denomination of tx_fee, identical with denom param when None

Raises:

  • None: RuntimeError if number of pubkeys is not equal to number of from_addresses

Returns:

the transaction

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#send_signed_transaction","title":"send_signed_transaction","text":"
def send_signed_transaction(tx_signed: JSONLike) -> Optional[str]\n

Send a signed transaction and wait for confirmation.

Arguments:

  • tx_signed: the signed transaction

Returns:

tx_digest, if present

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#get_transaction_receipt","title":"get_transaction_receipt","text":"
def get_transaction_receipt(tx_digest: str) -> Optional[JSONLike]\n

Get the transaction receipt for a transaction digest.

Arguments:

  • tx_digest: the digest associated to the transaction.

Returns:

the tx receipt, if present

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#get_transaction","title":"get_transaction","text":"
def get_transaction(tx_digest: str) -> Optional[JSONLike]\n

Get the transaction for a transaction digest.

Arguments:

  • tx_digest: the digest associated to the transaction.

Returns:

the tx, if present

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#get_contract_instance","title":"get_contract_instance","text":"
def get_contract_instance(contract_interface: Dict[str, str],\ncontract_address: Optional[str] = None) -> Any\n

Get the instance of a contract.

Arguments:

  • contract_interface: the contract interface.
  • contract_address: the contract address.

Returns:

the contract instance

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#update_with_gas_estimate","title":"update_with_gas_estimate","text":"
def update_with_gas_estimate(transaction: JSONLike) -> JSONLike\n

Attempts to update the transaction with a gas estimate

Arguments:

  • transaction: the transaction

Raises:

  • None: NotImplementedError

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#cosmosapi-objects","title":"CosmosApi Objects","text":"
class CosmosApi(_CosmosApi, CosmosHelper)\n

Class to interact with the Cosmos SDK via a HTTP APIs.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#cosmosfaucetapi-objects","title":"CosmosFaucetApi Objects","text":"
class CosmosFaucetApi(FaucetApi)\n

Cosmos testnet faucet API.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#__init___2","title":"__init__","text":"
def __init__(poll_interval: Optional[float] = None,\nfinal_wait_interval: Optional[float] = None)\n

Initialize CosmosFaucetApi.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#get_wealth","title":"get_wealth","text":"
def get_wealth(address: Address, url: Optional[str] = None) -> None\n

Get wealth from the faucet for the provided address.

Arguments:

  • address: the address.
  • url: the url

Raises:

  • None: RuntimeError of explicit faucet failures
"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/","title":"API","text":""},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#pluginsaea-ledger-ethereumaea_ledger_ethereumethereum","title":"plugins.aea-ledger-ethereum.aea_ledger_ethereum.ethereum","text":"

Ethereum module wrapping the public and private key cryptography and ledger api.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#get_gas_price_strategy","title":"get_gas_price_strategy","text":"
def get_gas_price_strategy(\ngas_price_strategy: Optional[str] = None,\napi_key: Optional[str] = None) -> Callable[[Web3, TxParams], Wei]\n

Get the gas price strategy.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#signedtransactiontranslator-objects","title":"SignedTransactionTranslator Objects","text":"
class SignedTransactionTranslator()\n

Translator for SignedTransaction.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#to_dict","title":"to_dict","text":"
@staticmethod\ndef to_dict(\nsigned_transaction: SignedTransaction) -> Dict[str, Union[str, int]]\n

Write SignedTransaction to dict.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#from_dict","title":"from_dict","text":"
@staticmethod\ndef from_dict(signed_transaction_dict: JSONLike) -> SignedTransaction\n

Get SignedTransaction from dict.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#attributedicttranslator-objects","title":"AttributeDictTranslator Objects","text":"
class AttributeDictTranslator()\n

Translator for AttributeDict.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#to_dict_1","title":"to_dict","text":"
@classmethod\ndef to_dict(cls, attr_dict: Union[AttributeDict, TxReceipt,\nTxData]) -> JSONLike\n

Simplify to dict.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#from_dict_1","title":"from_dict","text":"
@classmethod\ndef from_dict(cls, di: JSONLike) -> AttributeDict\n

Get back attribute dict.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#ethereumcrypto-objects","title":"EthereumCrypto Objects","text":"
class EthereumCrypto(Crypto[Account])\n

Class wrapping the Account Generation from Ethereum ledger.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#__init__","title":"__init__","text":"
def __init__(private_key_path: Optional[str] = None,\npassword: Optional[str] = None) -> None\n

Instantiate an ethereum crypto object.

Arguments:

  • private_key_path: the private key path of the agent
  • password: the password to encrypt/decrypt the private key.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#private_key","title":"private_key","text":"
@property\ndef private_key() -> str\n

Return a private key.

Returns:

a private key string

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#public_key","title":"public_key","text":"
@property\ndef public_key() -> str\n

Return a public key in hex format.

Returns:

a public key string in hex format

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#address","title":"address","text":"
@property\ndef address() -> str\n

Return the address for the key pair.

Returns:

a display_address str

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#load_private_key_from_path","title":"load_private_key_from_path","text":"
@classmethod\ndef load_private_key_from_path(cls,\nfile_name: str,\npassword: Optional[str] = None) -> Account\n

Load a private key in hex format from a file.

Arguments:

  • file_name: the path to the hex file.
  • password: the password to encrypt/decrypt the private key.

Returns:

the Entity.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#sign_message","title":"sign_message","text":"
def sign_message(message: bytes, is_deprecated_mode: bool = False) -> str\n

Sign a message in bytes string form.

Arguments:

  • message: the message to be signed
  • is_deprecated_mode: if the deprecated signing is used

Returns:

signature of the message in string form

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#sign_transaction","title":"sign_transaction","text":"
def sign_transaction(transaction: JSONLike) -> JSONLike\n

Sign a transaction in bytes string form.

Arguments:

  • transaction: the transaction to be signed

Returns:

signed transaction

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#generate_private_key","title":"generate_private_key","text":"
@classmethod\ndef generate_private_key(cls) -> Account\n

Generate a key pair for ethereum network.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#encrypt","title":"encrypt","text":"
def encrypt(password: str) -> str\n

Encrypt the private key and return in json.

Arguments:

  • password: the password to decrypt.

Returns:

json string containing encrypted private key.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#decrypt","title":"decrypt","text":"
@classmethod\ndef decrypt(cls, keyfile_json: str, password: str) -> str\n

Decrypt the private key and return in raw form.

Arguments:

  • keyfile_json: json str containing encrypted private key.
  • password: the password to decrypt.

Returns:

the raw private key.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#ethereumhelper-objects","title":"EthereumHelper Objects","text":"
class EthereumHelper(Helper)\n

Helper class usable as Mixin for EthereumApi or as standalone class.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#is_transaction_settled","title":"is_transaction_settled","text":"
@staticmethod\ndef is_transaction_settled(tx_receipt: JSONLike) -> bool\n

Check whether a transaction is settled or not.

Arguments:

  • tx_receipt: the receipt associated to the transaction.

Returns:

True if the transaction has been settled, False o/w.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#get_contract_address","title":"get_contract_address","text":"
@staticmethod\ndef get_contract_address(tx_receipt: JSONLike) -> Optional[str]\n

Retrieve the contract_address from a transaction receipt.

Arguments:

  • tx_receipt: the receipt of the transaction.

Returns:

the contract address, if present

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#is_transaction_valid","title":"is_transaction_valid","text":"
@staticmethod\ndef is_transaction_valid(tx: dict, seller: Address, client: Address,\ntx_nonce: str, amount: int) -> bool\n

Check whether a transaction is valid or not.

Arguments:

  • tx: the transaction.
  • seller: the address of the seller.
  • client: the address of the client.
  • tx_nonce: the transaction nonce.
  • amount: the amount we expect to get from the transaction.

Returns:

True if the random_message is equals to tx['input']

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#generate_tx_nonce","title":"generate_tx_nonce","text":"
@staticmethod\ndef generate_tx_nonce(seller: Address, client: Address) -> str\n

Generate a unique hash to distinguish transactions with the same terms.

Arguments:

  • seller: the address of the seller.
  • client: the address of the client.

Returns:

return the hash in hex.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#get_address_from_public_key","title":"get_address_from_public_key","text":"
@classmethod\ndef get_address_from_public_key(cls, public_key: str) -> str\n

Get the address from the public key.

Arguments:

  • public_key: the public key

Returns:

str

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#recover_message","title":"recover_message","text":"
@classmethod\ndef recover_message(cls,\nmessage: bytes,\nsignature: str,\nis_deprecated_mode: bool = False) -> Tuple[Address, ...]\n

Recover the addresses from the hash.

Arguments:

  • message: the message we expect
  • signature: the transaction signature
  • is_deprecated_mode: if the deprecated signing was used

Returns:

the recovered addresses

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#recover_public_keys_from_message","title":"recover_public_keys_from_message","text":"
@classmethod\ndef recover_public_keys_from_message(\ncls,\nmessage: bytes,\nsignature: str,\nis_deprecated_mode: bool = False) -> Tuple[str, ...]\n

Get the public key used to produce the signature of the message

Arguments:

  • message: raw bytes used to produce signature
  • signature: signature of the message
  • is_deprecated_mode: if the deprecated signing was used

Returns:

the recovered public keys

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#get_hash","title":"get_hash","text":"
@staticmethod\ndef get_hash(message: bytes) -> str\n

Get the hash of a message.

Arguments:

  • message: the message to be hashed.

Returns:

the hash of the message.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#load_contract_interface","title":"load_contract_interface","text":"
@classmethod\ndef load_contract_interface(cls, file_path: Path) -> Dict[str, str]\n

Load contract interface.

Arguments:

  • file_path: the file path to the interface

Returns:

the interface

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#ethereumapi-objects","title":"EthereumApi Objects","text":"
class EthereumApi(LedgerApi, EthereumHelper)\n

Class to interact with the Ethereum Web3 APIs.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#__init___1","title":"__init__","text":"
def __init__(**kwargs: Any)\n

Initialize the Ethereum ledger APIs.

Arguments:

  • kwargs: keyword arguments

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#api","title":"api","text":"
@property\ndef api() -> Web3\n

Get the underlying API object.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#get_balance","title":"get_balance","text":"
def get_balance(address: Address) -> Optional[int]\n

Get the balance of a given account.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#get_state","title":"get_state","text":"
def get_state(callable_name: str, *args: Any,\n**kwargs: Any) -> Optional[JSONLike]\n

Call a specified function on the ledger API.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#get_transfer_transaction","title":"get_transfer_transaction","text":"
def get_transfer_transaction(sender_address: Address,\ndestination_address: Address,\namount: int,\ntx_fee: int,\ntx_nonce: str,\nchain_id: Optional[int] = None,\ngas_price: Optional[str] = None,\ngas_price_strategy: Optional[str] = None,\n**kwargs: Any) -> Optional[JSONLike]\n

Submit a transfer transaction to the ledger.

Arguments:

  • sender_address: the sender address of the payer.
  • destination_address: the destination address of the payee.
  • amount: the amount of wealth to be transferred (in Wei).
  • tx_fee: the transaction fee (gas) to be used (in Wei).
  • tx_nonce: verifies the authenticity of the tx.
  • chain_id: the Chain ID of the Ethereum transaction.
  • gas_price: the gas price (in Wei)
  • gas_price_strategy: the gas price strategy to be used.
  • kwargs: keyword arguments

Returns:

the transfer transaction

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#update_with_gas_estimate","title":"update_with_gas_estimate","text":"
def update_with_gas_estimate(transaction: JSONLike) -> JSONLike\n

Attempts to update the transaction with a gas estimate

Arguments:

  • transaction: the transaction

Returns:

the updated transaction

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#send_signed_transaction","title":"send_signed_transaction","text":"
def send_signed_transaction(tx_signed: JSONLike) -> Optional[str]\n

Send a signed transaction and wait for confirmation.

Arguments:

  • tx_signed: the signed transaction

Returns:

tx_digest, if present

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#get_transaction_receipt","title":"get_transaction_receipt","text":"
def get_transaction_receipt(tx_digest: str) -> Optional[JSONLike]\n

Get the transaction receipt for a transaction digest.

Arguments:

  • tx_digest: the digest associated to the transaction.

Returns:

the tx receipt, if present

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#get_transaction","title":"get_transaction","text":"
def get_transaction(tx_digest: str) -> Optional[JSONLike]\n

Get the transaction for a transaction digest.

Arguments:

  • tx_digest: the digest associated to the transaction.

Returns:

the tx, if present

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#get_contract_instance","title":"get_contract_instance","text":"
def get_contract_instance(contract_interface: Dict[str, str],\ncontract_address: Optional[str] = None) -> Any\n

Get the instance of a contract.

Arguments:

  • contract_interface: the contract interface.
  • contract_address: the contract address.

Returns:

the contract instance

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#get_deploy_transaction","title":"get_deploy_transaction","text":"
def get_deploy_transaction(contract_interface: Dict[str, str],\ndeployer_address: Address,\nvalue: int = 0,\ngas: int = 0,\ngas_price: Optional[str] = None,\ngas_price_strategy: Optional[str] = None,\n**kwargs: Any) -> Optional[JSONLike]\n

Get the transaction to deploy the smart contract.

Arguments:

  • contract_interface: the contract interface.
  • deployer_address: The address that will deploy the contract.
  • value: value to send to contract (in Wei)
  • gas: the gas to be used (in Wei)
  • gas_price: the gas price (in Wei)
  • gas_price_strategy: the gas price strategy to be used.
  • kwargs: keyword arguments

Returns:

the transaction dictionary.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#is_valid_address","title":"is_valid_address","text":"
@classmethod\ndef is_valid_address(cls, address: Address) -> bool\n

Check if the address is valid.

Arguments:

  • address: the address to validate

Returns:

whether the address is valid

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#ethereumfaucetapi-objects","title":"EthereumFaucetApi Objects","text":"
class EthereumFaucetApi(FaucetApi)\n

Ethereum testnet faucet API.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#get_wealth","title":"get_wealth","text":"
def get_wealth(address: Address, url: Optional[str] = None) -> None\n

Get wealth from the faucet for the provided address.

Arguments:

  • address: the address.
  • url: the url

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#lrulockwrapper-objects","title":"LruLockWrapper Objects","text":"
class LruLockWrapper()\n

Wrapper for LRU with threading.Lock.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#__init___2","title":"__init__","text":"
def __init__(lru: LRU) -> None\n

Init wrapper.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#__getitem__","title":"__getitem__","text":"
def __getitem__(*args: Any, **kwargs: Any) -> Any\n

Get item

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#__setitem__","title":"__setitem__","text":"
def __setitem__(*args: Any, **kwargs: Any) -> Any\n

Set item.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#__contains__","title":"__contains__","text":"
def __contains__(*args: Any, **kwargs: Any) -> Any\n

Contain item.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#__delitem__","title":"__delitem__","text":"
def __delitem__(*args: Any, **kwargs: Any) -> Any\n

Del item.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#set_wrapper_for_web3py_session_cache","title":"set_wrapper_for_web3py_session_cache","text":"
def set_wrapper_for_web3py_session_cache() -> None\n

Wrap web3py session cache with threading.Lock.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/","title":"Helper","text":""},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#pluginsaea-ledger-fetchaiaea_ledger_fetchai_cosmos","title":"plugins.aea-ledger-fetchai.aea_ledger_fetchai._cosmos","text":"

Cosmos module wrapping the public and private key cryptography and ledger api.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#dataencrypt-objects","title":"DataEncrypt Objects","text":"
class DataEncrypt()\n

Class to encrypt/decrypt data strings with password provided.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#encrypt","title":"encrypt","text":"
@classmethod\ndef encrypt(cls, data: bytes, password: str) -> bytes\n

Encrypt data with password.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#bytes_encode","title":"bytes_encode","text":"
@staticmethod\ndef bytes_encode(data: bytes) -> str\n

Encode bytes to ascii friendly string.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#bytes_decode","title":"bytes_decode","text":"
@staticmethod\ndef bytes_decode(data: str) -> bytes\n

Decode ascii friendly string to bytes.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#decrypt","title":"decrypt","text":"
@classmethod\ndef decrypt(cls, encrypted_data: bytes, password: str) -> bytes\n

Decrypt data with password provided.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#cosmoshelper-objects","title":"CosmosHelper Objects","text":"
class CosmosHelper(Helper)\n

Helper class usable as Mixin for CosmosApi or as standalone class.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#is_transaction_settled","title":"is_transaction_settled","text":"
@staticmethod\ndef is_transaction_settled(tx_receipt: JSONLike) -> bool\n

Check whether a transaction is settled or not.

Arguments:

  • tx_receipt: the receipt of the transaction.

Returns:

True if the transaction has been settled, False o/w.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#get_code_id","title":"get_code_id","text":"
@classmethod\ndef get_code_id(cls, tx_receipt: JSONLike) -> Optional[int]\n

Retrieve the code_id from a transaction receipt.

Arguments:

  • tx_receipt: the receipt of the transaction.

Returns:

the code id, if present

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#get_event_attributes","title":"get_event_attributes","text":"
@staticmethod\ndef get_event_attributes(tx_receipt: JSONLike) -> Dict\n

Retrieve events attributes from tx receipt.

Arguments:

  • tx_receipt: the receipt of the transaction.

Returns:

dict

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#get_contract_address","title":"get_contract_address","text":"
@classmethod\ndef get_contract_address(cls, tx_receipt: JSONLike) -> Optional[str]\n

Retrieve the contract_address from a transaction receipt.

Arguments:

  • tx_receipt: the receipt of the transaction.

Returns:

the contract address, if present

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#is_transaction_valid","title":"is_transaction_valid","text":"
@staticmethod\ndef is_transaction_valid(tx: JSONLike, seller: Address, client: Address,\ntx_nonce: str, amount: int) -> bool\n

Check whether a transaction is valid or not.

Arguments:

  • tx: the transaction.
  • seller: the address of the seller.
  • client: the address of the client.
  • tx_nonce: the transaction nonce.
  • amount: the amount we expect to get from the transaction.

Returns:

True if the random_message is equals to tx['input']

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#generate_tx_nonce","title":"generate_tx_nonce","text":"
@staticmethod\ndef generate_tx_nonce(seller: Address, client: Address) -> str\n

Generate a unique hash to distinguish transactions with the same terms.

Arguments:

  • seller: the address of the seller.
  • client: the address of the client.

Returns:

return the hash in hex.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#get_address_from_public_key","title":"get_address_from_public_key","text":"
@classmethod\ndef get_address_from_public_key(cls, public_key: str) -> str\n

Get the address from the public key.

Arguments:

  • public_key: the public key

Returns:

str

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#recover_message","title":"recover_message","text":"
@classmethod\ndef recover_message(cls,\nmessage: bytes,\nsignature: str,\nis_deprecated_mode: bool = False) -> Tuple[Address, ...]\n

Recover the addresses from the hash.

Arguments:

  • message: the message we expect
  • signature: the transaction signature
  • is_deprecated_mode: if the deprecated signing was used

Returns:

the recovered addresses

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#recover_public_keys_from_message","title":"recover_public_keys_from_message","text":"
@classmethod\ndef recover_public_keys_from_message(\ncls,\nmessage: bytes,\nsignature: str,\nis_deprecated_mode: bool = False) -> Tuple[str, ...]\n

Get the public key used to produce the signature of the message

Arguments:

  • message: raw bytes used to produce signature
  • signature: signature of the message
  • is_deprecated_mode: if the deprecated signing was used

Returns:

the recovered public keys

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#get_hash","title":"get_hash","text":"
@staticmethod\ndef get_hash(message: bytes) -> str\n

Get the hash of a message.

Arguments:

  • message: the message to be hashed.

Returns:

the hash of the message.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#is_valid_address","title":"is_valid_address","text":"
@classmethod\ndef is_valid_address(cls, address: Address) -> bool\n

Check if the address is valid.

Arguments:

  • address: the address to validate

Returns:

whether address is valid or not

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#load_contract_interface","title":"load_contract_interface","text":"
@classmethod\ndef load_contract_interface(cls, file_path: Path) -> Dict[str, str]\n

Load contract interface.

Arguments:

  • file_path: the file path to the interface

Returns:

the interface

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#cosmoscrypto-objects","title":"CosmosCrypto Objects","text":"
class CosmosCrypto(Crypto[SigningKey])\n

Class wrapping the Account Generation from Ethereum ledger.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#__init__","title":"__init__","text":"
def __init__(private_key_path: Optional[str] = None,\npassword: Optional[str] = None) -> None\n

Instantiate an ethereum crypto object.

Arguments:

  • private_key_path: the private key path of the agent
  • password: the password to encrypt/decrypt the private key.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#private_key","title":"private_key","text":"
@property\ndef private_key() -> str\n

Return a private key.

Returns:

a private key string

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#public_key","title":"public_key","text":"
@property\ndef public_key() -> str\n

Return a public key in hex format.

Returns:

a public key string in hex format

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#address","title":"address","text":"
@property\ndef address() -> str\n

Return the address for the key pair.

Returns:

a display_address str

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#load_private_key_from_path","title":"load_private_key_from_path","text":"
@classmethod\ndef load_private_key_from_path(cls,\nfile_name: str,\npassword: Optional[str] = None) -> SigningKey\n

Load a private key in hex format from a file.

Arguments:

  • file_name: the path to the hex file.
  • password: the password to encrypt/decrypt the private key.

Returns:

the Entity.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#sign_message","title":"sign_message","text":"
def sign_message(message: bytes, is_deprecated_mode: bool = False) -> str\n

Sign a message in bytes string form.

Arguments:

  • message: the message to be signed
  • is_deprecated_mode: if the deprecated signing is used

Returns:

signature of the message in string form

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#sign_transaction","title":"sign_transaction","text":"
def sign_transaction(transaction: JSONLike) -> JSONLike\n

Sign a transaction in bytes string form.

Arguments:

  • transaction: the transaction to be signed

Returns:

signed transaction

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#generate_private_key","title":"generate_private_key","text":"
@classmethod\ndef generate_private_key(cls) -> SigningKey\n

Generate a key pair for cosmos network.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#encrypt_1","title":"encrypt","text":"
def encrypt(password: str) -> str\n

Encrypt the private key and return in json.

Arguments:

  • password: the password to decrypt.

Returns:

json string containing encrypted private key.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#decrypt_1","title":"decrypt","text":"
@classmethod\ndef decrypt(cls, keyfile_json: str, password: str) -> str\n

Decrypt the private key and return in raw form.

Arguments:

  • keyfile_json: json string containing encrypted private key.
  • password: the password to decrypt.

Returns:

the raw private key.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#_cosmosapi-objects","title":"_CosmosApi Objects","text":"
class _CosmosApi(LedgerApi)\n

Class to interact with the Cosmos SDK via a HTTP APIs.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#__init___1","title":"__init__","text":"
def __init__(**kwargs: Any) -> None\n

Initialize the Cosmos ledger APIs.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#api","title":"api","text":"
@property\ndef api() -> Any\n

Get the underlying API object.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#get_balance","title":"get_balance","text":"
def get_balance(address: Address) -> Optional[int]\n

Get the balance of a given account.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#get_state","title":"get_state","text":"
def get_state(callable_name: str, *args: Any,\n**kwargs: Any) -> Optional[JSONLike]\n

Call a specified function on the ledger API.

Based on the cosmos REST API specification, which takes a path (strings separated by '/'). The convention here is to define the root of the path (txs, blocks, etc.) as the callable_name and the rest of the path as args.

Arguments:

  • callable_name: name of the callable
  • args: positional arguments
  • kwargs: keyword arguments

Returns:

the transaction dictionary

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#get_deploy_transaction","title":"get_deploy_transaction","text":"
def get_deploy_transaction(contract_interface: Dict[str, str],\ndeployer_address: Address,\n**kwargs: Any) -> Optional[JSONLike]\n

Get the transaction to deploy the smart contract.

Dispatches to _get_storage_transaction and _get_init_transaction based on kwargs.

Arguments:

  • contract_interface: the contract interface.
  • deployer_address: The address that will deploy the contract.
  • kwargs: keyword arguments.

Returns:

the transaction dictionary.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#get_handle_transaction","title":"get_handle_transaction","text":"
def get_handle_transaction(\nsender_address: Address,\ncontract_address: Address,\nhandle_msg: Any,\namount: int,\ntx_fee: int,\ndenom: Optional[str] = None,\ngas: int = DEFAULT_GAS_AMOUNT,\nmemo: str = \"\",\nchain_id: Optional[str] = None,\naccount_number: Optional[int] = None,\nsequence: Optional[int] = None,\ntx_fee_denom: Optional[str] = None) -> Optional[JSONLike]\n

Create a CosmWasm HandleMsg transaction.

Arguments:

  • sender_address: the sender address of the message initiator.
  • contract_address: the address of the smart contract.
  • handle_msg: HandleMsg in JSON format.
  • amount: Funds amount sent with transaction.
  • tx_fee: the tx fee accepted.
  • denom: the name of the denomination of the contract funds
  • gas: Maximum amount of gas to be used on executing command.
  • memo: any string comment.
  • chain_id: the Chain ID of the CosmWasm transaction. Default is 1 (i.e. mainnet).
  • account_number: Account number
  • sequence: Sequence
  • tx_fee_denom: Denomination of tx_fee, identical with denom param when None

Returns:

the unsigned CosmWasm HandleMsg

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#execute_contract_query","title":"execute_contract_query","text":"
def execute_contract_query(contract_address: Address,\nquery_msg: JSONLike) -> Optional[JSONLike]\n

Execute a CosmWasm QueryMsg. QueryMsg doesn't require signing.

Arguments:

  • contract_address: the address of the smart contract.
  • query_msg: QueryMsg in JSON format.

Returns:

the message receipt

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#get_transfer_transaction","title":"get_transfer_transaction","text":"
def get_transfer_transaction(sender_address: Address,\ndestination_address: Address,\namount: int,\ntx_fee: int,\ntx_nonce: str,\ndenom: Optional[str] = None,\ngas: int = DEFAULT_GAS_AMOUNT,\nmemo: str = \"\",\nchain_id: Optional[str] = None,\naccount_number: Optional[int] = None,\nsequence: Optional[int] = None,\ntx_fee_denom: Optional[str] = None,\n**kwargs: Any) -> Optional[JSONLike]\n

Submit a transfer transaction to the ledger.

Arguments:

  • sender_address: the sender address of the payer.
  • destination_address: the destination address of the payee.
  • amount: the amount of wealth to be transferred.
  • tx_fee: the transaction fee.
  • tx_nonce: verifies the authenticity of the tx
  • denom: the denomination of tx fee and amount
  • gas: the gas used.
  • memo: memo to include in tx.
  • chain_id: the chain ID of the transaction.
  • account_number: Account number
  • sequence: Sequence
  • tx_fee_denom: Denomination of tx_fee, identical with denom param when None
  • kwargs: keyword arguments.

Returns:

the transfer transaction

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#get_packed_exec_msg","title":"get_packed_exec_msg","text":"
def get_packed_exec_msg(sender_address: Address,\ncontract_address: str,\nmsg: JSONLike,\nfunds: int = 0,\ndenom: Optional[str] = None) -> ProtoAny\n

Create and pack MsgExecuteContract

Arguments:

  • sender_address: Address of sender
  • contract_address: Address of contract
  • msg: Paramaters to be passed to smart contract
  • funds: Funds to be sent to smart contract
  • denom: the denomination of funds

Returns:

Packed MsgExecuteContract

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#get_packed_send_msg","title":"get_packed_send_msg","text":"
def get_packed_send_msg(from_address: Address,\nto_address: Address,\namount: int,\ndenom: Optional[str] = None) -> ProtoAny\n

Generate and pack MsgSend

Arguments:

  • from_address: Address of sender
  • to_address: Address of recipient
  • amount: amount of coins to be sent
  • denom: the denomination of and amount

Returns:

packer ProtoAny type message

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#get_multi_transaction","title":"get_multi_transaction","text":"
def get_multi_transaction(from_addresses: List[str],\npub_keys: Optional[List[bytes]],\nmsgs: List[ProtoAny],\ngas: int,\ntx_fee: int = 0,\nmemo: str = \"\",\nchain_id: Optional[str] = None,\ndenom: Optional[str] = None,\ntx_fee_denom: Optional[str] = None) -> JSONLike\n

Generate transaction with multiple messages

Arguments:

  • from_addresses: Addresses of signers
  • pub_keys: Public keys of signers
  • msgs: Messages to be included in transaction
  • gas: the gas used.
  • tx_fee: the transaction fee.
  • memo: memo to include in tx.
  • chain_id: the chain ID of the transaction.
  • denom: the denomination of tx fee
  • tx_fee_denom: Denomination of tx_fee, identical with denom param when None

Raises:

  • None: RuntimeError if number of pubkeys is not equal to number of from_addresses

Returns:

the transaction

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#send_signed_transaction","title":"send_signed_transaction","text":"
def send_signed_transaction(tx_signed: JSONLike) -> Optional[str]\n

Send a signed transaction and wait for confirmation.

Arguments:

  • tx_signed: the signed transaction

Returns:

tx_digest, if present

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#get_transaction_receipt","title":"get_transaction_receipt","text":"
def get_transaction_receipt(tx_digest: str) -> Optional[JSONLike]\n

Get the transaction receipt for a transaction digest.

Arguments:

  • tx_digest: the digest associated to the transaction.

Returns:

the tx receipt, if present

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#get_transaction","title":"get_transaction","text":"
def get_transaction(tx_digest: str) -> Optional[JSONLike]\n

Get the transaction for a transaction digest.

Arguments:

  • tx_digest: the digest associated to the transaction.

Returns:

the tx, if present

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#get_contract_instance","title":"get_contract_instance","text":"
def get_contract_instance(contract_interface: Dict[str, str],\ncontract_address: Optional[str] = None) -> Any\n

Get the instance of a contract.

Arguments:

  • contract_interface: the contract interface.
  • contract_address: the contract address.

Returns:

the contract instance

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#update_with_gas_estimate","title":"update_with_gas_estimate","text":"
def update_with_gas_estimate(transaction: JSONLike) -> JSONLike\n

Attempts to update the transaction with a gas estimate

Arguments:

  • transaction: the transaction

Raises:

  • None: NotImplementedError

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#cosmosapi-objects","title":"CosmosApi Objects","text":"
class CosmosApi(_CosmosApi, CosmosHelper)\n

Class to interact with the Cosmos SDK via a HTTP APIs.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#cosmosfaucetapi-objects","title":"CosmosFaucetApi Objects","text":"
class CosmosFaucetApi(FaucetApi)\n

Cosmos testnet faucet API.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#__init___2","title":"__init__","text":"
def __init__(poll_interval: Optional[float] = None,\nfinal_wait_interval: Optional[float] = None)\n

Initialize CosmosFaucetApi.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#get_wealth","title":"get_wealth","text":"
def get_wealth(address: Address, url: Optional[str] = None) -> None\n

Get wealth from the faucet for the provided address.

Arguments:

  • address: the address.
  • url: the url

Raises:

  • None: RuntimeError of explicit faucet failures
"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/fetchai/","title":"API","text":""},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/fetchai/#pluginsaea-ledger-fetchaiaea_ledger_fetchaifetchai","title":"plugins.aea-ledger-fetchai.aea_ledger_fetchai.fetchai","text":"

Fetchai module wrapping the public and private key cryptography and ledger api.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/fetchai/#fetchaihelper-objects","title":"FetchAIHelper Objects","text":"
class FetchAIHelper(CosmosHelper)\n

Helper class usable as Mixin for FetchAIApi or as standalone class.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/fetchai/#fetchaicrypto-objects","title":"FetchAICrypto Objects","text":"
class FetchAICrypto(CosmosCrypto)\n

Class wrapping the Entity Generation from Fetch.AI ledger.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/fetchai/#fetchaiapi-objects","title":"FetchAIApi Objects","text":"
class FetchAIApi(_CosmosApi, FetchAIHelper)\n

Class to interact with the Fetch ledger APIs.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/fetchai/#__init__","title":"__init__","text":"
def __init__(**kwargs: Any) -> None\n

Initialize the Fetch.ai ledger APIs.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/fetchai/#fetchaifaucetapi-objects","title":"FetchAIFaucetApi Objects","text":"
class FetchAIFaucetApi(CosmosFaucetApi)\n

Fetchai testnet faucet API.

"},{"location":"aea-framework-documentation/api/protocols/base/","title":"Base","text":""},{"location":"aea-framework-documentation/api/protocols/base/#aeaprotocolsbase","title":"aea.protocols.base","text":"

This module contains the base message and serialization definition.

"},{"location":"aea-framework-documentation/api/protocols/base/#message-objects","title":"Message Objects","text":"
class Message()\n

This class implements a message.

"},{"location":"aea-framework-documentation/api/protocols/base/#performative-objects","title":"Performative Objects","text":"
class Performative(Enum)\n

Performatives for the base message.

"},{"location":"aea-framework-documentation/api/protocols/base/#__str__","title":"__str__","text":"
def __str__() -> str\n

Get the string representation.

"},{"location":"aea-framework-documentation/api/protocols/base/#__init__","title":"__init__","text":"
def __init__(_body: Optional[Dict] = None, **kwargs: Any) -> None\n

Initialize a Message object.

Arguments:

  • _body: the dictionary of values to hold.
  • kwargs: any additional value to add to the body. It will overwrite the body values.

"},{"location":"aea-framework-documentation/api/protocols/base/#json","title":"json","text":"
def json() -> dict\n

Get json friendly str representation of the message.

"},{"location":"aea-framework-documentation/api/protocols/base/#from_json","title":"from_json","text":"
@classmethod\ndef from_json(cls, data: dict) -> \"Message\"\n

Construct message instance from json data.

"},{"location":"aea-framework-documentation/api/protocols/base/#valid_performatives","title":"valid_performatives","text":"
@property\ndef valid_performatives() -> Set[str]\n

Get valid performatives.

"},{"location":"aea-framework-documentation/api/protocols/base/#has_sender","title":"has_sender","text":"
@property\ndef has_sender() -> bool\n

Check if it has a sender.

"},{"location":"aea-framework-documentation/api/protocols/base/#sender","title":"sender","text":"
@property\ndef sender() -> Address\n

Get the sender of the message in Address form.

"},{"location":"aea-framework-documentation/api/protocols/base/#sender_1","title":"sender","text":"
@sender.setter\ndef sender(sender: Address) -> None\n

Set the sender of the message.

"},{"location":"aea-framework-documentation/api/protocols/base/#has_to","title":"has_to","text":"
@property\ndef has_to() -> bool\n

Check if it has a sender.

"},{"location":"aea-framework-documentation/api/protocols/base/#to","title":"to","text":"
@property\ndef to() -> Address\n

Get address of receiver.

"},{"location":"aea-framework-documentation/api/protocols/base/#to_1","title":"to","text":"
@to.setter\ndef to(to: Address) -> None\n

Set address of receiver.

"},{"location":"aea-framework-documentation/api/protocols/base/#dialogue_reference","title":"dialogue_reference","text":"
@property\ndef dialogue_reference() -> Tuple[str, str]\n

Get the dialogue_reference of the message.

"},{"location":"aea-framework-documentation/api/protocols/base/#message_id","title":"message_id","text":"
@property\ndef message_id() -> int\n

Get the message_id of the message.

"},{"location":"aea-framework-documentation/api/protocols/base/#performative","title":"performative","text":"
@property\ndef performative() -> \"Performative\"\n

Get the performative of the message.

"},{"location":"aea-framework-documentation/api/protocols/base/#target","title":"target","text":"
@property\ndef target() -> int\n

Get the target of the message.

"},{"location":"aea-framework-documentation/api/protocols/base/#set","title":"set","text":"
def set(key: str, value: Any) -> None\n

Set key and value pair.

Arguments:

  • key: the key.
  • value: the value.

"},{"location":"aea-framework-documentation/api/protocols/base/#get","title":"get","text":"
def get(key: str) -> Optional[Any]\n

Get value for key.

"},{"location":"aea-framework-documentation/api/protocols/base/#is_set","title":"is_set","text":"
def is_set(key: str) -> bool\n

Check value is set for key.

"},{"location":"aea-framework-documentation/api/protocols/base/#__eq__","title":"__eq__","text":"
def __eq__(other: Any) -> bool\n

Compare with another object.

"},{"location":"aea-framework-documentation/api/protocols/base/#__repr__","title":"__repr__","text":"
def __repr__() -> str\n

Get the representation of the message.

"},{"location":"aea-framework-documentation/api/protocols/base/#__str___1","title":"__str__","text":"
def __str__() -> str\n

Get the string representation of the message. Abbreviated to prevent spamming of logs.

"},{"location":"aea-framework-documentation/api/protocols/base/#encode","title":"encode","text":"
def encode() -> bytes\n

Encode the message.

"},{"location":"aea-framework-documentation/api/protocols/base/#decode","title":"decode","text":"
@classmethod\ndef decode(cls, data: bytes) -> \"Message\"\n

Decode the message.

"},{"location":"aea-framework-documentation/api/protocols/base/#has_dialogue_info","title":"has_dialogue_info","text":"
@property\ndef has_dialogue_info() -> bool\n

Check whether a message has the dialogue fields populated.

More precisely, it checks whether the fields 'message_id', 'target' and 'dialogue_reference' are set.

Returns:

True if the message has the dialogue fields set, False otherwise.

"},{"location":"aea-framework-documentation/api/protocols/base/#encoder-objects","title":"Encoder Objects","text":"
class Encoder(ABC)\n

Encoder interface.

"},{"location":"aea-framework-documentation/api/protocols/base/#encode_1","title":"encode","text":"
@staticmethod\n@abstractmethod\ndef encode(msg: Message) -> bytes\n

Encode a message.

Arguments:

  • msg: the message to be encoded.

Returns:

the encoded message.

"},{"location":"aea-framework-documentation/api/protocols/base/#decoder-objects","title":"Decoder Objects","text":"
class Decoder(ABC)\n

Decoder interface.

"},{"location":"aea-framework-documentation/api/protocols/base/#decode_1","title":"decode","text":"
@staticmethod\n@abstractmethod\ndef decode(obj: bytes) -> Message\n

Decode a message.

Arguments:

  • obj: the sequence of bytes to be decoded.

Returns:

the decoded message.

"},{"location":"aea-framework-documentation/api/protocols/base/#serializer-objects","title":"Serializer Objects","text":"
class Serializer(Encoder, Decoder, ABC)\n

The implementations of this class defines a serialization layer for a protocol.

"},{"location":"aea-framework-documentation/api/protocols/base/#protocol-objects","title":"Protocol Objects","text":"
class Protocol(Component)\n

This class implements a specifications for a protocol.

It includes a serializer to encode/decode a message.

"},{"location":"aea-framework-documentation/api/protocols/base/#__init___1","title":"__init__","text":"
def __init__(configuration: ProtocolConfig, message_class: Type[Message],\n**kwargs: Any) -> None\n

Initialize the protocol manager.

Arguments:

  • configuration: the protocol configurations.
  • message_class: the message class.
  • kwargs: the keyword arguments.

"},{"location":"aea-framework-documentation/api/protocols/base/#serializer","title":"serializer","text":"
@property\ndef serializer() -> Type[Serializer]\n

Get the serializer.

"},{"location":"aea-framework-documentation/api/protocols/base/#from_dir","title":"from_dir","text":"
@classmethod\ndef from_dir(cls, directory: str, **kwargs: Any) -> \"Protocol\"\n

Load the protocol from a directory.

Arguments:

  • directory: the directory to the skill package.
  • kwargs: the keyword arguments.

Returns:

the protocol object.

"},{"location":"aea-framework-documentation/api/protocols/base/#from_config","title":"from_config","text":"
@classmethod\ndef from_config(cls, configuration: ProtocolConfig,\n**kwargs: Any) -> \"Protocol\"\n

Load the protocol from configuration.

Arguments:

  • configuration: the protocol configuration.
  • kwargs: the keyword arguments.

Returns:

the protocol object.

"},{"location":"aea-framework-documentation/api/protocols/base/#protocol_id","title":"protocol_id","text":"
@property\ndef protocol_id() -> PublicId\n

Get protocol id.

"},{"location":"aea-framework-documentation/api/protocols/base/#protocol_specification_id","title":"protocol_specification_id","text":"
@property\ndef protocol_specification_id() -> PublicId\n

Get protocol specification id.

"},{"location":"aea-framework-documentation/api/protocols/base/#__repr___1","title":"__repr__","text":"
def __repr__() -> str\n

Get str representation of the protocol.

"},{"location":"aea-framework-documentation/api/protocols/default/custom_types/","title":"Custom Types","text":""},{"location":"aea-framework-documentation/api/protocols/default/custom_types/#packagesfetchaiprotocolsdefaultcustom_types","title":"packages.fetchai.protocols.default.custom_types","text":"

This module contains class representations corresponding to every custom type in the protocol specification.

"},{"location":"aea-framework-documentation/api/protocols/default/custom_types/#errorcode-objects","title":"ErrorCode Objects","text":"
class ErrorCode(Enum)\n

This class represents an instance of ErrorCode.

"},{"location":"aea-framework-documentation/api/protocols/default/custom_types/#encode","title":"encode","text":"
@staticmethod\ndef encode(error_code_protobuf_object: Any,\nerror_code_object: \"ErrorCode\") -> None\n

Encode an instance of this class into the protocol buffer object.

The protocol buffer object in the error_code_protobuf_object argument is matched with the instance of this class in the 'error_code_object' argument.

Arguments:

  • error_code_protobuf_object: the protocol buffer object whose type corresponds with this class.
  • error_code_object: an instance of this class to be encoded in the protocol buffer object.

"},{"location":"aea-framework-documentation/api/protocols/default/custom_types/#decode","title":"decode","text":"
@classmethod\ndef decode(cls, error_code_protobuf_object: Any) -> \"ErrorCode\"\n

Decode a protocol buffer object that corresponds with this class into an instance of this class.

A new instance of this class is created that matches the protocol buffer object in the 'error_code_protobuf_object' argument.

Arguments:

  • error_code_protobuf_object: the protocol buffer object whose type corresponds with this class.

Returns:

A new instance of this class that matches the protocol buffer object in the 'error_code_protobuf_object' argument.

"},{"location":"aea-framework-documentation/api/protocols/default/dialogues/","title":"Dialogues","text":""},{"location":"aea-framework-documentation/api/protocols/default/dialogues/#packagesfetchaiprotocolsdefaultdialogues","title":"packages.fetchai.protocols.default.dialogues","text":"

This module contains the classes required for default dialogue management.

  • DefaultDialogue: The dialogue class maintains state of a dialogue and manages it.
  • DefaultDialogues: The dialogues class keeps track of all dialogues.

"},{"location":"aea-framework-documentation/api/protocols/default/dialogues/#defaultdialogue-objects","title":"DefaultDialogue Objects","text":"
class DefaultDialogue(Dialogue)\n

The default dialogue class maintains state of a dialogue and manages it.

"},{"location":"aea-framework-documentation/api/protocols/default/dialogues/#role-objects","title":"Role Objects","text":"
class Role(Dialogue.Role)\n

This class defines the agent's role in a default dialogue.

"},{"location":"aea-framework-documentation/api/protocols/default/dialogues/#endstate-objects","title":"EndState Objects","text":"
class EndState(Dialogue.EndState)\n

This class defines the end states of a default dialogue.

"},{"location":"aea-framework-documentation/api/protocols/default/dialogues/#__init__","title":"__init__","text":"
def __init__(dialogue_label: DialogueLabel,\nself_address: Address,\nrole: Dialogue.Role,\nmessage_class: Type[DefaultMessage] = DefaultMessage) -> None\n

Initialize a dialogue.

Arguments:

  • dialogue_label: the identifier of the dialogue
  • self_address: the address of the entity for whom this dialogue is maintained
  • role: the role of the agent this dialogue is maintained for
  • message_class: the message class used

"},{"location":"aea-framework-documentation/api/protocols/default/dialogues/#defaultdialogues-objects","title":"DefaultDialogues Objects","text":"
class DefaultDialogues(Dialogues, ABC)\n

This class keeps track of all default dialogues.

"},{"location":"aea-framework-documentation/api/protocols/default/dialogues/#__init___1","title":"__init__","text":"
def __init__(self_address: Address,\nrole_from_first_message: Callable[[Message, Address],\nDialogue.Role],\ndialogue_class: Type[DefaultDialogue] = DefaultDialogue) -> None\n

Initialize dialogues.

Arguments:

  • self_address: the address of the entity for whom dialogues are maintained
  • dialogue_class: the dialogue class used
  • role_from_first_message: the callable determining role from first message
"},{"location":"aea-framework-documentation/api/protocols/default/message/","title":"Message","text":""},{"location":"aea-framework-documentation/api/protocols/default/message/#packagesfetchaiprotocolsdefaultmessage","title":"packages.fetchai.protocols.default.message","text":"

This module contains default's message definition.

"},{"location":"aea-framework-documentation/api/protocols/default/message/#defaultmessage-objects","title":"DefaultMessage Objects","text":"
class DefaultMessage(Message)\n

A protocol for exchanging any bytes message.

"},{"location":"aea-framework-documentation/api/protocols/default/message/#performative-objects","title":"Performative Objects","text":"
class Performative(Message.Performative)\n

Performatives for the default protocol.

"},{"location":"aea-framework-documentation/api/protocols/default/message/#__str__","title":"__str__","text":"
def __str__() -> str\n

Get the string representation.

"},{"location":"aea-framework-documentation/api/protocols/default/message/#__init__","title":"__init__","text":"
def __init__(performative: Performative,\ndialogue_reference: Tuple[str, str] = (\"\", \"\"),\nmessage_id: int = 1,\ntarget: int = 0,\n**kwargs: Any)\n

Initialise an instance of DefaultMessage.

Arguments:

  • message_id: the message id.
  • dialogue_reference: the dialogue reference.
  • target: the message target.
  • performative: the message performative.
  • **kwargs: extra options.

"},{"location":"aea-framework-documentation/api/protocols/default/message/#valid_performatives","title":"valid_performatives","text":"
@property\ndef valid_performatives() -> Set[str]\n

Get valid performatives.

"},{"location":"aea-framework-documentation/api/protocols/default/message/#dialogue_reference","title":"dialogue_reference","text":"
@property\ndef dialogue_reference() -> Tuple[str, str]\n

Get the dialogue_reference of the message.

"},{"location":"aea-framework-documentation/api/protocols/default/message/#message_id","title":"message_id","text":"
@property\ndef message_id() -> int\n

Get the message_id of the message.

"},{"location":"aea-framework-documentation/api/protocols/default/message/#performative","title":"performative","text":"
@property\ndef performative() -> Performative\n

Get the performative of the message.

"},{"location":"aea-framework-documentation/api/protocols/default/message/#target","title":"target","text":"
@property\ndef target() -> int\n

Get the target of the message.

"},{"location":"aea-framework-documentation/api/protocols/default/message/#content","title":"content","text":"
@property\ndef content() -> bytes\n

Get the 'content' content from the message.

"},{"location":"aea-framework-documentation/api/protocols/default/message/#error_code","title":"error_code","text":"
@property\ndef error_code() -> CustomErrorCode\n

Get the 'error_code' content from the message.

"},{"location":"aea-framework-documentation/api/protocols/default/message/#error_data","title":"error_data","text":"
@property\ndef error_data() -> Dict[str, bytes]\n

Get the 'error_data' content from the message.

"},{"location":"aea-framework-documentation/api/protocols/default/message/#error_msg","title":"error_msg","text":"
@property\ndef error_msg() -> str\n

Get the 'error_msg' content from the message.

"},{"location":"aea-framework-documentation/api/protocols/default/serialization/","title":"Serialization","text":""},{"location":"aea-framework-documentation/api/protocols/default/serialization/#packagesfetchaiprotocolsdefaultserialization","title":"packages.fetchai.protocols.default.serialization","text":"

Serialization module for default protocol.

"},{"location":"aea-framework-documentation/api/protocols/default/serialization/#defaultserializer-objects","title":"DefaultSerializer Objects","text":"
class DefaultSerializer(Serializer)\n

Serialization for the 'default' protocol.

"},{"location":"aea-framework-documentation/api/protocols/default/serialization/#encode","title":"encode","text":"
@staticmethod\ndef encode(msg: Message) -> bytes\n

Encode a 'Default' message into bytes.

Arguments:

  • msg: the message object.

Returns:

the bytes.

"},{"location":"aea-framework-documentation/api/protocols/default/serialization/#decode","title":"decode","text":"
@staticmethod\ndef decode(obj: bytes) -> Message\n

Decode bytes into a 'Default' message.

Arguments:

  • obj: the bytes object.

Returns:

the 'Default' message.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/","title":"Base","text":""},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#aeaprotocolsdialoguebase","title":"aea.protocols.dialogue.base","text":"

This module contains the classes required for dialogue management.

  • DialogueLabel: The dialogue label class acts as an identifier for dialogues.
  • Dialogue: The dialogue class maintains state of a dialogue and manages it.
  • Dialogues: The dialogues class keeps track of all dialogues.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#invaliddialoguemessage-objects","title":"InvalidDialogueMessage Objects","text":"
class InvalidDialogueMessage(Exception)\n

Exception for adding invalid message to a dialogue.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#dialoguelabel-objects","title":"DialogueLabel Objects","text":"
class DialogueLabel()\n

The dialogue label class acts as an identifier for dialogues.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#__init__","title":"__init__","text":"
def __init__(dialogue_reference: Tuple[str,\nstr], dialogue_opponent_addr: Address,\ndialogue_starter_addr: Address) -> None\n

Initialize a dialogue label.

Arguments:

  • dialogue_reference: the reference of the dialogue.
  • dialogue_opponent_addr: the addr of the agent with which the dialogue is kept.
  • dialogue_starter_addr: the addr of the agent which started the dialogue.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#dialogue_reference","title":"dialogue_reference","text":"
@property\ndef dialogue_reference() -> Tuple[str, str]\n

Get the dialogue reference.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#dialogue_starter_reference","title":"dialogue_starter_reference","text":"
@property\ndef dialogue_starter_reference() -> str\n

Get the dialogue starter reference.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#dialogue_responder_reference","title":"dialogue_responder_reference","text":"
@property\ndef dialogue_responder_reference() -> str\n

Get the dialogue responder reference.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#dialogue_opponent_addr","title":"dialogue_opponent_addr","text":"
@property\ndef dialogue_opponent_addr() -> str\n

Get the address of the dialogue opponent.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#dialogue_starter_addr","title":"dialogue_starter_addr","text":"
@property\ndef dialogue_starter_addr() -> str\n

Get the address of the dialogue starter.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#__eq__","title":"__eq__","text":"
def __eq__(other: Any) -> bool\n

Check for equality between two DialogueLabel objects.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#__hash__","title":"__hash__","text":"
def __hash__() -> int\n

Turn object into hash.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#json","title":"json","text":"
@property\ndef json() -> Dict\n

Return the JSON representation.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#from_json","title":"from_json","text":"
@classmethod\ndef from_json(cls, obj: Dict[str, str]) -> \"DialogueLabel\"\n

Get dialogue label from json.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#get_incomplete_version","title":"get_incomplete_version","text":"
def get_incomplete_version() -> \"DialogueLabel\"\n

Get the incomplete version of the label.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#__str__","title":"__str__","text":"
def __str__() -> str\n

Get the string representation.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#from_str","title":"from_str","text":"
@classmethod\ndef from_str(cls, obj: str) -> \"DialogueLabel\"\n

Get the dialogue label from string representation.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#_dialoguemeta-objects","title":"_DialogueMeta Objects","text":"
class _DialogueMeta(type)\n

Metaclass for Dialogue.

Creates class level Rules instance to share among instances

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#__new__","title":"__new__","text":"
def __new__(cls, name: str, bases: Tuple[Type], dct: Dict) -> \"_DialogueMeta\"\n

Construct a new type.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#dialogue-objects","title":"Dialogue Objects","text":"
class Dialogue(metaclass=_DialogueMeta)\n

The dialogue class maintains state of a dialogue and manages it.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#rules-objects","title":"Rules Objects","text":"
class Rules()\n

This class defines the rules for the dialogue.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#__init___1","title":"__init__","text":"
def __init__(\ninitial_performatives: FrozenSet[Message.Performative],\nterminal_performatives: FrozenSet[Message.Performative],\nvalid_replies: Dict[Message.Performative, FrozenSet[Message.Performative]]\n) -> None\n

Initialize a dialogue.

Arguments:

  • initial_performatives: the set of all initial performatives.
  • terminal_performatives: the set of all terminal performatives.
  • valid_replies: the reply structure of speech-acts.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#initial_performatives","title":"initial_performatives","text":"
@property\ndef initial_performatives() -> FrozenSet[Message.Performative]\n

Get the performatives one of which the terminal message in the dialogue must have.

Returns:

the valid performatives of an terminal message

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#terminal_performatives","title":"terminal_performatives","text":"
@property\ndef terminal_performatives() -> FrozenSet[Message.Performative]\n

Get the performatives one of which the terminal message in the dialogue must have.

Returns:

the valid performatives of an terminal message

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#valid_replies","title":"valid_replies","text":"
@property\ndef valid_replies(\n) -> Dict[Message.Performative, FrozenSet[Message.Performative]]\n

Get all the valid performatives which are a valid replies to performatives.

Returns:

the full valid reply structure.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#get_valid_replies","title":"get_valid_replies","text":"
def get_valid_replies(\nperformative: Message.Performative) -> FrozenSet[Message.Performative]\n

Given a performative, return the list of performatives which are its valid replies in a dialogue.

Arguments:

  • performative: the performative in a message

Returns:

list of valid performative replies

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#role-objects","title":"Role Objects","text":"
class Role(Enum)\n

This class defines the agent's role in a dialogue.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#__str___1","title":"__str__","text":"
def __str__() -> str\n

Get the string representation.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#endstate-objects","title":"EndState Objects","text":"
class EndState(Enum)\n

This class defines the end states of a dialogue.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#__str___2","title":"__str__","text":"
def __str__() -> str\n

Get the string representation.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#__init___2","title":"__init__","text":"
def __init__(dialogue_label: DialogueLabel, message_class: Type[Message],\nself_address: Address, role: Role) -> None\n

Initialize a dialogue.

Arguments:

  • dialogue_label: the identifier of the dialogue
  • message_class: the message class used
  • self_address: the address of the entity for whom this dialogue is maintained
  • role: the role of the agent this dialogue is maintained for

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#add_terminal_state_callback","title":"add_terminal_state_callback","text":"
def add_terminal_state_callback(fn: Callable[[\"Dialogue\"], None]) -> None\n

Add callback to be called on dialogue reach terminal state.

Arguments:

  • fn: callable to be called with one argument: Dialogue

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#__eq___1","title":"__eq__","text":"
def __eq__(other: Any) -> bool\n

Compare two dialogues.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#json_1","title":"json","text":"
def json() -> dict\n

Get json representation of the dialogue.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#from_json_1","title":"from_json","text":"
@classmethod\ndef from_json(cls, message_class: Type[Message], data: dict) -> \"Dialogue\"\n

Create a dialogue instance with all messages from json data.

Arguments:

  • message_class: type of message used with this dialogue
  • data: dict with data exported with Dialogue.to_json() method

Returns:

Dialogue instance

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#dialogue_label","title":"dialogue_label","text":"
@property\ndef dialogue_label() -> DialogueLabel\n

Get the dialogue label.

Returns:

The dialogue label

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#incomplete_dialogue_label","title":"incomplete_dialogue_label","text":"
@property\ndef incomplete_dialogue_label() -> DialogueLabel\n

Get the dialogue label.

Returns:

The incomplete dialogue label

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#dialogue_labels","title":"dialogue_labels","text":"
@property\ndef dialogue_labels() -> Set[DialogueLabel]\n

Get the dialogue labels (incomplete and complete, if it exists).

Returns:

the dialogue labels

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#self_address","title":"self_address","text":"
@property\ndef self_address() -> Address\n

Get the address of the entity for whom this dialogues is maintained.

Returns:

the address of this entity

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#role","title":"role","text":"
@property\ndef role() -> \"Role\"\n

Get the agent's role in the dialogue.

Returns:

the agent's role

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#rules","title":"rules","text":"
@property\ndef rules() -> \"Rules\"\n

Get the dialogue rules.

Returns:

the rules

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#message_class","title":"message_class","text":"
@property\ndef message_class() -> Type[Message]\n

Get the message class.

Returns:

the message class

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#is_self_initiated","title":"is_self_initiated","text":"
@property\ndef is_self_initiated() -> bool\n

Check whether the agent initiated the dialogue.

Returns:

True if the agent initiated the dialogue, False otherwise

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#last_incoming_message","title":"last_incoming_message","text":"
@property\ndef last_incoming_message() -> Optional[Message]\n

Get the last incoming message.

Returns:

the last incoming message if it exists, None otherwise

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#last_outgoing_message","title":"last_outgoing_message","text":"
@property\ndef last_outgoing_message() -> Optional[Message]\n

Get the last outgoing message.

Returns:

the last outgoing message if it exists, None otherwise

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#last_message","title":"last_message","text":"
@property\ndef last_message() -> Optional[Message]\n

Get the last message.

Returns:

the last message if it exists, None otherwise

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#is_empty","title":"is_empty","text":"
@property\ndef is_empty() -> bool\n

Check whether the dialogue is empty.

Returns:

True if empty, False otherwise

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#reply","title":"reply","text":"
def reply(performative: Message.Performative,\ntarget_message: Optional[Message] = None,\ntarget: Optional[int] = None,\n**kwargs: Any) -> Message\n

Reply to the 'target_message' in this dialogue with a message with 'performative', and contents from kwargs.

Note if no target_message is provided, the last message in the dialogue will be replied to.

Arguments:

  • target_message: the message to reply to.
  • target: the id of the message to reply to.
  • performative: the performative of the reply message.
  • kwargs: the content of the reply message.

Returns:

the reply message if it was successfully added as a reply, None otherwise.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#get_message_by_id","title":"get_message_by_id","text":"
def get_message_by_id(message_id: int) -> Optional[Message]\n

Get message by id, if not presents return None.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#get_outgoing_next_message_id","title":"get_outgoing_next_message_id","text":"
def get_outgoing_next_message_id() -> int\n

Get next outgoing message id.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#get_incoming_next_message_id","title":"get_incoming_next_message_id","text":"
def get_incoming_next_message_id() -> int\n

Get next incoming message id.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#__str___3","title":"__str__","text":"
def __str__() -> str\n

Get the string representation.

Returns:

The string representation of the dialogue

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#dialoguestats-objects","title":"DialogueStats Objects","text":"
class DialogueStats()\n

Class to handle statistics on default dialogues.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#__init___3","title":"__init__","text":"
def __init__(end_states: FrozenSet[Dialogue.EndState]) -> None\n

Initialize a StatsManager.

Arguments:

  • end_states: the list of dialogue endstates

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#self_initiated","title":"self_initiated","text":"
@property\ndef self_initiated() -> Dict[Dialogue.EndState, int]\n

Get the stats dictionary on self initiated dialogues.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#other_initiated","title":"other_initiated","text":"
@property\ndef other_initiated() -> Dict[Dialogue.EndState, int]\n

Get the stats dictionary on other initiated dialogues.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#add_dialogue_endstate","title":"add_dialogue_endstate","text":"
def add_dialogue_endstate(end_state: Dialogue.EndState,\nis_self_initiated: bool) -> None\n

Add dialogue endstate stats.

Arguments:

  • end_state: the end state of the dialogue
  • is_self_initiated: whether the dialogue is initiated by the agent or the opponent

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#find_caller_object","title":"find_caller_object","text":"
def find_caller_object(object_type: Type) -> Any\n

Find caller object of certain type in the call stack.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#basicdialoguesstorage-objects","title":"BasicDialoguesStorage Objects","text":"
class BasicDialoguesStorage()\n

Dialogues state storage.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#__init___4","title":"__init__","text":"
def __init__(dialogues: \"Dialogues\") -> None\n

Init dialogues storage.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#dialogues_in_terminal_state","title":"dialogues_in_terminal_state","text":"
@property\ndef dialogues_in_terminal_state() -> List[\"Dialogue\"]\n

Get all dialogues in terminal state.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#dialogues_in_active_state","title":"dialogues_in_active_state","text":"
@property\ndef dialogues_in_active_state() -> List[\"Dialogue\"]\n

Get all dialogues in active state.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#is_terminal_dialogues_kept","title":"is_terminal_dialogues_kept","text":"
@property\ndef is_terminal_dialogues_kept() -> bool\n

Return True if dialogues should stay after terminal state.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#dialogue_terminal_state_callback","title":"dialogue_terminal_state_callback","text":"
def dialogue_terminal_state_callback(dialogue: \"Dialogue\") -> None\n

Method to be called on dialogue terminal state reached.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#setup","title":"setup","text":"
def setup() -> None\n

Set up dialogue storage.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#teardown","title":"teardown","text":"
def teardown() -> None\n

Tear down dialogue storage.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#add","title":"add","text":"
def add(dialogue: Dialogue) -> None\n

Add dialogue to storage.

Arguments:

  • dialogue: dialogue to add.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#remove","title":"remove","text":"
def remove(dialogue_label: DialogueLabel) -> None\n

Remove dialogue from storage by it's label.

Arguments:

  • dialogue_label: label of the dialogue to remove

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#get","title":"get","text":"
def get(dialogue_label: DialogueLabel) -> Optional[Dialogue]\n

Get dialogue stored by it's label.

Arguments:

  • dialogue_label: label of the dialogue

Returns:

dialogue if presents or None

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#get_dialogues_with_counterparty","title":"get_dialogues_with_counterparty","text":"
def get_dialogues_with_counterparty(counterparty: Address) -> List[Dialogue]\n

Get the dialogues by address.

Arguments:

  • counterparty: the counterparty

Returns:

The dialogues with the counterparty.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#is_in_incomplete","title":"is_in_incomplete","text":"
def is_in_incomplete(dialogue_label: DialogueLabel) -> bool\n

Check dialogue label presents in list of incomplete.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#set_incomplete_dialogue","title":"set_incomplete_dialogue","text":"
def set_incomplete_dialogue(incomplete_dialogue_label: DialogueLabel,\ncomplete_dialogue_label: DialogueLabel) -> None\n

Set incomplete dialogue label.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#is_dialogue_present","title":"is_dialogue_present","text":"
def is_dialogue_present(dialogue_label: DialogueLabel) -> bool\n

Check dialogue with label specified presents in storage.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#get_latest_label","title":"get_latest_label","text":"
def get_latest_label(dialogue_label: DialogueLabel) -> DialogueLabel\n

Get latest label for dialogue.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#persistdialoguesstorage-objects","title":"PersistDialoguesStorage Objects","text":"
class PersistDialoguesStorage(BasicDialoguesStorage)\n

Persist dialogues storage.

Uses generic storage to load/save dialogues data on setup/teardown.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#__init___5","title":"__init__","text":"
def __init__(dialogues: \"Dialogues\") -> None\n

Init dialogues storage.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#get_skill_component","title":"get_skill_component","text":"
@staticmethod\ndef get_skill_component() -> Optional[SkillComponent]\n

Get skill component dialogues storage constructed for.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#setup_1","title":"setup","text":"
def setup() -> None\n

Set up dialogue storage.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#teardown_1","title":"teardown","text":"
def teardown() -> None\n

Tear down dialogue storage.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#remove_1","title":"remove","text":"
def remove(dialogue_label: DialogueLabel) -> None\n

Remove dialogue from memory and persistent storage.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#persistdialoguesstoragewithoffloading-objects","title":"PersistDialoguesStorageWithOffloading Objects","text":"
class PersistDialoguesStorageWithOffloading(PersistDialoguesStorage)\n

Dialogue Storage with dialogues offloading.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#dialogue_terminal_state_callback_1","title":"dialogue_terminal_state_callback","text":"
def dialogue_terminal_state_callback(dialogue: \"Dialogue\") -> None\n

Call on dialogue reaches terminal state.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#get_1","title":"get","text":"
def get(dialogue_label: DialogueLabel) -> Optional[Dialogue]\n

Try to get dialogue by label from memory or persists storage.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#get_dialogues_with_counterparty_1","title":"get_dialogues_with_counterparty","text":"
def get_dialogues_with_counterparty(counterparty: Address) -> List[Dialogue]\n

Get the dialogues by address.

Arguments:

  • counterparty: the counterparty

Returns:

The dialogues with the counterparty.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#dialogues_in_terminal_state_1","title":"dialogues_in_terminal_state","text":"
@property\ndef dialogues_in_terminal_state() -> List[\"Dialogue\"]\n

Get all dialogues in terminal state.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#dialogues-objects","title":"Dialogues Objects","text":"
class Dialogues()\n

The dialogues class keeps track of all dialogues for an agent.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#__init___6","title":"__init__","text":"
def __init__(self_address: Address,\nend_states: FrozenSet[Dialogue.EndState],\nmessage_class: Type[Message],\ndialogue_class: Type[Dialogue],\nrole_from_first_message: Callable[[Message, Address],\nDialogue.Role],\nkeep_terminal_state_dialogues: Optional[bool] = None) -> None\n

Initialize dialogues.

Arguments:

  • self_address: the address of the entity for whom dialogues are maintained
  • end_states: the list of dialogue endstates
  • message_class: the message class used
  • dialogue_class: the dialogue class used
  • role_from_first_message: the callable determining role from first message
  • keep_terminal_state_dialogues: specify do dialogues in terminal state should stay or not

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#is_keep_dialogues_in_terminal_state","title":"is_keep_dialogues_in_terminal_state","text":"
@property\ndef is_keep_dialogues_in_terminal_state() -> bool\n

Is required to keep dialogues in terminal state.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#self_address_1","title":"self_address","text":"
@property\ndef self_address() -> Address\n

Get the address of the agent for whom dialogues are maintained.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#dialogue_stats","title":"dialogue_stats","text":"
@property\ndef dialogue_stats() -> DialogueStats\n

Get the dialogue statistics.

Returns:

dialogue stats object

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#message_class_1","title":"message_class","text":"
@property\ndef message_class() -> Type[Message]\n

Get the message class.

Returns:

the message class

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#dialogue_class","title":"dialogue_class","text":"
@property\ndef dialogue_class() -> Type[Dialogue]\n

Get the dialogue class.

Returns:

the dialogue class

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#get_dialogues_with_counterparty_2","title":"get_dialogues_with_counterparty","text":"
def get_dialogues_with_counterparty(counterparty: Address) -> List[Dialogue]\n

Get the dialogues by address.

Arguments:

  • counterparty: the counterparty

Returns:

The dialogues with the counterparty.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#new_self_initiated_dialogue_reference","title":"new_self_initiated_dialogue_reference","text":"
@classmethod\ndef new_self_initiated_dialogue_reference(cls) -> Tuple[str, str]\n

Return a dialogue label for a new self initiated dialogue.

Returns:

the next nonce

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#create","title":"create","text":"
def create(counterparty: Address, performative: Message.Performative,\n**kwargs: Any) -> Tuple[Message, Dialogue]\n

Create a dialogue with 'counterparty', with an initial message whose performative is 'performative' and contents are from 'kwargs'.

Arguments:

  • counterparty: the counterparty of the dialogue.
  • performative: the performative of the initial message.
  • kwargs: the content of the initial message.

Returns:

the initial message and the dialogue.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#create_with_message","title":"create_with_message","text":"
def create_with_message(counterparty: Address,\ninitial_message: Message) -> Dialogue\n

Create a dialogue with 'counterparty', with an initial message provided.

Arguments:

  • counterparty: the counterparty of the dialogue.
  • initial_message: the initial_message.

Returns:

the initial message and the dialogue.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#update","title":"update","text":"
def update(message: Message) -> Optional[Dialogue]\n

Update the state of dialogues with a new incoming message.

If the message is for a new dialogue, a new dialogue is created with 'message' as its first message, and returned. If the message is addressed to an existing dialogue, the dialogue is retrieved, extended with this message and returned. If there are any errors, e.g. the message dialogue reference does not exists or the message is invalid w.r.t. the dialogue, return None.

Arguments:

  • message: a new incoming message

Returns:

the new or existing dialogue the message is intended for, or None in case of any errors.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#get_dialogue","title":"get_dialogue","text":"
def get_dialogue(message: Message) -> Optional[Dialogue]\n

Retrieve the dialogue 'message' belongs to.

Arguments:

  • message: a message

Returns:

the dialogue, or None in case such a dialogue does not exist

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#get_dialogue_from_label","title":"get_dialogue_from_label","text":"
def get_dialogue_from_label(\ndialogue_label: DialogueLabel) -> Optional[Dialogue]\n

Retrieve a dialogue based on its label.

Arguments:

  • dialogue_label: the dialogue label

Returns:

the dialogue if present

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#setup_2","title":"setup","text":"
def setup() -> None\n

Set up.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#teardown_2","title":"teardown","text":"
def teardown() -> None\n

Tear down.

"},{"location":"aea-framework-documentation/api/protocols/generator/common/","title":"Common","text":""},{"location":"aea-framework-documentation/api/protocols/generator/common/#aeaprotocolsgeneratorcommon","title":"aea.protocols.generator.common","text":"

This module contains utility code for generator modules.

"},{"location":"aea-framework-documentation/api/protocols/generator/common/#is_installed","title":"is_installed","text":"
def is_installed(programme: str) -> bool\n

Check whether a programme is installed on the system.

Arguments:

  • programme: the name of the programme.

Returns:

True if installed, False otherwise

"},{"location":"aea-framework-documentation/api/protocols/generator/common/#base_protolint_command","title":"base_protolint_command","text":"
def base_protolint_command() -> str\n

Return the base protolint command.

Returns:

The base protolint command

"},{"location":"aea-framework-documentation/api/protocols/generator/common/#check_prerequisites","title":"check_prerequisites","text":"
def check_prerequisites() -> None\n

Check whether a programme is installed on the system.

"},{"location":"aea-framework-documentation/api/protocols/generator/common/#get_protoc_version","title":"get_protoc_version","text":"
def get_protoc_version() -> str\n

Get the protoc version used.

"},{"location":"aea-framework-documentation/api/protocols/generator/common/#load_protocol_specification","title":"load_protocol_specification","text":"
def load_protocol_specification(\nspecification_path: str) -> ProtocolSpecification\n

Load a protocol specification.

Arguments:

  • specification_path: path to the protocol specification yaml file.

Returns:

A ProtocolSpecification object

"},{"location":"aea-framework-documentation/api/protocols/generator/common/#try_run_black_formatting","title":"try_run_black_formatting","text":"
def try_run_black_formatting(path_to_protocol_package: str) -> None\n

Run Black code formatting via subprocess.

Arguments:

  • path_to_protocol_package: a path where formatting should be applied.

"},{"location":"aea-framework-documentation/api/protocols/generator/common/#try_run_isort_formatting","title":"try_run_isort_formatting","text":"
def try_run_isort_formatting(path_to_protocol_package: str) -> None\n

Run Isort code formatting via subprocess.

Arguments:

  • path_to_protocol_package: a path where formatting should be applied.

"},{"location":"aea-framework-documentation/api/protocols/generator/common/#try_run_protoc","title":"try_run_protoc","text":"
def try_run_protoc(path_to_generated_protocol_package: str,\nname: str,\nlanguage: str = PROTOCOL_LANGUAGE_PYTHON) -> None\n

Run 'protoc' protocol buffer compiler via subprocess.

Arguments:

  • path_to_generated_protocol_package: path to the protocol buffer schema file.
  • name: name of the protocol buffer schema file.
  • language: the target language in which to compile the protobuf schema file

"},{"location":"aea-framework-documentation/api/protocols/generator/common/#try_run_protolint","title":"try_run_protolint","text":"
def try_run_protolint(path_to_generated_protocol_package: str,\nname: str) -> None\n

Run 'protolint' linter via subprocess.

Arguments:

  • path_to_generated_protocol_package: path to the protocol buffer schema file.
  • name: name of the protocol buffer schema file.

"},{"location":"aea-framework-documentation/api/protocols/generator/common/#check_protobuf_using_protoc","title":"check_protobuf_using_protoc","text":"
def check_protobuf_using_protoc(path_to_generated_protocol_package: str,\nname: str) -> Tuple[bool, str]\n

Check whether a protocol buffer schema file is valid.

Validation is via trying to compile the schema file. If successfully compiled it is valid, otherwise invalid. If valid, return True and a 'protobuf file is valid' message, otherwise return False and the error thrown by the compiler.

Arguments:

  • path_to_generated_protocol_package: path to the protocol buffer schema file.
  • name: name of the protocol buffer schema file.

Returns:

Boolean result and an accompanying message

"},{"location":"aea-framework-documentation/api/protocols/generator/common/#compile_protobuf_using_protoc","title":"compile_protobuf_using_protoc","text":"
def compile_protobuf_using_protoc(path_to_generated_protocol_package: str,\nname: str,\nlanguage: str) -> Tuple[bool, str]\n

Compile a protocol buffer schema file using protoc.

If successfully compiled, return True and a success message, otherwise return False and the error thrown by the compiler.

Arguments:

  • path_to_generated_protocol_package: path to the protocol buffer schema file.
  • name: name of the protocol buffer schema file.
  • language: the target language in which to compile the protobuf schema file

Returns:

Boolean result and an accompanying message

"},{"location":"aea-framework-documentation/api/protocols/generator/common/#apply_protolint","title":"apply_protolint","text":"
def apply_protolint(path_to_proto_file: str, name: str) -> Tuple[bool, str]\n

Apply protolint linter to a protocol buffer schema file.

If no output, return True and a success message, otherwise return False and the output shown by the linter (minus the indentation suggestions which are automatically fixed by protolint).

Arguments:

  • path_to_proto_file: path to the protocol buffer schema file.
  • name: name of the protocol buffer schema file.

Returns:

Boolean result and an accompanying message

"},{"location":"aea-framework-documentation/api/protocols/generator/extract_specification/","title":"Extract Specification","text":""},{"location":"aea-framework-documentation/api/protocols/generator/extract_specification/#aeaprotocolsgeneratorextract_specification","title":"aea.protocols.generator.extract_specification","text":"

This module extracts a valid protocol specification into pythonic objects.

"},{"location":"aea-framework-documentation/api/protocols/generator/extract_specification/#pythonicprotocolspecification-objects","title":"PythonicProtocolSpecification Objects","text":"
class PythonicProtocolSpecification()\n

This class represents a protocol specification in python.

"},{"location":"aea-framework-documentation/api/protocols/generator/extract_specification/#__init__","title":"__init__","text":"
def __init__() -> None\n

Instantiate a Pythonic protocol specification.

"},{"location":"aea-framework-documentation/api/protocols/generator/extract_specification/#extract","title":"extract","text":"
def extract(\nprotocol_specification: ProtocolSpecification\n) -> PythonicProtocolSpecification\n

Converts a protocol specification into a Pythonic protocol specification.

Arguments:

  • protocol_specification: a protocol specification

Returns:

a Pythonic protocol specification

"},{"location":"aea-framework-documentation/api/protocols/generator/validate/","title":"Validate","text":""},{"location":"aea-framework-documentation/api/protocols/generator/validate/#aeaprotocolsgeneratorvalidate","title":"aea.protocols.generator.validate","text":"

This module validates a protocol specification.

"},{"location":"aea-framework-documentation/api/protocols/generator/validate/#validate","title":"validate","text":"
def validate(\nprotocol_specification: ProtocolSpecification) -> Tuple[bool, str]\n

Evaluate whether a protocol specification is valid.

Arguments:

  • protocol_specification: a protocol specification.

Returns:

Boolean result, and associated message.

"},{"location":"aea-framework-documentation/api/protocols/signing/custom_types/","title":"Custom Types","text":""},{"location":"aea-framework-documentation/api/protocols/signing/custom_types/#packagesfetchaiprotocolssigningcustom_types","title":"packages.fetchai.protocols.signing.custom_types","text":"

This module contains class representations corresponding to every custom type in the protocol specification.

"},{"location":"aea-framework-documentation/api/protocols/signing/custom_types/#errorcode-objects","title":"ErrorCode Objects","text":"
class ErrorCode(Enum)\n

This class represents an instance of ErrorCode.

"},{"location":"aea-framework-documentation/api/protocols/signing/custom_types/#encode","title":"encode","text":"
@staticmethod\ndef encode(error_code_protobuf_object: Any,\nerror_code_object: \"ErrorCode\") -> None\n

Encode an instance of this class into the protocol buffer object.

The protocol buffer object in the error_code_protobuf_object argument is matched with the instance of this class in the 'error_code_object' argument.

Arguments:

  • error_code_protobuf_object: the protocol buffer object whose type corresponds with this class.
  • error_code_object: an instance of this class to be encoded in the protocol buffer object.

"},{"location":"aea-framework-documentation/api/protocols/signing/custom_types/#decode","title":"decode","text":"
@classmethod\ndef decode(cls, error_code_protobuf_object: Any) -> \"ErrorCode\"\n

Decode a protocol buffer object that corresponds with this class into an instance of this class.

A new instance of this class is created that matches the protocol buffer object in the 'error_code_protobuf_object' argument.

Arguments:

  • error_code_protobuf_object: the protocol buffer object whose type corresponds with this class.

Returns:

A new instance of this class that matches the protocol buffer object in the 'error_code_protobuf_object' argument.

"},{"location":"aea-framework-documentation/api/protocols/signing/dialogues/","title":"Dialogues","text":""},{"location":"aea-framework-documentation/api/protocols/signing/dialogues/#packagesfetchaiprotocolssigningdialogues","title":"packages.fetchai.protocols.signing.dialogues","text":"

This module contains the classes required for signing dialogue management.

  • SigningDialogue: The dialogue class maintains state of a dialogue and manages it.
  • SigningDialogues: The dialogues class keeps track of all dialogues.

"},{"location":"aea-framework-documentation/api/protocols/signing/dialogues/#signingdialogue-objects","title":"SigningDialogue Objects","text":"
class SigningDialogue(Dialogue)\n

The signing dialogue class maintains state of a dialogue and manages it.

"},{"location":"aea-framework-documentation/api/protocols/signing/dialogues/#role-objects","title":"Role Objects","text":"
class Role(Dialogue.Role)\n

This class defines the agent's role in a signing dialogue.

"},{"location":"aea-framework-documentation/api/protocols/signing/dialogues/#endstate-objects","title":"EndState Objects","text":"
class EndState(Dialogue.EndState)\n

This class defines the end states of a signing dialogue.

"},{"location":"aea-framework-documentation/api/protocols/signing/dialogues/#__init__","title":"__init__","text":"
def __init__(dialogue_label: DialogueLabel,\nself_address: Address,\nrole: Dialogue.Role,\nmessage_class: Type[SigningMessage] = SigningMessage) -> None\n

Initialize a dialogue.

Arguments:

  • dialogue_label: the identifier of the dialogue
  • self_address: the address of the entity for whom this dialogue is maintained
  • role: the role of the agent this dialogue is maintained for
  • message_class: the message class used

"},{"location":"aea-framework-documentation/api/protocols/signing/dialogues/#signingdialogues-objects","title":"SigningDialogues Objects","text":"
class SigningDialogues(Dialogues, ABC)\n

This class keeps track of all signing dialogues.

"},{"location":"aea-framework-documentation/api/protocols/signing/dialogues/#__init___1","title":"__init__","text":"
def __init__(self_address: Address,\nrole_from_first_message: Callable[[Message, Address],\nDialogue.Role],\ndialogue_class: Type[SigningDialogue] = SigningDialogue) -> None\n

Initialize dialogues.

Arguments:

  • self_address: the address of the entity for whom dialogues are maintained
  • dialogue_class: the dialogue class used
  • role_from_first_message: the callable determining role from first message
"},{"location":"aea-framework-documentation/api/protocols/signing/message/","title":"Message","text":""},{"location":"aea-framework-documentation/api/protocols/signing/message/#packagesfetchaiprotocolssigningmessage","title":"packages.fetchai.protocols.signing.message","text":"

This module contains signing's message definition.

"},{"location":"aea-framework-documentation/api/protocols/signing/message/#signingmessage-objects","title":"SigningMessage Objects","text":"
class SigningMessage(Message)\n

A protocol for communication between skills and decision maker.

"},{"location":"aea-framework-documentation/api/protocols/signing/message/#performative-objects","title":"Performative Objects","text":"
class Performative(Message.Performative)\n

Performatives for the signing protocol.

"},{"location":"aea-framework-documentation/api/protocols/signing/message/#__str__","title":"__str__","text":"
def __str__() -> str\n

Get the string representation.

"},{"location":"aea-framework-documentation/api/protocols/signing/message/#__init__","title":"__init__","text":"
def __init__(performative: Performative,\ndialogue_reference: Tuple[str, str] = (\"\", \"\"),\nmessage_id: int = 1,\ntarget: int = 0,\n**kwargs: Any)\n

Initialise an instance of SigningMessage.

Arguments:

  • message_id: the message id.
  • dialogue_reference: the dialogue reference.
  • target: the message target.
  • performative: the message performative.
  • **kwargs: extra options.

"},{"location":"aea-framework-documentation/api/protocols/signing/message/#valid_performatives","title":"valid_performatives","text":"
@property\ndef valid_performatives() -> Set[str]\n

Get valid performatives.

"},{"location":"aea-framework-documentation/api/protocols/signing/message/#dialogue_reference","title":"dialogue_reference","text":"
@property\ndef dialogue_reference() -> Tuple[str, str]\n

Get the dialogue_reference of the message.

"},{"location":"aea-framework-documentation/api/protocols/signing/message/#message_id","title":"message_id","text":"
@property\ndef message_id() -> int\n

Get the message_id of the message.

"},{"location":"aea-framework-documentation/api/protocols/signing/message/#performative","title":"performative","text":"
@property\ndef performative() -> Performative\n

Get the performative of the message.

"},{"location":"aea-framework-documentation/api/protocols/signing/message/#target","title":"target","text":"
@property\ndef target() -> int\n

Get the target of the message.

"},{"location":"aea-framework-documentation/api/protocols/signing/message/#error_code","title":"error_code","text":"
@property\ndef error_code() -> CustomErrorCode\n

Get the 'error_code' content from the message.

"},{"location":"aea-framework-documentation/api/protocols/signing/message/#raw_message","title":"raw_message","text":"
@property\ndef raw_message() -> CustomRawMessage\n

Get the 'raw_message' content from the message.

"},{"location":"aea-framework-documentation/api/protocols/signing/message/#raw_transaction","title":"raw_transaction","text":"
@property\ndef raw_transaction() -> CustomRawTransaction\n

Get the 'raw_transaction' content from the message.

"},{"location":"aea-framework-documentation/api/protocols/signing/message/#signed_message","title":"signed_message","text":"
@property\ndef signed_message() -> CustomSignedMessage\n

Get the 'signed_message' content from the message.

"},{"location":"aea-framework-documentation/api/protocols/signing/message/#signed_transaction","title":"signed_transaction","text":"
@property\ndef signed_transaction() -> CustomSignedTransaction\n

Get the 'signed_transaction' content from the message.

"},{"location":"aea-framework-documentation/api/protocols/signing/message/#terms","title":"terms","text":"
@property\ndef terms() -> CustomTerms\n

Get the 'terms' content from the message.

"},{"location":"aea-framework-documentation/api/protocols/signing/serialization/","title":"Serialization","text":""},{"location":"aea-framework-documentation/api/protocols/signing/serialization/#packagesfetchaiprotocolssigningserialization","title":"packages.fetchai.protocols.signing.serialization","text":"

Serialization module for signing protocol.

"},{"location":"aea-framework-documentation/api/protocols/signing/serialization/#signingserializer-objects","title":"SigningSerializer Objects","text":"
class SigningSerializer(Serializer)\n

Serialization for the 'signing' protocol.

"},{"location":"aea-framework-documentation/api/protocols/signing/serialization/#encode","title":"encode","text":"
@staticmethod\ndef encode(msg: Message) -> bytes\n

Encode a 'Signing' message into bytes.

Arguments:

  • msg: the message object.

Returns:

the bytes.

"},{"location":"aea-framework-documentation/api/protocols/signing/serialization/#decode","title":"decode","text":"
@staticmethod\ndef decode(obj: bytes) -> Message\n

Decode bytes into a 'Signing' message.

Arguments:

  • obj: the bytes object.

Returns:

the 'Signing' message.

"},{"location":"aea-framework-documentation/api/protocols/state_update/dialogues/","title":"Dialogues","text":""},{"location":"aea-framework-documentation/api/protocols/state_update/dialogues/#packagesfetchaiprotocolsstate_updatedialogues","title":"packages.fetchai.protocols.state_update.dialogues","text":"

This module contains the classes required for state_update dialogue management.

  • StateUpdateDialogue: The dialogue class maintains state of a dialogue and manages it.
  • StateUpdateDialogues: The dialogues class keeps track of all dialogues.

"},{"location":"aea-framework-documentation/api/protocols/state_update/dialogues/#stateupdatedialogue-objects","title":"StateUpdateDialogue Objects","text":"
class StateUpdateDialogue(Dialogue)\n

The state_update dialogue class maintains state of a dialogue and manages it.

"},{"location":"aea-framework-documentation/api/protocols/state_update/dialogues/#role-objects","title":"Role Objects","text":"
class Role(Dialogue.Role)\n

This class defines the agent's role in a state_update dialogue.

"},{"location":"aea-framework-documentation/api/protocols/state_update/dialogues/#endstate-objects","title":"EndState Objects","text":"
class EndState(Dialogue.EndState)\n

This class defines the end states of a state_update dialogue.

"},{"location":"aea-framework-documentation/api/protocols/state_update/dialogues/#__init__","title":"__init__","text":"
def __init__(\ndialogue_label: DialogueLabel,\nself_address: Address,\nrole: Dialogue.Role,\nmessage_class: Type[StateUpdateMessage] = StateUpdateMessage) -> None\n

Initialize a dialogue.

Arguments:

  • dialogue_label: the identifier of the dialogue
  • self_address: the address of the entity for whom this dialogue is maintained
  • role: the role of the agent this dialogue is maintained for
  • message_class: the message class used

"},{"location":"aea-framework-documentation/api/protocols/state_update/dialogues/#stateupdatedialogues-objects","title":"StateUpdateDialogues Objects","text":"
class StateUpdateDialogues(Dialogues, ABC)\n

This class keeps track of all state_update dialogues.

"},{"location":"aea-framework-documentation/api/protocols/state_update/dialogues/#__init___1","title":"__init__","text":"
def __init__(\nself_address: Address,\nrole_from_first_message: Callable[[Message, Address], Dialogue.Role],\ndialogue_class: Type[StateUpdateDialogue] = StateUpdateDialogue\n) -> None\n

Initialize dialogues.

Arguments:

  • self_address: the address of the entity for whom dialogues are maintained
  • dialogue_class: the dialogue class used
  • role_from_first_message: the callable determining role from first message
"},{"location":"aea-framework-documentation/api/protocols/state_update/message/","title":"Message","text":""},{"location":"aea-framework-documentation/api/protocols/state_update/message/#packagesfetchaiprotocolsstate_updatemessage","title":"packages.fetchai.protocols.state_update.message","text":"

This module contains state_update's message definition.

"},{"location":"aea-framework-documentation/api/protocols/state_update/message/#stateupdatemessage-objects","title":"StateUpdateMessage Objects","text":"
class StateUpdateMessage(Message)\n

A protocol for state updates to the decision maker state.

"},{"location":"aea-framework-documentation/api/protocols/state_update/message/#performative-objects","title":"Performative Objects","text":"
class Performative(Message.Performative)\n

Performatives for the state_update protocol.

"},{"location":"aea-framework-documentation/api/protocols/state_update/message/#__str__","title":"__str__","text":"
def __str__() -> str\n

Get the string representation.

"},{"location":"aea-framework-documentation/api/protocols/state_update/message/#__init__","title":"__init__","text":"
def __init__(performative: Performative,\ndialogue_reference: Tuple[str, str] = (\"\", \"\"),\nmessage_id: int = 1,\ntarget: int = 0,\n**kwargs: Any)\n

Initialise an instance of StateUpdateMessage.

Arguments:

  • message_id: the message id.
  • dialogue_reference: the dialogue reference.
  • target: the message target.
  • performative: the message performative.
  • **kwargs: extra options.

"},{"location":"aea-framework-documentation/api/protocols/state_update/message/#valid_performatives","title":"valid_performatives","text":"
@property\ndef valid_performatives() -> Set[str]\n

Get valid performatives.

"},{"location":"aea-framework-documentation/api/protocols/state_update/message/#dialogue_reference","title":"dialogue_reference","text":"
@property\ndef dialogue_reference() -> Tuple[str, str]\n

Get the dialogue_reference of the message.

"},{"location":"aea-framework-documentation/api/protocols/state_update/message/#message_id","title":"message_id","text":"
@property\ndef message_id() -> int\n

Get the message_id of the message.

"},{"location":"aea-framework-documentation/api/protocols/state_update/message/#performative","title":"performative","text":"
@property\ndef performative() -> Performative\n

Get the performative of the message.

"},{"location":"aea-framework-documentation/api/protocols/state_update/message/#target","title":"target","text":"
@property\ndef target() -> int\n

Get the target of the message.

"},{"location":"aea-framework-documentation/api/protocols/state_update/message/#amount_by_currency_id","title":"amount_by_currency_id","text":"
@property\ndef amount_by_currency_id() -> Dict[str, int]\n

Get the 'amount_by_currency_id' content from the message.

"},{"location":"aea-framework-documentation/api/protocols/state_update/message/#exchange_params_by_currency_id","title":"exchange_params_by_currency_id","text":"
@property\ndef exchange_params_by_currency_id() -> Dict[str, float]\n

Get the 'exchange_params_by_currency_id' content from the message.

"},{"location":"aea-framework-documentation/api/protocols/state_update/message/#quantities_by_good_id","title":"quantities_by_good_id","text":"
@property\ndef quantities_by_good_id() -> Dict[str, int]\n

Get the 'quantities_by_good_id' content from the message.

"},{"location":"aea-framework-documentation/api/protocols/state_update/message/#utility_params_by_good_id","title":"utility_params_by_good_id","text":"
@property\ndef utility_params_by_good_id() -> Dict[str, float]\n

Get the 'utility_params_by_good_id' content from the message.

"},{"location":"aea-framework-documentation/api/protocols/state_update/serialization/","title":"Serialization","text":""},{"location":"aea-framework-documentation/api/protocols/state_update/serialization/#packagesfetchaiprotocolsstate_updateserialization","title":"packages.fetchai.protocols.state_update.serialization","text":"

Serialization module for state_update protocol.

"},{"location":"aea-framework-documentation/api/protocols/state_update/serialization/#stateupdateserializer-objects","title":"StateUpdateSerializer Objects","text":"
class StateUpdateSerializer(Serializer)\n

Serialization for the 'state_update' protocol.

"},{"location":"aea-framework-documentation/api/protocols/state_update/serialization/#encode","title":"encode","text":"
@staticmethod\ndef encode(msg: Message) -> bytes\n

Encode a 'StateUpdate' message into bytes.

Arguments:

  • msg: the message object.

Returns:

the bytes.

"},{"location":"aea-framework-documentation/api/protocols/state_update/serialization/#decode","title":"decode","text":"
@staticmethod\ndef decode(obj: bytes) -> Message\n

Decode bytes into a 'StateUpdate' message.

Arguments:

  • obj: the bytes object.

Returns:

the 'StateUpdate' message.

"},{"location":"aea-framework-documentation/api/registries/base/","title":"Base","text":""},{"location":"aea-framework-documentation/api/registries/base/#aearegistriesbase","title":"aea.registries.base","text":"

This module contains registries.

"},{"location":"aea-framework-documentation/api/registries/base/#registry-objects","title":"Registry Objects","text":"
class Registry(Generic[ItemId, Item], WithLogger, ABC)\n

This class implements an abstract registry.

"},{"location":"aea-framework-documentation/api/registries/base/#__init__","title":"__init__","text":"
def __init__(agent_name: str = \"standalone\") -> None\n

Initialize the registry.

Arguments:

  • agent_name: the name of the agent

"},{"location":"aea-framework-documentation/api/registries/base/#register","title":"register","text":"
@abstractmethod\ndef register(item_id: ItemId,\nitem: Item,\nis_dynamically_added: bool = False) -> None\n

Register an item.

Arguments:

  • item_id: the public id of the item.
  • item: the item.
  • is_dynamically_added: whether or not the item is dynamically added.

Raises:

  • None: ValueError if an item is already registered with that item id.

Returns:

None

"},{"location":"aea-framework-documentation/api/registries/base/#unregister","title":"unregister","text":"
@abstractmethod\ndef unregister(item_id: ItemId) -> Optional[Item]\n

Unregister an item.

Arguments:

  • item_id: the public id of the item.

Raises:

  • None: ValueError if no item registered with that item id.

Returns:

the item

"},{"location":"aea-framework-documentation/api/registries/base/#fetch","title":"fetch","text":"
@abstractmethod\ndef fetch(item_id: ItemId) -> Optional[Item]\n

Fetch an item.

Arguments:

  • item_id: the public id of the item.

Returns:

the Item

"},{"location":"aea-framework-documentation/api/registries/base/#fetch_all","title":"fetch_all","text":"
@abstractmethod\ndef fetch_all() -> List[Item]\n

Fetch all the items.

Returns:

the list of items.

"},{"location":"aea-framework-documentation/api/registries/base/#ids","title":"ids","text":"
@abstractmethod\ndef ids() -> Set[ItemId]\n

Return the set of all the used item ids.

Returns:

the set of item ids.

"},{"location":"aea-framework-documentation/api/registries/base/#setup","title":"setup","text":"
@abstractmethod\ndef setup() -> None\n

Set up registry.

Returns:

None

"},{"location":"aea-framework-documentation/api/registries/base/#teardown","title":"teardown","text":"
@abstractmethod\ndef teardown() -> None\n

Teardown the registry.

Returns:

None

"},{"location":"aea-framework-documentation/api/registries/base/#publicidregistry-objects","title":"PublicIdRegistry Objects","text":"
class PublicIdRegistry(Generic[Item], Registry[PublicId, Item])\n

This class implement a registry whose keys are public ids.

In particular, it is able to handle the case when the public id points to the 'latest' version of a package.

"},{"location":"aea-framework-documentation/api/registries/base/#__init___1","title":"__init__","text":"
def __init__() -> None\n

Initialize the registry.

"},{"location":"aea-framework-documentation/api/registries/base/#register_1","title":"register","text":"
def register(public_id: PublicId,\nitem: Item,\nis_dynamically_added: bool = False) -> None\n

Register an item.

"},{"location":"aea-framework-documentation/api/registries/base/#unregister_1","title":"unregister","text":"
def unregister(public_id: PublicId) -> Item\n

Unregister an item.

"},{"location":"aea-framework-documentation/api/registries/base/#fetch_1","title":"fetch","text":"
def fetch(public_id: PublicId) -> Optional[Item]\n

Fetch an item associated with a public id.

Arguments:

  • public_id: the public id.

Returns:

an item, or None if the key is not present.

"},{"location":"aea-framework-documentation/api/registries/base/#fetch_all_1","title":"fetch_all","text":"
def fetch_all() -> List[Item]\n

Fetch all the items.

"},{"location":"aea-framework-documentation/api/registries/base/#ids_1","title":"ids","text":"
def ids() -> Set[PublicId]\n

Get all the item ids.

"},{"location":"aea-framework-documentation/api/registries/base/#setup_1","title":"setup","text":"
def setup() -> None\n

Set up the items.

"},{"location":"aea-framework-documentation/api/registries/base/#teardown_1","title":"teardown","text":"
def teardown() -> None\n

Tear down the items.

"},{"location":"aea-framework-documentation/api/registries/base/#agentcomponentregistry-objects","title":"AgentComponentRegistry Objects","text":"
class AgentComponentRegistry(Registry[ComponentId, Component])\n

This class implements a simple dictionary-based registry for agent components.

"},{"location":"aea-framework-documentation/api/registries/base/#__init___2","title":"__init__","text":"
def __init__(**kwargs: Any) -> None\n

Instantiate the registry.

Arguments:

  • kwargs: kwargs

"},{"location":"aea-framework-documentation/api/registries/base/#register_2","title":"register","text":"
def register(component_id: ComponentId,\ncomponent: Component,\nis_dynamically_added: bool = False) -> None\n

Register a component.

Arguments:

  • component_id: the id of the component.
  • component: the component object.
  • is_dynamically_added: whether or not the item is dynamically added.

"},{"location":"aea-framework-documentation/api/registries/base/#unregister_2","title":"unregister","text":"
def unregister(component_id: ComponentId) -> Optional[Component]\n

Unregister a component.

Arguments:

  • component_id: the ComponentId

Returns:

the item

"},{"location":"aea-framework-documentation/api/registries/base/#fetch_2","title":"fetch","text":"
def fetch(component_id: ComponentId) -> Optional[Component]\n

Fetch the component by id.

Arguments:

  • component_id: the contract id

Returns:

the component or None if the component is not registered

"},{"location":"aea-framework-documentation/api/registries/base/#fetch_all_2","title":"fetch_all","text":"
def fetch_all() -> List[Component]\n

Fetch all the components.

Returns:

the list of registered components.

"},{"location":"aea-framework-documentation/api/registries/base/#fetch_by_type","title":"fetch_by_type","text":"
def fetch_by_type(component_type: ComponentType) -> List[Component]\n

Fetch all the components by a given type..

Arguments:

  • component_type: a component type

Returns:

the list of registered components of a given type.

"},{"location":"aea-framework-documentation/api/registries/base/#ids_2","title":"ids","text":"
def ids() -> Set[ComponentId]\n

Get the item ids.

"},{"location":"aea-framework-documentation/api/registries/base/#setup_2","title":"setup","text":"
def setup() -> None\n

Set up the registry.

"},{"location":"aea-framework-documentation/api/registries/base/#teardown_2","title":"teardown","text":"
def teardown() -> None\n

Teardown the registry.

"},{"location":"aea-framework-documentation/api/registries/base/#componentregistry-objects","title":"ComponentRegistry Objects","text":"
class ComponentRegistry(Registry[Tuple[PublicId, str], SkillComponentType],\nGeneric[SkillComponentType])\n

This class implements a generic registry for skill components.

"},{"location":"aea-framework-documentation/api/registries/base/#__init___3","title":"__init__","text":"
def __init__(**kwargs: Any) -> None\n

Instantiate the registry.

Arguments:

  • kwargs: kwargs

"},{"location":"aea-framework-documentation/api/registries/base/#register_3","title":"register","text":"
def register(item_id: Tuple[PublicId, str],\nitem: SkillComponentType,\nis_dynamically_added: bool = False) -> None\n

Register a item.

Arguments:

  • item_id: a pair (skill id, item name).
  • item: the item to register.
  • is_dynamically_added: whether or not the item is dynamically added.

Raises:

  • None: ValueError if an item is already registered with that item id.

"},{"location":"aea-framework-documentation/api/registries/base/#unregister_3","title":"unregister","text":"
def unregister(item_id: Tuple[PublicId, str]) -> Optional[SkillComponentType]\n

Unregister a item.

Arguments:

  • item_id: a pair (skill id, item name).

Raises:

  • None: ValueError if no item registered with that item id.

Returns:

skill component

"},{"location":"aea-framework-documentation/api/registries/base/#fetch_3","title":"fetch","text":"
def fetch(item_id: Tuple[PublicId, str]) -> Optional[SkillComponentType]\n

Fetch an item.

Arguments:

  • item_id: the public id of the item.

Returns:

the Item

"},{"location":"aea-framework-documentation/api/registries/base/#fetch_by_skill","title":"fetch_by_skill","text":"
def fetch_by_skill(skill_id: PublicId) -> List[SkillComponentType]\n

Fetch all the items of a given skill.

"},{"location":"aea-framework-documentation/api/registries/base/#fetch_all_3","title":"fetch_all","text":"
def fetch_all() -> List[SkillComponentType]\n

Fetch all the items.

"},{"location":"aea-framework-documentation/api/registries/base/#unregister_by_skill","title":"unregister_by_skill","text":"
def unregister_by_skill(skill_id: PublicId) -> None\n

Unregister all the components by skill.

"},{"location":"aea-framework-documentation/api/registries/base/#ids_3","title":"ids","text":"
def ids() -> Set[Tuple[PublicId, str]]\n

Get the item ids.

"},{"location":"aea-framework-documentation/api/registries/base/#setup_3","title":"setup","text":"
def setup() -> None\n

Set up the items in the registry.

"},{"location":"aea-framework-documentation/api/registries/base/#teardown_3","title":"teardown","text":"
def teardown() -> None\n

Teardown the registry.

"},{"location":"aea-framework-documentation/api/registries/base/#handlerregistry-objects","title":"HandlerRegistry Objects","text":"
class HandlerRegistry(ComponentRegistry[Handler])\n

This class implements the handlers registry.

"},{"location":"aea-framework-documentation/api/registries/base/#__init___4","title":"__init__","text":"
def __init__(**kwargs: Any) -> None\n

Instantiate the registry.

Arguments:

  • kwargs: kwargs

"},{"location":"aea-framework-documentation/api/registries/base/#register_4","title":"register","text":"
def register(item_id: Tuple[PublicId, str],\nitem: Handler,\nis_dynamically_added: bool = False) -> None\n

Register a handler.

Arguments:

  • item_id: the item id.
  • item: the handler.
  • is_dynamically_added: whether or not the item is dynamically added.

Raises:

  • ValueError: if the protocol is None, or an item with pair (skill_id, protocol_id_ already exists.

"},{"location":"aea-framework-documentation/api/registries/base/#unregister_4","title":"unregister","text":"
def unregister(item_id: Tuple[PublicId, str]) -> Handler\n

Unregister a item.

Arguments:

  • item_id: a pair (skill id, item name).

Raises:

  • None: ValueError if no item is registered with that item id.

Returns:

the unregistered handler

"},{"location":"aea-framework-documentation/api/registries/base/#unregister_by_skill_1","title":"unregister_by_skill","text":"
def unregister_by_skill(skill_id: PublicId) -> None\n

Unregister all the components by skill.

"},{"location":"aea-framework-documentation/api/registries/base/#fetch_by_protocol","title":"fetch_by_protocol","text":"
def fetch_by_protocol(protocol_id: PublicId) -> List[Handler]\n

Fetch the handler by the pair protocol id and skill id.

Arguments:

  • protocol_id: the protocol id

Returns:

the handlers registered for the protocol_id and skill_id

"},{"location":"aea-framework-documentation/api/registries/base/#fetch_by_protocol_and_skill","title":"fetch_by_protocol_and_skill","text":"
def fetch_by_protocol_and_skill(protocol_id: PublicId,\nskill_id: PublicId) -> Optional[Handler]\n

Fetch the handler by the pair protocol id and skill id.

Arguments:

  • protocol_id: the protocol id
  • skill_id: the skill id.

Returns:

the handlers registered for the protocol_id and skill_id

"},{"location":"aea-framework-documentation/api/registries/filter/","title":"Filter","text":""},{"location":"aea-framework-documentation/api/registries/filter/#aearegistriesfilter","title":"aea.registries.filter","text":"

This module contains registries.

"},{"location":"aea-framework-documentation/api/registries/filter/#filter-objects","title":"Filter Objects","text":"
class Filter(WithLogger)\n

This class implements the filter of an AEA.

"},{"location":"aea-framework-documentation/api/registries/filter/#__init__","title":"__init__","text":"
def __init__(resources: Resources,\ndecision_maker_out_queue: AsyncFriendlyQueue) -> None\n

Instantiate the filter.

Arguments:

  • resources: the resources
  • decision_maker_out_queue: the decision maker queue

"},{"location":"aea-framework-documentation/api/registries/filter/#resources","title":"resources","text":"
@property\ndef resources() -> Resources\n

Get resources.

"},{"location":"aea-framework-documentation/api/registries/filter/#decision_maker_out_queue","title":"decision_maker_out_queue","text":"
@property\ndef decision_maker_out_queue() -> AsyncFriendlyQueue\n

Get decision maker (out) queue.

"},{"location":"aea-framework-documentation/api/registries/filter/#get_active_handlers","title":"get_active_handlers","text":"
def get_active_handlers(protocol_id: PublicId,\nskill_id: Optional[PublicId] = None) -> List[Handler]\n

Get active handlers based on protocol id and optional skill id.

Arguments:

  • protocol_id: the protocol id
  • skill_id: the skill id

Returns:

the list of handlers currently active

"},{"location":"aea-framework-documentation/api/registries/filter/#get_active_behaviours","title":"get_active_behaviours","text":"
def get_active_behaviours() -> List[Behaviour]\n

Get the active behaviours.

Returns:

the list of behaviours currently active

"},{"location":"aea-framework-documentation/api/registries/filter/#handle_new_handlers_and_behaviours","title":"handle_new_handlers_and_behaviours","text":"
def handle_new_handlers_and_behaviours() -> None\n

Handle the messages from the decision maker.

"},{"location":"aea-framework-documentation/api/registries/filter/#get_internal_message","title":"get_internal_message","text":"
async def get_internal_message() -> Optional[Message]\n

Get a message from decision_maker_out_queue.

"},{"location":"aea-framework-documentation/api/registries/filter/#handle_internal_message","title":"handle_internal_message","text":"
def handle_internal_message(internal_message: Optional[Message]) -> None\n

Handle internal message.

"},{"location":"aea-framework-documentation/api/registries/resources/","title":"Resources","text":""},{"location":"aea-framework-documentation/api/registries/resources/#aearegistriesresources","title":"aea.registries.resources","text":"

This module contains the resources class.

"},{"location":"aea-framework-documentation/api/registries/resources/#resources-objects","title":"Resources Objects","text":"
class Resources()\n

This class implements the object that holds the resources of an AEA.

"},{"location":"aea-framework-documentation/api/registries/resources/#__init__","title":"__init__","text":"
def __init__(agent_name: str = \"standalone\") -> None\n

Instantiate the resources.

Arguments:

  • agent_name: the name of the agent

"},{"location":"aea-framework-documentation/api/registries/resources/#agent_name","title":"agent_name","text":"
@property\ndef agent_name() -> str\n

Get the agent name.

"},{"location":"aea-framework-documentation/api/registries/resources/#component_registry","title":"component_registry","text":"
@property\ndef component_registry() -> AgentComponentRegistry\n

Get the agent component registry.

"},{"location":"aea-framework-documentation/api/registries/resources/#behaviour_registry","title":"behaviour_registry","text":"
@property\ndef behaviour_registry() -> ComponentRegistry[Behaviour]\n

Get the behaviour registry.

"},{"location":"aea-framework-documentation/api/registries/resources/#handler_registry","title":"handler_registry","text":"
@property\ndef handler_registry() -> HandlerRegistry\n

Get the handler registry.

"},{"location":"aea-framework-documentation/api/registries/resources/#model_registry","title":"model_registry","text":"
@property\ndef model_registry() -> ComponentRegistry[Model]\n

Get the model registry.

"},{"location":"aea-framework-documentation/api/registries/resources/#add_component","title":"add_component","text":"
def add_component(component: Component) -> None\n

Add a component to resources.

"},{"location":"aea-framework-documentation/api/registries/resources/#add_protocol","title":"add_protocol","text":"
def add_protocol(protocol: Protocol) -> None\n

Add a protocol to the set of resources.

Arguments:

  • protocol: a protocol

"},{"location":"aea-framework-documentation/api/registries/resources/#get_protocol","title":"get_protocol","text":"
def get_protocol(protocol_id: PublicId) -> Optional[Protocol]\n

Get protocol for given protocol id.

Arguments:

  • protocol_id: the protocol id

Returns:

a matching protocol, if present, else None

"},{"location":"aea-framework-documentation/api/registries/resources/#get_protocol_by_specification_id","title":"get_protocol_by_specification_id","text":"
def get_protocol_by_specification_id(\nprotocol_specification_id: PublicId) -> Optional[Protocol]\n

Get protocol for given protocol_specification_id.

Arguments:

  • protocol_specification_id: the protocol id

Returns:

a matching protocol, if present, else None

"},{"location":"aea-framework-documentation/api/registries/resources/#get_all_protocols","title":"get_all_protocols","text":"
def get_all_protocols() -> List[Protocol]\n

Get the list of all the protocols.

Returns:

the list of protocols.

"},{"location":"aea-framework-documentation/api/registries/resources/#remove_protocol","title":"remove_protocol","text":"
def remove_protocol(protocol_id: PublicId) -> None\n

Remove a protocol from the set of resources.

Arguments:

  • protocol_id: the protocol id for the protocol to be removed.

"},{"location":"aea-framework-documentation/api/registries/resources/#add_contract","title":"add_contract","text":"
def add_contract(contract: Contract) -> None\n

Add a contract to the set of resources.

Arguments:

  • contract: a contract

"},{"location":"aea-framework-documentation/api/registries/resources/#get_contract","title":"get_contract","text":"
def get_contract(contract_id: PublicId) -> Optional[Contract]\n

Get contract for given contract id.

Arguments:

  • contract_id: the contract id

Returns:

a matching contract, if present, else None

"},{"location":"aea-framework-documentation/api/registries/resources/#get_all_contracts","title":"get_all_contracts","text":"
def get_all_contracts() -> List[Contract]\n

Get the list of all the contracts.

Returns:

the list of contracts.

"},{"location":"aea-framework-documentation/api/registries/resources/#remove_contract","title":"remove_contract","text":"
def remove_contract(contract_id: PublicId) -> None\n

Remove a contract from the set of resources.

Arguments:

  • contract_id: the contract id for the contract to be removed.

"},{"location":"aea-framework-documentation/api/registries/resources/#add_connection","title":"add_connection","text":"
def add_connection(connection: Connection) -> None\n

Add a connection to the set of resources.

Arguments:

  • connection: a connection

"},{"location":"aea-framework-documentation/api/registries/resources/#get_connection","title":"get_connection","text":"
def get_connection(connection_id: PublicId) -> Optional[Connection]\n

Get connection for given connection id.

Arguments:

  • connection_id: the connection id

Returns:

a matching connection, if present, else None

"},{"location":"aea-framework-documentation/api/registries/resources/#get_all_connections","title":"get_all_connections","text":"
def get_all_connections() -> List[Connection]\n

Get the list of all the connections.

Returns:

the list of connections.

"},{"location":"aea-framework-documentation/api/registries/resources/#remove_connection","title":"remove_connection","text":"
def remove_connection(connection_id: PublicId) -> None\n

Remove a connection from the set of resources.

Arguments:

  • connection_id: the connection id for the connection to be removed.

"},{"location":"aea-framework-documentation/api/registries/resources/#add_skill","title":"add_skill","text":"
def add_skill(skill: Skill) -> None\n

Add a skill to the set of resources.

Arguments:

  • skill: a skill

"},{"location":"aea-framework-documentation/api/registries/resources/#get_skill","title":"get_skill","text":"
def get_skill(skill_id: PublicId) -> Optional[Skill]\n

Get the skill for a given skill id.

Arguments:

  • skill_id: the skill id

Returns:

a matching skill, if present, else None

"},{"location":"aea-framework-documentation/api/registries/resources/#get_all_skills","title":"get_all_skills","text":"
def get_all_skills() -> List[Skill]\n

Get the list of all the skills.

Returns:

the list of skills.

"},{"location":"aea-framework-documentation/api/registries/resources/#remove_skill","title":"remove_skill","text":"
def remove_skill(skill_id: PublicId) -> None\n

Remove a skill from the set of resources.

Arguments:

  • skill_id: the skill id for the skill to be removed.

"},{"location":"aea-framework-documentation/api/registries/resources/#get_handler","title":"get_handler","text":"
def get_handler(protocol_id: PublicId,\nskill_id: PublicId) -> Optional[Handler]\n

Get a specific handler.

Arguments:

  • protocol_id: the protocol id the handler is handling
  • skill_id: the skill id of the handler's skill

Returns:

the handler

"},{"location":"aea-framework-documentation/api/registries/resources/#get_handlers","title":"get_handlers","text":"
def get_handlers(protocol_id: PublicId) -> List[Handler]\n

Get all handlers for a given protocol.

Arguments:

  • protocol_id: the protocol id the handler is handling

Returns:

the list of handlers matching the protocol

"},{"location":"aea-framework-documentation/api/registries/resources/#get_all_handlers","title":"get_all_handlers","text":"
def get_all_handlers() -> List[Handler]\n

Get all handlers from all skills.

Returns:

the list of handlers

"},{"location":"aea-framework-documentation/api/registries/resources/#get_behaviour","title":"get_behaviour","text":"
def get_behaviour(skill_id: PublicId,\nbehaviour_name: str) -> Optional[Behaviour]\n

Get a specific behaviours for a given skill.

Arguments:

  • skill_id: the skill id
  • behaviour_name: the behaviour name

Returns:

the behaviour, if it is present, else None

"},{"location":"aea-framework-documentation/api/registries/resources/#get_behaviours","title":"get_behaviours","text":"
def get_behaviours(skill_id: PublicId) -> List[Behaviour]\n

Get all behaviours for a given skill.

Arguments:

  • skill_id: the skill id

Returns:

the list of behaviours of the skill

"},{"location":"aea-framework-documentation/api/registries/resources/#get_all_behaviours","title":"get_all_behaviours","text":"
def get_all_behaviours() -> List[Behaviour]\n

Get all behaviours from all skills.

Returns:

the list of all behaviours

"},{"location":"aea-framework-documentation/api/registries/resources/#setup","title":"setup","text":"
def setup() -> None\n

Set up the resources.

Calls setup on all resources.

"},{"location":"aea-framework-documentation/api/registries/resources/#teardown","title":"teardown","text":"
def teardown() -> None\n

Teardown the resources.

Calls teardown on all resources.

"},{"location":"aea-framework-documentation/api/skills/base/","title":"Base","text":""},{"location":"aea-framework-documentation/api/skills/base/#aeaskillsbase","title":"aea.skills.base","text":"

This module contains the base classes for the skills.

"},{"location":"aea-framework-documentation/api/skills/base/#skillcontext-objects","title":"SkillContext Objects","text":"
class SkillContext()\n

This class implements the context of a skill.

"},{"location":"aea-framework-documentation/api/skills/base/#__init__","title":"__init__","text":"
def __init__(agent_context: Optional[AgentContext] = None,\nskill: Optional[\"Skill\"] = None) -> None\n

Initialize a skill context.

Arguments:

  • agent_context: the agent context.
  • skill: the skill.

"},{"location":"aea-framework-documentation/api/skills/base/#logger","title":"logger","text":"
@property\ndef logger() -> Logger\n

Get the logger.

"},{"location":"aea-framework-documentation/api/skills/base/#logger_1","title":"logger","text":"
@logger.setter\ndef logger(logger_: Logger) -> None\n

Set the logger.

"},{"location":"aea-framework-documentation/api/skills/base/#set_agent_context","title":"set_agent_context","text":"
def set_agent_context(agent_context: AgentContext) -> None\n

Set the agent context.

"},{"location":"aea-framework-documentation/api/skills/base/#shared_state","title":"shared_state","text":"
@property\ndef shared_state() -> Dict[str, Any]\n

Get the shared state dictionary.

"},{"location":"aea-framework-documentation/api/skills/base/#agent_name","title":"agent_name","text":"
@property\ndef agent_name() -> str\n

Get agent name.

"},{"location":"aea-framework-documentation/api/skills/base/#skill_id","title":"skill_id","text":"
@property\ndef skill_id() -> PublicId\n

Get the skill id of the skill context.

"},{"location":"aea-framework-documentation/api/skills/base/#is_active","title":"is_active","text":"
@property\ndef is_active() -> bool\n

Get the status of the skill (active/not active).

"},{"location":"aea-framework-documentation/api/skills/base/#is_active_1","title":"is_active","text":"
@is_active.setter\ndef is_active(value: bool) -> None\n

Set the status of the skill (active/not active).

"},{"location":"aea-framework-documentation/api/skills/base/#new_behaviours","title":"new_behaviours","text":"
@property\ndef new_behaviours() -> \"Queue[Behaviour]\"\n

Queue for the new behaviours.

This queue can be used to send messages to the framework to request the registration of a behaviour.

Returns:

the queue of new behaviours.

"},{"location":"aea-framework-documentation/api/skills/base/#new_handlers","title":"new_handlers","text":"
@property\ndef new_handlers() -> \"Queue[Handler]\"\n

Queue for the new handlers.

This queue can be used to send messages to the framework to request the registration of a handler.

Returns:

the queue of new handlers.

"},{"location":"aea-framework-documentation/api/skills/base/#agent_addresses","title":"agent_addresses","text":"
@property\ndef agent_addresses() -> Dict[str, str]\n

Get addresses.

"},{"location":"aea-framework-documentation/api/skills/base/#agent_address","title":"agent_address","text":"
@property\ndef agent_address() -> str\n

Get address.

"},{"location":"aea-framework-documentation/api/skills/base/#public_key","title":"public_key","text":"
@property\ndef public_key() -> str\n

Get public key.

"},{"location":"aea-framework-documentation/api/skills/base/#public_keys","title":"public_keys","text":"
@property\ndef public_keys() -> Dict[str, str]\n

Get public keys.

"},{"location":"aea-framework-documentation/api/skills/base/#connection_status","title":"connection_status","text":"
@property\ndef connection_status() -> MultiplexerStatus\n

Get connection status.

"},{"location":"aea-framework-documentation/api/skills/base/#outbox","title":"outbox","text":"
@property\ndef outbox() -> OutBox\n

Get outbox.

"},{"location":"aea-framework-documentation/api/skills/base/#storage","title":"storage","text":"
@property\ndef storage() -> Optional[Storage]\n

Get optional storage for agent.

"},{"location":"aea-framework-documentation/api/skills/base/#message_in_queue","title":"message_in_queue","text":"
@property\ndef message_in_queue() -> Queue\n

Get message in queue.

"},{"location":"aea-framework-documentation/api/skills/base/#decision_maker_message_queue","title":"decision_maker_message_queue","text":"
@property\ndef decision_maker_message_queue() -> Queue\n

Get message queue of decision maker.

"},{"location":"aea-framework-documentation/api/skills/base/#decision_maker_handler_context","title":"decision_maker_handler_context","text":"
@property\ndef decision_maker_handler_context() -> SimpleNamespace\n

Get decision maker handler context.

"},{"location":"aea-framework-documentation/api/skills/base/#task_manager","title":"task_manager","text":"
@property\ndef task_manager() -> TaskManager\n

Get behaviours of the skill.

"},{"location":"aea-framework-documentation/api/skills/base/#default_ledger_id","title":"default_ledger_id","text":"
@property\ndef default_ledger_id() -> str\n

Get the default ledger id.

"},{"location":"aea-framework-documentation/api/skills/base/#currency_denominations","title":"currency_denominations","text":"
@property\ndef currency_denominations() -> Dict[str, str]\n

Get a dictionary mapping ledger ids to currency denominations.

"},{"location":"aea-framework-documentation/api/skills/base/#search_service_address","title":"search_service_address","text":"
@property\ndef search_service_address() -> Address\n

Get the address of the search service.

"},{"location":"aea-framework-documentation/api/skills/base/#decision_maker_address","title":"decision_maker_address","text":"
@property\ndef decision_maker_address() -> Address\n

Get the address of the decision maker.

"},{"location":"aea-framework-documentation/api/skills/base/#handlers","title":"handlers","text":"
@property\ndef handlers() -> SimpleNamespace\n

Get handlers of the skill.

"},{"location":"aea-framework-documentation/api/skills/base/#behaviours","title":"behaviours","text":"
@property\ndef behaviours() -> SimpleNamespace\n

Get behaviours of the skill.

"},{"location":"aea-framework-documentation/api/skills/base/#namespace","title":"namespace","text":"
@property\ndef namespace() -> SimpleNamespace\n

Get the agent context namespace.

"},{"location":"aea-framework-documentation/api/skills/base/#__getattr__","title":"__getattr__","text":"
def __getattr__(item: Any) -> Any\n

Get attribute.

"},{"location":"aea-framework-documentation/api/skills/base/#send_to_skill","title":"send_to_skill","text":"
def send_to_skill(message_or_envelope: Union[Message, Envelope],\ncontext: Optional[EnvelopeContext] = None) -> None\n

Send message or envelope to another skill.

If message passed it will be wrapped into envelope with optional envelope context.

Arguments:

  • message_or_envelope: envelope to send to another skill.
  • context: the optional envelope context

"},{"location":"aea-framework-documentation/api/skills/base/#skillcomponent-objects","title":"SkillComponent Objects","text":"
class SkillComponent(ABC)\n

This class defines an abstract interface for skill component classes.

"},{"location":"aea-framework-documentation/api/skills/base/#__init___1","title":"__init__","text":"
def __init__(name: str,\nskill_context: SkillContext,\nconfiguration: Optional[SkillComponentConfiguration] = None,\n**kwargs: Any) -> None\n

Initialize a skill component.

Arguments:

  • name: the name of the component.
  • configuration: the configuration for the component.
  • skill_context: the skill context.
  • kwargs: the keyword arguments.

"},{"location":"aea-framework-documentation/api/skills/base/#name","title":"name","text":"
@property\ndef name() -> str\n

Get the name of the skill component.

"},{"location":"aea-framework-documentation/api/skills/base/#context","title":"context","text":"
@property\ndef context() -> SkillContext\n

Get the context of the skill component.

"},{"location":"aea-framework-documentation/api/skills/base/#skill_id_1","title":"skill_id","text":"
@property\ndef skill_id() -> PublicId\n

Get the skill id of the skill component.

"},{"location":"aea-framework-documentation/api/skills/base/#configuration","title":"configuration","text":"
@property\ndef configuration() -> SkillComponentConfiguration\n

Get the skill component configuration.

"},{"location":"aea-framework-documentation/api/skills/base/#config","title":"config","text":"
@property\ndef config() -> Dict[Any, Any]\n

Get the config of the skill component.

"},{"location":"aea-framework-documentation/api/skills/base/#setup","title":"setup","text":"
@abstractmethod\ndef setup() -> None\n

Implement the setup.

"},{"location":"aea-framework-documentation/api/skills/base/#teardown","title":"teardown","text":"
@abstractmethod\ndef teardown() -> None\n

Implement the teardown.

"},{"location":"aea-framework-documentation/api/skills/base/#parse_module","title":"parse_module","text":"
@classmethod\n@abstractmethod\ndef parse_module(cls, path: str, configs: Dict[str,\nSkillComponentConfiguration],\nskill_context: SkillContext) -> dict\n

Parse the component module.

"},{"location":"aea-framework-documentation/api/skills/base/#abstractbehaviour-objects","title":"AbstractBehaviour Objects","text":"
class AbstractBehaviour(SkillComponent, ABC)\n

Abstract behaviour for periodical calls.

tick_interval: float, interval to call behaviour's act. start_at: optional datetime, when to start periodical calls.

"},{"location":"aea-framework-documentation/api/skills/base/#tick_interval","title":"tick_interval","text":"
@property\ndef tick_interval() -> float\n

Get the tick_interval in seconds.

"},{"location":"aea-framework-documentation/api/skills/base/#start_at","title":"start_at","text":"
@property\ndef start_at() -> Optional[datetime.datetime]\n

Get the start time of the behaviour.

"},{"location":"aea-framework-documentation/api/skills/base/#behaviour-objects","title":"Behaviour Objects","text":"
class Behaviour(AbstractBehaviour, ABC)\n

This class implements an abstract behaviour.

In a subclass of Behaviour, the flag 'is_programmatically_defined' can be used by the developer to signal to the framework that the class is meant to be used programmatically; hence, in case the class is not declared in the configuration file but it is present in a skill module, the framework will just ignore this class instead of printing a warning message.

"},{"location":"aea-framework-documentation/api/skills/base/#act","title":"act","text":"
@abstractmethod\ndef act() -> None\n

Implement the behaviour.

Returns:

None

"},{"location":"aea-framework-documentation/api/skills/base/#is_done","title":"is_done","text":"
def is_done() -> bool\n

Return True if the behaviour is terminated, False otherwise.

"},{"location":"aea-framework-documentation/api/skills/base/#act_wrapper","title":"act_wrapper","text":"
def act_wrapper() -> None\n

Wrap the call of the action. This method must be called only by the framework.

"},{"location":"aea-framework-documentation/api/skills/base/#parse_module_1","title":"parse_module","text":"
@classmethod\ndef parse_module(cls, path: str,\nbehaviour_configs: Dict[str, SkillComponentConfiguration],\nskill_context: SkillContext) -> Dict[str, \"Behaviour\"]\n

Parse the behaviours module.

Arguments:

  • path: path to the Python module containing the Behaviour classes.
  • behaviour_configs: a list of behaviour configurations.
  • skill_context: the skill context

Returns:

a list of Behaviour.

"},{"location":"aea-framework-documentation/api/skills/base/#handler-objects","title":"Handler Objects","text":"
class Handler(SkillComponent, ABC)\n

This class implements an abstract behaviour.

In a subclass of Handler, the flag 'is_programmatically_defined' can be used by the developer to signal to the framework that the component is meant to be used programmatically; hence, in case the class is not declared in the configuration file but it is present in a skill module, the framework will just ignore this class instead of printing a warning message.

SUPPORTED_PROTOCOL is read by the framework when the handlers are loaded to register them as 'listeners' to the protocol identified by the specified public id. Whenever a message of protocol 'SUPPORTED_PROTOCOL' is sent to the agent, the framework will call the 'handle' method.

"},{"location":"aea-framework-documentation/api/skills/base/#handle","title":"handle","text":"
@abstractmethod\ndef handle(message: Message) -> None\n

Implement the reaction to a message.

Arguments:

  • message: the message

Returns:

None

"},{"location":"aea-framework-documentation/api/skills/base/#handle_wrapper","title":"handle_wrapper","text":"
def handle_wrapper(message: Message) -> None\n

Wrap the call of the handler. This method must be called only by the framework.

"},{"location":"aea-framework-documentation/api/skills/base/#parse_module_2","title":"parse_module","text":"
@classmethod\ndef parse_module(cls, path: str,\nhandler_configs: Dict[str, SkillComponentConfiguration],\nskill_context: SkillContext) -> Dict[str, \"Handler\"]\n

Parse the handler module.

Arguments:

  • path: path to the Python module containing the Handler class.
  • handler_configs: the list of handler configurations.
  • skill_context: the skill context

Returns:

an handler, or None if the parsing fails.

"},{"location":"aea-framework-documentation/api/skills/base/#model-objects","title":"Model Objects","text":"
class Model(SkillComponent, ABC)\n

This class implements an abstract model.

"},{"location":"aea-framework-documentation/api/skills/base/#__init___2","title":"__init__","text":"
def __init__(name: str,\nskill_context: SkillContext,\nconfiguration: Optional[SkillComponentConfiguration] = None,\nkeep_terminal_state_dialogues: Optional[bool] = None,\n**kwargs: Any) -> None\n

Initialize a model.

Arguments:

  • name: the name of the component.
  • configuration: the configuration for the component.
  • skill_context: the skill context.
  • keep_terminal_state_dialogues: specify do dialogues in terminal state should stay or not
  • kwargs: the keyword arguments.

"},{"location":"aea-framework-documentation/api/skills/base/#setup_1","title":"setup","text":"
def setup() -> None\n

Set the class up.

"},{"location":"aea-framework-documentation/api/skills/base/#teardown_1","title":"teardown","text":"
def teardown() -> None\n

Tear the class down.

"},{"location":"aea-framework-documentation/api/skills/base/#parse_module_3","title":"parse_module","text":"
@classmethod\ndef parse_module(cls, path: str,\nmodel_configs: Dict[str, SkillComponentConfiguration],\nskill_context: SkillContext) -> Dict[str, \"Model\"]\n

Parse the model module.

Arguments:

  • path: path to the Python skill module.
  • model_configs: a list of model configurations.
  • skill_context: the skill context

Returns:

a list of Model.

"},{"location":"aea-framework-documentation/api/skills/base/#skill-objects","title":"Skill Objects","text":"
class Skill(Component)\n

This class implements a skill.

"},{"location":"aea-framework-documentation/api/skills/base/#__init___3","title":"__init__","text":"
def __init__(configuration: SkillConfig,\nskill_context: Optional[SkillContext] = None,\nhandlers: Optional[Dict[str, Handler]] = None,\nbehaviours: Optional[Dict[str, Behaviour]] = None,\nmodels: Optional[Dict[str, Model]] = None,\n**kwargs: Any)\n

Initialize a skill.

Arguments:

  • configuration: the skill configuration.
  • skill_context: the skill context.
  • handlers: dictionary of handlers.
  • behaviours: dictionary of behaviours.
  • models: dictionary of models.
  • kwargs: the keyword arguments.

"},{"location":"aea-framework-documentation/api/skills/base/#skill_context","title":"skill_context","text":"
@property\ndef skill_context() -> SkillContext\n

Get the skill context.

"},{"location":"aea-framework-documentation/api/skills/base/#handlers_1","title":"handlers","text":"
@property\ndef handlers() -> Dict[str, Handler]\n

Get the handlers.

"},{"location":"aea-framework-documentation/api/skills/base/#behaviours_1","title":"behaviours","text":"
@property\ndef behaviours() -> Dict[str, Behaviour]\n

Get the handlers.

"},{"location":"aea-framework-documentation/api/skills/base/#models","title":"models","text":"
@property\ndef models() -> Dict[str, Model]\n

Get the handlers.

"},{"location":"aea-framework-documentation/api/skills/base/#from_dir","title":"from_dir","text":"
@classmethod\ndef from_dir(cls, directory: str, agent_context: AgentContext,\n**kwargs: Any) -> \"Skill\"\n

Load the skill from a directory.

Arguments:

  • directory: the directory to the skill package.
  • agent_context: the skill context.
  • kwargs: the keyword arguments.

Returns:

the skill object.

"},{"location":"aea-framework-documentation/api/skills/base/#logger_2","title":"logger","text":"
@property\ndef logger() -> Logger\n

Get the logger.

In the case of a skill, return the logger provided by the skill context.

Returns:

the logger

"},{"location":"aea-framework-documentation/api/skills/base/#logger_3","title":"logger","text":"
@logger.setter\ndef logger(*args: str) -> None\n

Set the logger.

"},{"location":"aea-framework-documentation/api/skills/base/#from_config","title":"from_config","text":"
@classmethod\ndef from_config(cls, configuration: SkillConfig, agent_context: AgentContext,\n**kwargs: Any) -> \"Skill\"\n

Load the skill from configuration.

Arguments:

  • configuration: a skill configuration. Must be associated with a directory.
  • agent_context: the agent context.
  • kwargs: the keyword arguments.

Returns:

the skill.

"},{"location":"aea-framework-documentation/api/skills/base/#_skillcomponentloadingitem-objects","title":"_SkillComponentLoadingItem Objects","text":"
class _SkillComponentLoadingItem()\n

Class to represent a triple (component name, component configuration, component class).

"},{"location":"aea-framework-documentation/api/skills/base/#__init___4","title":"__init__","text":"
def __init__(name: str, config: SkillComponentConfiguration,\nclass_: Type[SkillComponent], type_: _SKILL_COMPONENT_TYPES)\n

Initialize the item.

"},{"location":"aea-framework-documentation/api/skills/base/#_skillcomponentloader-objects","title":"_SkillComponentLoader Objects","text":"
class _SkillComponentLoader()\n

This class implements the loading policy for skill components.

"},{"location":"aea-framework-documentation/api/skills/base/#__init___5","title":"__init__","text":"
def __init__(configuration: SkillConfig, skill_context: SkillContext,\n**kwargs: Any)\n

Initialize the helper class.

"},{"location":"aea-framework-documentation/api/skills/base/#load_skill","title":"load_skill","text":"
def load_skill() -> Skill\n

Load the skill.

"},{"location":"aea-framework-documentation/api/skills/behaviours/","title":"Behaviors","text":""},{"location":"aea-framework-documentation/api/skills/behaviours/#aeaskillsbehaviours","title":"aea.skills.behaviours","text":"

This module contains the classes for specific behaviours.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#simplebehaviour-objects","title":"SimpleBehaviour Objects","text":"
class SimpleBehaviour(Behaviour, ABC)\n

This class implements a simple behaviour.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#__init__","title":"__init__","text":"
def __init__(act: Optional[Callable[[], None]] = None, **kwargs: Any) -> None\n

Initialize a simple behaviour.

Arguments:

  • act: the act callable.
  • kwargs: the keyword arguments to be passed to the parent class.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#setup","title":"setup","text":"
def setup() -> None\n

Set the behaviour up.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#act","title":"act","text":"
def act() -> None\n

Do the action.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#teardown","title":"teardown","text":"
def teardown() -> None\n

Tear the behaviour down.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#compositebehaviour-objects","title":"CompositeBehaviour Objects","text":"
class CompositeBehaviour(Behaviour, ABC)\n

This class implements a composite behaviour.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#cyclicbehaviour-objects","title":"CyclicBehaviour Objects","text":"
class CyclicBehaviour(SimpleBehaviour, ABC)\n

This behaviour is executed until the agent is stopped.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#__init___1","title":"__init__","text":"
def __init__(**kwargs: Any) -> None\n

Initialize the cyclic behaviour.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#number_of_executions","title":"number_of_executions","text":"
@property\ndef number_of_executions() -> int\n

Get the number of executions.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#act_wrapper","title":"act_wrapper","text":"
def act_wrapper() -> None\n

Wrap the call of the action. This method must be called only by the framework.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#is_done","title":"is_done","text":"
def is_done() -> bool\n

Return True if the behaviour is terminated, False otherwise.

The user should implement it properly to determine the stopping condition.

Returns:

bool indicating status

"},{"location":"aea-framework-documentation/api/skills/behaviours/#oneshotbehaviour-objects","title":"OneShotBehaviour Objects","text":"
class OneShotBehaviour(SimpleBehaviour, ABC)\n

This behaviour is executed only once.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#__init___2","title":"__init__","text":"
def __init__(**kwargs: Any) -> None\n

Initialize the cyclic behaviour.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#is_done_1","title":"is_done","text":"
def is_done() -> bool\n

Return True if the behaviour is terminated, False otherwise.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#act_wrapper_1","title":"act_wrapper","text":"
def act_wrapper() -> None\n

Wrap the call of the action. This method must be called only by the framework.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#tickerbehaviour-objects","title":"TickerBehaviour Objects","text":"
class TickerBehaviour(SimpleBehaviour, ABC)\n

This behaviour is executed periodically with an interval.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#__init___3","title":"__init__","text":"
def __init__(tick_interval: float = 1.0,\nstart_at: Optional[datetime.datetime] = None,\n**kwargs: Any) -> None\n

Initialize the ticker behaviour.

Arguments:

  • tick_interval: interval of the behaviour in seconds.
  • start_at: whether to start the behaviour with an offset.
  • kwargs: the keyword arguments.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#tick_interval","title":"tick_interval","text":"
@property\ndef tick_interval() -> float\n

Get the tick_interval in seconds.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#start_at","title":"start_at","text":"
@property\ndef start_at() -> datetime.datetime\n

Get the start time.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#last_act_time","title":"last_act_time","text":"
@property\ndef last_act_time() -> datetime.datetime\n

Get the last time the act method has been called.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#act_wrapper_2","title":"act_wrapper","text":"
def act_wrapper() -> None\n

Wrap the call of the action. This method must be called only by the framework.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#is_time_to_act","title":"is_time_to_act","text":"
def is_time_to_act() -> bool\n

Check whether it is time to act, according to the tick_interval constraint and the 'start at' constraint.

Returns:

True if it is time to act, false otherwise.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#sequencebehaviour-objects","title":"SequenceBehaviour Objects","text":"
class SequenceBehaviour(CompositeBehaviour, ABC)\n

This behaviour executes sub-behaviour serially.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#__init___4","title":"__init__","text":"
def __init__(behaviour_sequence: List[Behaviour], **kwargs: Any) -> None\n

Initialize the sequence behaviour.

Arguments:

  • behaviour_sequence: the sequence of behaviour.
  • kwargs: the keyword arguments

"},{"location":"aea-framework-documentation/api/skills/behaviours/#current_behaviour","title":"current_behaviour","text":"
@property\ndef current_behaviour() -> Optional[Behaviour]\n

Get the current behaviour.

If None, the sequence behaviour can be considered done.

Returns:

current behaviour or None

"},{"location":"aea-framework-documentation/api/skills/behaviours/#act_1","title":"act","text":"
def act() -> None\n

Implement the behaviour.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#is_done_2","title":"is_done","text":"
def is_done() -> bool\n

Return True if the behaviour is terminated, False otherwise.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#state-objects","title":"State Objects","text":"
class State(SimpleBehaviour, ABC)\n

A state of a FSMBehaviour.

A State behaviour is a simple behaviour with a special property 'event' that is opportunely set by the implementer. The event is read by the framework when the behaviour is done in order to pick the transition to trigger.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#__init___5","title":"__init__","text":"
def __init__(**kwargs: Any) -> None\n

Initialize a state of the state machine.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#event","title":"event","text":"
@property\ndef event() -> Optional[str]\n

Get the event to be triggered at the end of the behaviour.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#is_done_3","title":"is_done","text":"
@abstractmethod\ndef is_done() -> bool\n

Return True if the behaviour is terminated, False otherwise.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#reset","title":"reset","text":"
def reset() -> None\n

Reset initial conditions.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#fsmbehaviour-objects","title":"FSMBehaviour Objects","text":"
class FSMBehaviour(CompositeBehaviour, ABC)\n

This class implements a finite-state machine behaviour.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#__init___6","title":"__init__","text":"
def __init__(**kwargs: Any) -> None\n

Initialize the finite-state machine behaviour.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#is_started","title":"is_started","text":"
@property\ndef is_started() -> bool\n

Check if the behaviour is started.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#register_state","title":"register_state","text":"
def register_state(name: str, state: State, initial: bool = False) -> None\n

Register a state.

Arguments:

  • name: the name of the state.
  • state: the behaviour in that state.
  • initial: whether the state is an initial state.

Raises:

  • ValueError: if a state with the provided name already exists.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#register_final_state","title":"register_final_state","text":"
def register_final_state(name: str, state: State) -> None\n

Register a final state.

Arguments:

  • name: the name of the state.
  • state: the state.

Raises:

  • ValueError: if a state with the provided name already exists.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#unregister_state","title":"unregister_state","text":"
def unregister_state(name: str) -> None\n

Unregister a state.

Arguments:

  • name: the state name to unregister.

Raises:

  • ValueError: if the state is not registered.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#states","title":"states","text":"
@property\ndef states() -> Set[str]\n

Get all the state names.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#initial_state","title":"initial_state","text":"
@property\ndef initial_state() -> Optional[str]\n

Get the initial state name.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#initial_state_1","title":"initial_state","text":"
@initial_state.setter\ndef initial_state(name: str) -> None\n

Set the initial state.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#final_states","title":"final_states","text":"
@property\ndef final_states() -> Set[str]\n

Get the final state names.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#get_state","title":"get_state","text":"
def get_state(name: str) -> Optional[State]\n

Get a state from its name.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#act_2","title":"act","text":"
def act() -> None\n

Implement the behaviour.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#is_done_4","title":"is_done","text":"
def is_done() -> bool\n

Return True if the behaviour is terminated, False otherwise.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#register_transition","title":"register_transition","text":"
def register_transition(source: str,\ndestination: str,\nevent: Optional[str] = None) -> None\n

Register a transition.

No sanity check is done.

Arguments:

  • source: the source state name.
  • destination: the destination state name.
  • event: the event.

Raises:

  • ValueError: if a transition from source with event is already present.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#unregister_transition","title":"unregister_transition","text":"
def unregister_transition(source: str,\ndestination: str,\nevent: Optional[str] = None) -> None\n

Unregister a transition.

Arguments:

  • source: the source state name.
  • destination: the destination state name.
  • event: the event.

Raises:

  • ValueError: if a transition from source with event is not present.
"},{"location":"aea-framework-documentation/api/skills/tasks/","title":"Task","text":""},{"location":"aea-framework-documentation/api/skills/tasks/#aeaskillstasks","title":"aea.skills.tasks","text":"

This module contains the classes for tasks.

"},{"location":"aea-framework-documentation/api/skills/tasks/#task-objects","title":"Task Objects","text":"
class Task(WithLogger)\n

This class implements an abstract task.

"},{"location":"aea-framework-documentation/api/skills/tasks/#__init__","title":"__init__","text":"
def __init__(**kwargs: Any) -> None\n

Initialize a task.

"},{"location":"aea-framework-documentation/api/skills/tasks/#__call__","title":"__call__","text":"
def __call__(*args: Any, **kwargs: Any) -> Any\n

Execute the task.

Arguments:

  • args: positional arguments forwarded to the 'execute' method.
  • kwargs: keyword arguments forwarded to the 'execute' method.

Raises:

  • ValueError: if the task has already been executed.

Returns:

the task instance

"},{"location":"aea-framework-documentation/api/skills/tasks/#is_executed","title":"is_executed","text":"
@property\ndef is_executed() -> bool\n

Check if the task has already been executed.

"},{"location":"aea-framework-documentation/api/skills/tasks/#result","title":"result","text":"
@property\ndef result() -> Any\n

Get the result.

Raises:

  • ValueError: if the task has not been executed yet.

Returns:

the result from the execute method.

"},{"location":"aea-framework-documentation/api/skills/tasks/#setup","title":"setup","text":"
def setup() -> None\n

Implement the task setup.

"},{"location":"aea-framework-documentation/api/skills/tasks/#execute","title":"execute","text":"
@abstractmethod\ndef execute(*args: Any, **kwargs: Any) -> Any\n

Run the task logic.

Arguments:

  • args: the positional arguments
  • kwargs: the keyword arguments

Returns:

any

"},{"location":"aea-framework-documentation/api/skills/tasks/#teardown","title":"teardown","text":"
def teardown() -> None\n

Implement the task teardown.

"},{"location":"aea-framework-documentation/api/skills/tasks/#init_worker","title":"init_worker","text":"
def init_worker() -> None\n

Initialize a worker.

Disable the SIGINT handler of process pool is using. Related to a well-known bug: https://bugs.python.org/issue8296

"},{"location":"aea-framework-documentation/api/skills/tasks/#taskmanager-objects","title":"TaskManager Objects","text":"
class TaskManager(WithLogger)\n

A Task manager.

"},{"location":"aea-framework-documentation/api/skills/tasks/#__init___1","title":"__init__","text":"
def __init__(nb_workers: int = DEFAULT_WORKERS_AMOUNT,\nis_lazy_pool_start: bool = True,\nlogger: Optional[logging.Logger] = None,\npool_mode: str = THREAD_POOL_MODE) -> None\n

Initialize the task manager.

Arguments:

  • nb_workers: the number of worker processes.
  • is_lazy_pool_start: option to postpone pool creation till the first enqueue_task called.
  • logger: the logger.
  • pool_mode: str. multithread or multiprocess

"},{"location":"aea-framework-documentation/api/skills/tasks/#is_started","title":"is_started","text":"
@property\ndef is_started() -> bool\n

Get started status of TaskManager.

Returns:

bool

"},{"location":"aea-framework-documentation/api/skills/tasks/#nb_workers","title":"nb_workers","text":"
@property\ndef nb_workers() -> int\n

Get the number of workers.

Returns:

int

"},{"location":"aea-framework-documentation/api/skills/tasks/#enqueue_task","title":"enqueue_task","text":"
def enqueue_task(func: Callable,\nargs: Sequence = (),\nkwargs: Optional[Dict[str, Any]] = None) -> int\n

Enqueue a task with the executor.

Arguments:

  • func: the callable instance to be enqueued
  • args: the positional arguments to be passed to the function.
  • kwargs: the keyword arguments to be passed to the function.

Raises:

  • ValueError: if the task manager is not running.

Returns:

the task id to get the the result.

"},{"location":"aea-framework-documentation/api/skills/tasks/#get_task_result","title":"get_task_result","text":"
def get_task_result(task_id: int) -> AsyncResult\n

Get the result from a task.

Arguments:

  • task_id: the task id

Returns:

async result for task_id

"},{"location":"aea-framework-documentation/api/skills/tasks/#start","title":"start","text":"
def start() -> None\n

Start the task manager.

"},{"location":"aea-framework-documentation/api/skills/tasks/#stop","title":"stop","text":"
def stop() -> None\n

Stop the task manager.

"},{"location":"aea-framework-documentation/api/skills/tasks/#threadedtaskmanager-objects","title":"ThreadedTaskManager Objects","text":"
class ThreadedTaskManager(TaskManager)\n

A threaded task manager.

"},{"location":"aea-framework-documentation/api/skills/tasks/#__init___2","title":"__init__","text":"
def __init__(nb_workers: int = DEFAULT_WORKERS_AMOUNT,\nis_lazy_pool_start: bool = True,\nlogger: Optional[logging.Logger] = None) -> None\n

Initialize the task manager.

Arguments:

  • nb_workers: the number of worker processes.
  • is_lazy_pool_start: option to postpone pool creation till the first enqueue_task called.
  • logger: the logger.

"},{"location":"aea-framework-documentation/api/skills/tasks/#processtaskmanager-objects","title":"ProcessTaskManager Objects","text":"
class ProcessTaskManager(TaskManager)\n

A multiprocess task manager.

"},{"location":"aea-framework-documentation/api/skills/tasks/#__init___3","title":"__init__","text":"
def __init__(nb_workers: int = DEFAULT_WORKERS_AMOUNT,\nis_lazy_pool_start: bool = True,\nlogger: Optional[logging.Logger] = None) -> None\n

Initialize the task manager.

Arguments:

  • nb_workers: the number of worker processes.
  • is_lazy_pool_start: option to postpone pool creation till the first enqueue_task called.
  • logger: the logger.
"},{"location":"aea-framework-documentation/api/test_tools/constants/","title":"Constants","text":""},{"location":"aea-framework-documentation/api/test_tools/constants/#aeatest_toolsconstants","title":"aea.test_tools.constants","text":"

This is a module with constants for test tools.

"},{"location":"aea-framework-documentation/api/test_tools/exceptions/","title":"Exceptions","text":""},{"location":"aea-framework-documentation/api/test_tools/exceptions/#aeatest_toolsexceptions","title":"aea.test_tools.exceptions","text":"

Module with AEA testing exceptions.

"},{"location":"aea-framework-documentation/api/test_tools/exceptions/#aeatestingexception-objects","title":"AEATestingException Objects","text":"
class AEATestingException(Exception)\n

An exception to be raised on incorrect testing tools usage.

"},{"location":"aea-framework-documentation/api/test_tools/generic/","title":"Generic","text":""},{"location":"aea-framework-documentation/api/test_tools/generic/#aeatest_toolsgeneric","title":"aea.test_tools.generic","text":"

This module contains generic tools for AEA end-to-end testing.

"},{"location":"aea-framework-documentation/api/test_tools/generic/#write_envelope_to_file","title":"write_envelope_to_file","text":"
def write_envelope_to_file(envelope: Envelope, file_path: str) -> None\n

Write an envelope to a file.

Arguments:

  • envelope: Envelope.
  • file_path: the file path

"},{"location":"aea-framework-documentation/api/test_tools/generic/#read_envelope_from_file","title":"read_envelope_from_file","text":"
def read_envelope_from_file(file_path: str) -> Envelope\n

Read an envelope from a file.

Arguments:

  • file_path: the file path.

Returns:

envelope

"},{"location":"aea-framework-documentation/api/test_tools/generic/#nested_set_config","title":"nested_set_config","text":"
def nested_set_config(dotted_path: str,\nvalue: Any,\nauthor: str = DEFAULT_AUTHOR) -> None\n

Set an AEA config with nested values.

Run from agent's directory.

Allowed dotted_path: 'agent.an_attribute_name' 'protocols.my_protocol.an_attribute_name' 'connections.my_connection.an_attribute_name' 'contracts.my_contract.an_attribute_name' 'skills.my_skill.an_attribute_name' 'vendor.author.[protocols|connections|skills].package_name.attribute_name

Arguments:

  • dotted_path: dotted path to a setting.
  • value: a value to assign. Must be of yaml serializable type.
  • author: the author name, used to parse the dotted path.
"},{"location":"aea-framework-documentation/api/test_tools/test_cases/","title":"Test Cases","text":""},{"location":"aea-framework-documentation/api/test_tools/test_cases/#aeatest_toolstest_cases","title":"aea.test_tools.test_cases","text":"

This module contains test case classes based on pytest for AEA end-to-end testing.

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#baseaeatestcase-objects","title":"BaseAEATestCase Objects","text":"
class BaseAEATestCase(ABC)\n

Base class for AEA test cases.

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#set_agent_context","title":"set_agent_context","text":"
@classmethod\ndef set_agent_context(cls, agent_name: str) -> None\n

Set the current agent context.

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#unset_agent_context","title":"unset_agent_context","text":"
@classmethod\ndef unset_agent_context(cls) -> None\n

Unset the current agent context.

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#set_config","title":"set_config","text":"
@classmethod\ndef set_config(cls,\ndotted_path: str,\nvalue: Any,\ntype_: Optional[str] = None) -> Result\n

Set a config.

Run from agent's directory.

Arguments:

  • dotted_path: str dotted path to config param.
  • value: a new value to set.
  • type_: the type

Returns:

Result

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#nested_set_config","title":"nested_set_config","text":"
@classmethod\ndef nested_set_config(cls, dotted_path: str, value: Any) -> None\n

Force set config.

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#disable_aea_logging","title":"disable_aea_logging","text":"
@classmethod\ndef disable_aea_logging(cls) -> None\n

Disable AEA logging of specific agent.

Run from agent's directory.

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#run_cli_command","title":"run_cli_command","text":"
@classmethod\ndef run_cli_command(cls, *args: str, cwd: str = \".\", **kwargs: str) -> Result\n

Run AEA CLI command.

Arguments:

  • args: CLI args
  • cwd: the working directory from where to run the command.
  • kwargs: other keyword arguments to click.CliRunner.invoke.

Raises:

  • AEATestingException: if command fails.

Returns:

Result

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#start_subprocess","title":"start_subprocess","text":"
@classmethod\ndef start_subprocess(cls, *args: str, cwd: str = \".\") -> subprocess.Popen\n

Run python with args as subprocess.

Arguments:

  • args: CLI args
  • cwd: the current working directory

Returns:

subprocess object.

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#start_thread","title":"start_thread","text":"
@classmethod\ndef start_thread(cls, target: Callable, **kwargs: subprocess.Popen) -> Thread\n

Start python Thread.

Arguments:

  • target: target method.
  • kwargs: thread keyword arguments

Returns:

thread

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#create_agents","title":"create_agents","text":"
@classmethod\ndef create_agents(cls,\n*agents_names: str,\nis_local: bool = True,\nis_empty: bool = False) -> None\n

Create agents in current working directory.

Arguments:

  • agents_names: str agent names.
  • is_local: a flag for local folder add True by default.
  • is_empty: optional boolean flag for skip adding default dependencies.

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#fetch_agent","title":"fetch_agent","text":"
@classmethod\ndef fetch_agent(cls,\npublic_id: str,\nagent_name: str,\nis_local: bool = True) -> None\n

Create agents in current working directory.

Arguments:

  • public_id: str public id
  • agent_name: str agent name.
  • is_local: a flag for local folder add True by default.

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#difference_to_fetched_agent","title":"difference_to_fetched_agent","text":"
@classmethod\ndef difference_to_fetched_agent(cls, public_id: str,\nagent_name: str) -> List[str]\n

Compare agent against the one fetched from public id.

Arguments:

  • public_id: str public id
  • agent_name: str agent name.

Returns:

list of files differing in the projects

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#delete_agents","title":"delete_agents","text":"
@classmethod\ndef delete_agents(cls, *agents_names: str) -> None\n

Delete agents in current working directory.

Arguments:

  • agents_names: str agent names.

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#run_agent","title":"run_agent","text":"
@classmethod\ndef run_agent(cls, *args: str) -> subprocess.Popen\n

Run agent as subprocess.

Run from agent's directory.

Arguments:

  • args: CLI args

Returns:

subprocess object.

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#run_interaction","title":"run_interaction","text":"
@classmethod\ndef run_interaction(cls) -> subprocess.Popen\n

Run interaction as subprocess.

Run from agent's directory.

Returns:

subprocess object.

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#terminate_agents","title":"terminate_agents","text":"
@classmethod\ndef terminate_agents(cls,\n*subprocesses: subprocess.Popen,\ntimeout: int = 20) -> None\n

Terminate agent subprocesses.

Run from agent's directory.

Arguments:

  • subprocesses: the subprocesses running the agents
  • timeout: the timeout for interruption

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#is_successfully_terminated","title":"is_successfully_terminated","text":"
@classmethod\ndef is_successfully_terminated(cls, *subprocesses: subprocess.Popen) -> bool\n

Check if all subprocesses terminated successfully.

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#initialize_aea","title":"initialize_aea","text":"
@classmethod\ndef initialize_aea(cls, author: str) -> None\n

Initialize AEA locally with author name.

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#add_item","title":"add_item","text":"
@classmethod\ndef add_item(cls,\nitem_type: str,\npublic_id: str,\nlocal: bool = True) -> Result\n

Add an item to the agent.

Run from agent's directory.

Arguments:

  • item_type: str item type.
  • public_id: public id of the item.
  • local: a flag for local folder add True by default.

Returns:

Result

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#remove_item","title":"remove_item","text":"
@classmethod\ndef remove_item(cls, item_type: str, public_id: str) -> Result\n

Remove an item from the agent.

Run from agent's directory.

Arguments:

  • item_type: str item type.
  • public_id: public id of the item.

Returns:

Result

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#scaffold_item","title":"scaffold_item","text":"
@classmethod\ndef scaffold_item(cls,\nitem_type: str,\nname: str,\nskip_consistency_check: bool = False) -> Result\n

Scaffold an item for the agent.

Run from agent's directory.

Arguments:

  • item_type: str item type.
  • name: name of the item.
  • skip_consistency_check: if True, skip consistency check.

Returns:

Result

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#fingerprint_item","title":"fingerprint_item","text":"
@classmethod\ndef fingerprint_item(cls, item_type: str, public_id: str) -> Result\n

Fingerprint an item for the agent.

Run from agent's directory.

Arguments:

  • item_type: str item type.
  • public_id: public id of the item.

Returns:

Result

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#eject_item","title":"eject_item","text":"
@classmethod\ndef eject_item(cls, item_type: str, public_id: str) -> Result\n

Eject an item in the agent in quiet mode (i.e. no interaction).

Run from agent's directory.

Arguments:

  • item_type: str item type.
  • public_id: public id of the item.

Returns:

None

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#run_install","title":"run_install","text":"
@classmethod\ndef run_install(cls) -> Result\n

Execute AEA CLI install command.

Run from agent's directory.

Returns:

Result

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#generate_private_key","title":"generate_private_key","text":"
@classmethod\ndef generate_private_key(cls,\nledger_api_id: str = DEFAULT_LEDGER,\nprivate_key_file: Optional[str] = None,\npassword: Optional[str] = None) -> Result\n

Generate AEA private key with CLI command.

Run from agent's directory.

Arguments:

  • ledger_api_id: ledger API ID.
  • private_key_file: the private key file.
  • password: the password.

Returns:

Result

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#add_private_key","title":"add_private_key","text":"
@classmethod\ndef add_private_key(cls,\nledger_api_id: str = DEFAULT_LEDGER,\nprivate_key_filepath: str = DEFAULT_PRIVATE_KEY_FILE,\nconnection: bool = False,\npassword: Optional[str] = None) -> Result\n

Add private key with CLI command.

Run from agent's directory.

Arguments:

  • ledger_api_id: ledger API ID.
  • private_key_filepath: private key filepath.
  • connection: whether or not the private key filepath is for a connection.
  • password: the password to encrypt private keys.

Returns:

Result

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#remove_private_key","title":"remove_private_key","text":"
@classmethod\ndef remove_private_key(cls,\nledger_api_id: str = DEFAULT_LEDGER,\nconnection: bool = False) -> Result\n

Remove private key with CLI command.

Run from agent's directory.

Arguments:

  • ledger_api_id: ledger API ID.
  • connection: whether or not the private key filepath is for a connection.

Returns:

Result

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#replace_private_key_in_file","title":"replace_private_key_in_file","text":"
@classmethod\ndef replace_private_key_in_file(\ncls,\nprivate_key: str,\nprivate_key_filepath: str = DEFAULT_PRIVATE_KEY_FILE) -> None\n

Replace the private key in the provided file with the provided key.

Arguments:

  • private_key: the private key
  • private_key_filepath: the filepath to the private key file

Raises:

  • None: exception if file does not exist

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#generate_wealth","title":"generate_wealth","text":"
@classmethod\ndef generate_wealth(cls,\nledger_api_id: str = DEFAULT_LEDGER,\npassword: Optional[str] = None) -> Result\n

Generate wealth with CLI command.

Run from agent's directory.

Arguments:

  • ledger_api_id: ledger API ID.
  • password: the password.

Returns:

Result

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#get_wealth","title":"get_wealth","text":"
@classmethod\ndef get_wealth(cls,\nledger_api_id: str = DEFAULT_LEDGER,\npassword: Optional[str] = None) -> str\n

Get wealth with CLI command.

Run from agent's directory.

Arguments:

  • ledger_api_id: ledger API ID.
  • password: the password to encrypt/decrypt private keys.

Returns:

command line output

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#get_address","title":"get_address","text":"
@classmethod\ndef get_address(cls,\nledger_api_id: str = DEFAULT_LEDGER,\npassword: Optional[str] = None) -> str\n

Get address with CLI command.

Run from agent's directory.

Arguments:

  • ledger_api_id: ledger API ID.
  • password: the password to encrypt/decrypt private keys.

Returns:

command line output

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#replace_file_content","title":"replace_file_content","text":"
@classmethod\ndef replace_file_content(cls, src: Path, dest: Path) -> None\n

Replace the content of the source file to the destination file.

Arguments:

  • src: the source file.
  • dest: the destination file.

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#change_directory","title":"change_directory","text":"
@classmethod\ndef change_directory(cls, path: Path) -> None\n

Change current working directory.

Arguments:

  • path: path to the new working directory.

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#send_envelope_to_agent","title":"send_envelope_to_agent","text":"
@classmethod\ndef send_envelope_to_agent(cls, envelope: Envelope, agent: str) -> None\n

Send an envelope to an agent, using the stub connection.

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#read_envelope_from_agent","title":"read_envelope_from_agent","text":"
@classmethod\ndef read_envelope_from_agent(cls, agent: str) -> Envelope\n

Read an envelope from an agent, using the stub connection.

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#missing_from_output","title":"missing_from_output","text":"
@classmethod\ndef missing_from_output(cls,\nprocess: subprocess.Popen,\nstrings: Sequence[str],\ntimeout: int = DEFAULT_PROCESS_TIMEOUT,\nperiod: int = 1,\nis_terminating: bool = True) -> List[str]\n

Check if strings are present in process output.

Read process stdout in thread and terminate when all strings are present or timeout expired.

Arguments:

  • process: agent subprocess.
  • strings: tuple of strings expected to appear in output.
  • timeout: int amount of seconds before stopping check.
  • period: int period of checking.
  • is_terminating: whether or not the agents are terminated

Returns:

list of missed strings.

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#is_running","title":"is_running","text":"
@classmethod\ndef is_running(cls,\nprocess: subprocess.Popen,\ntimeout: int = DEFAULT_LAUNCH_TIMEOUT) -> bool\n

Check if the AEA is launched and running (ready to process messages).

Arguments:

  • process: agent subprocess.
  • timeout: the timeout to wait for launch to complete

Returns:

bool indicating status

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#invoke","title":"invoke","text":"
@classmethod\ndef invoke(cls, *args: str) -> Result\n

Call the cli command.

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#load_agent_config","title":"load_agent_config","text":"
@classmethod\ndef load_agent_config(cls, agent_name: str) -> AgentConfig\n

Load agent configuration.

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#setup_class","title":"setup_class","text":"
@classmethod\ndef setup_class(cls) -> None\n

Set up the test class.

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#teardown_class","title":"teardown_class","text":"
@classmethod\ndef teardown_class(cls) -> None\n

Teardown the test.

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#aeatestcaseempty-objects","title":"AEATestCaseEmpty Objects","text":"
class AEATestCaseEmpty(BaseAEATestCase)\n

Test case for a default AEA project.

This test case will create a default AEA project.

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#setup_class_1","title":"setup_class","text":"
@classmethod\ndef setup_class(cls) -> None\n

Set up the test class.

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#teardown_class_1","title":"teardown_class","text":"
@classmethod\ndef teardown_class(cls) -> None\n

Teardown the test class.

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#aeatestcaseemptyflaky-objects","title":"AEATestCaseEmptyFlaky Objects","text":"
class AEATestCaseEmptyFlaky(AEATestCaseEmpty)\n

Test case for a default AEA project.

This test case will create a default AEA project.

Use for flaky tests with the flaky decorator.

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#setup_class_2","title":"setup_class","text":"
@classmethod\ndef setup_class(cls) -> None\n

Set up the test class.

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#teardown_class_2","title":"teardown_class","text":"
@classmethod\ndef teardown_class(cls) -> None\n

Teardown the test class.

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#aeatestcasemany-objects","title":"AEATestCaseMany Objects","text":"
class AEATestCaseMany(BaseAEATestCase)\n

Test case for many AEA projects.

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#setup_class_3","title":"setup_class","text":"
@classmethod\ndef setup_class(cls) -> None\n

Set up the test class.

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#teardown_class_3","title":"teardown_class","text":"
@classmethod\ndef teardown_class(cls) -> None\n

Teardown the test class.

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#aeatestcasemanyflaky-objects","title":"AEATestCaseManyFlaky Objects","text":"
class AEATestCaseManyFlaky(AEATestCaseMany)\n

Test case for many AEA projects which are flaky.

Use for flaky tests with the flaky decorator.

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#setup_class_4","title":"setup_class","text":"
@classmethod\ndef setup_class(cls) -> None\n

Set up the test class.

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#teardown_class_4","title":"teardown_class","text":"
@classmethod\ndef teardown_class(cls) -> None\n

Teardown the test class.

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#aeatestcase-objects","title":"AEATestCase Objects","text":"
class AEATestCase(BaseAEATestCase)\n

Test case from an existing AEA project.

Subclass this class and set path_to_aea properly. By default, it is assumed the project is inside the current working directory.

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#setup_class_5","title":"setup_class","text":"
@classmethod\ndef setup_class(cls) -> None\n

Set up the test class.

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#teardown_class_5","title":"teardown_class","text":"
@classmethod\ndef teardown_class(cls) -> None\n

Teardown the test class.

"},{"location":"aea-framework-documentation/api/test_tools/test_contract/","title":"Test Contract","text":""},{"location":"aea-framework-documentation/api/test_tools/test_contract/#aeatest_toolstest_contract","title":"aea.test_tools.test_contract","text":"

This module contains test case classes based on pytest for AEA contract testing.

"},{"location":"aea-framework-documentation/api/test_tools/test_contract/#basecontracttestcase-objects","title":"BaseContractTestCase Objects","text":"
class BaseContractTestCase(ABC)\n

A class to test a contract.

"},{"location":"aea-framework-documentation/api/test_tools/test_contract/#contract","title":"contract","text":"
@property\ndef contract() -> Contract\n

Get the contract.

"},{"location":"aea-framework-documentation/api/test_tools/test_contract/#setup","title":"setup","text":"
@classmethod\ndef setup(cls, **kwargs: Any) -> None\n

Set up the contract test case.

"},{"location":"aea-framework-documentation/api/test_tools/test_contract/#finish_contract_deployment","title":"finish_contract_deployment","text":"
@classmethod\n@abstractmethod\ndef finish_contract_deployment(cls) -> str\n

Finish deploying contract.

Returns:

contract address

"},{"location":"aea-framework-documentation/api/test_tools/test_contract/#refill_from_faucet","title":"refill_from_faucet","text":"
@staticmethod\ndef refill_from_faucet(ledger_api: LedgerApi, faucet_api: FaucetApi,\naddress: str) -> None\n

Refill from faucet.

"},{"location":"aea-framework-documentation/api/test_tools/test_contract/#sign_send_confirm_receipt_multisig_transaction","title":"sign_send_confirm_receipt_multisig_transaction","text":"
@staticmethod\ndef sign_send_confirm_receipt_multisig_transaction(\ntx: JSONLike,\nledger_api: LedgerApi,\ncryptos: List[Crypto],\nsleep_time: float = 2.0) -> JSONLike\n

Sign, send and confirm settlement of a transaction with multiple signatures.

Arguments:

  • tx: the transaction
  • ledger_api: the ledger api
  • cryptos: Cryptos to sign transaction with
  • sleep_time: the time to sleep between transaction submission and receipt request

Returns:

The transaction receipt

"},{"location":"aea-framework-documentation/api/test_tools/test_contract/#sign_send_confirm_receipt_transaction","title":"sign_send_confirm_receipt_transaction","text":"
@classmethod\ndef sign_send_confirm_receipt_transaction(cls,\ntx: JSONLike,\nledger_api: LedgerApi,\ncrypto: Crypto,\nsleep_time: float = 2.0) -> JSONLike\n

Sign, send and confirm settlement of a transaction with multiple signatures.

Arguments:

  • tx: the transaction
  • ledger_api: the ledger api
  • crypto: Crypto to sign transaction with
  • sleep_time: the time to sleep between transaction submission and receipt request

Returns:

The transaction receipt

"},{"location":"aea-framework-documentation/api/test_tools/test_skill/","title":"Test Skill","text":""},{"location":"aea-framework-documentation/api/test_tools/test_skill/#aeatest_toolstest_skill","title":"aea.test_tools.test_skill","text":"

This module contains test case classes based on pytest for AEA skill testing.

"},{"location":"aea-framework-documentation/api/test_tools/test_skill/#baseskilltestcase-objects","title":"BaseSkillTestCase Objects","text":"
class BaseSkillTestCase()\n

A class to test a skill.

"},{"location":"aea-framework-documentation/api/test_tools/test_skill/#skill","title":"skill","text":"
@property\ndef skill() -> Skill\n

Get the skill.

"},{"location":"aea-framework-documentation/api/test_tools/test_skill/#get_quantity_in_outbox","title":"get_quantity_in_outbox","text":"
def get_quantity_in_outbox() -> int\n

Get the quantity of envelopes in the outbox.

"},{"location":"aea-framework-documentation/api/test_tools/test_skill/#get_message_from_outbox","title":"get_message_from_outbox","text":"
def get_message_from_outbox() -> Optional[Message]\n

Get message from outbox.

"},{"location":"aea-framework-documentation/api/test_tools/test_skill/#drop_messages_from_outbox","title":"drop_messages_from_outbox","text":"
def drop_messages_from_outbox(number: int = 1) -> None\n

Dismiss the first 'number' number of message from outbox.

"},{"location":"aea-framework-documentation/api/test_tools/test_skill/#get_quantity_in_decision_maker_inbox","title":"get_quantity_in_decision_maker_inbox","text":"
def get_quantity_in_decision_maker_inbox() -> int\n

Get the quantity of messages in the decision maker inbox.

"},{"location":"aea-framework-documentation/api/test_tools/test_skill/#get_message_from_decision_maker_inbox","title":"get_message_from_decision_maker_inbox","text":"
def get_message_from_decision_maker_inbox() -> Optional[Message]\n

Get message from decision maker inbox.

"},{"location":"aea-framework-documentation/api/test_tools/test_skill/#drop_messages_from_decision_maker_inbox","title":"drop_messages_from_decision_maker_inbox","text":"
def drop_messages_from_decision_maker_inbox(number: int = 1) -> None\n

Dismiss the first 'number' number of message from decision maker inbox.

"},{"location":"aea-framework-documentation/api/test_tools/test_skill/#assert_quantity_in_outbox","title":"assert_quantity_in_outbox","text":"
def assert_quantity_in_outbox(expected_quantity: int) -> None\n

Assert the quantity of messages in the outbox.

"},{"location":"aea-framework-documentation/api/test_tools/test_skill/#assert_quantity_in_decision_making_queue","title":"assert_quantity_in_decision_making_queue","text":"
def assert_quantity_in_decision_making_queue(expected_quantity: int) -> None\n

Assert the quantity of messages in the decision maker queue.

"},{"location":"aea-framework-documentation/api/test_tools/test_skill/#message_has_attributes","title":"message_has_attributes","text":"
@staticmethod\ndef message_has_attributes(actual_message: Message,\nmessage_type: Type[Message],\n**kwargs: Any) -> Tuple[bool, str]\n

Evaluates whether a message's attributes match the expected attributes provided.

Arguments:

  • actual_message: the actual message
  • message_type: the expected message type
  • kwargs: other expected message attributes

Returns:

boolean result of the evaluation and accompanied message

"},{"location":"aea-framework-documentation/api/test_tools/test_skill/#build_incoming_message","title":"build_incoming_message","text":"
def build_incoming_message(message_type: Type[Message],\nperformative: Message.Performative,\ndialogue_reference: Optional[Tuple[str,\nstr]] = None,\nmessage_id: Optional[int] = None,\ntarget: Optional[int] = None,\nto: Optional[Address] = None,\nsender: Optional[Address] = None,\nis_agent_to_agent_messages: Optional[bool] = None,\n**kwargs: Any) -> Message\n

Quickly create an incoming message with the provided attributes.

For any attribute not provided, the corresponding default value in message is used.

Arguments:

  • message_type: the type of the message
  • dialogue_reference: the dialogue_reference
  • message_id: the message_id
  • target: the target
  • performative: the performative
  • to: the 'to' address
  • sender: the 'sender' address
  • is_agent_to_agent_messages: whether the dialogue is between agents or components
  • kwargs: other attributes

Returns:

the created incoming message

"},{"location":"aea-framework-documentation/api/test_tools/test_skill/#build_incoming_message_for_skill_dialogue","title":"build_incoming_message_for_skill_dialogue","text":"
def build_incoming_message_for_skill_dialogue(\ndialogue: Dialogue,\nperformative: Message.Performative,\nmessage_type: Optional[Type[Message]] = None,\ndialogue_reference: Optional[Tuple[str, str]] = None,\nmessage_id: Optional[int] = None,\ntarget: Optional[int] = None,\nto: Optional[Address] = None,\nsender: Optional[Address] = None,\n**kwargs: Any) -> Message\n

Quickly create an incoming message with the provided attributes for a dialogue.

For any attribute not provided, a value based on the dialogue is used. These values are shown in parentheses in the list of parameters below.

NOTE: This method must be used with care. The dialogue provided is part of the skill which is being tested. Because for any unspecified attribute, a \"correct\" value is used, the test will be, by design, insured to pass on these values.

Arguments:

  • dialogue: the dialogue to which the incoming message is intended
  • performative: the performative of the message
  • message_type: (the message_class of the provided dialogue) the type of the message
  • dialogue_reference: (the dialogue_reference of the provided dialogue) the dialogue reference of the message
  • message_id: (the id of the last message in the provided dialogue + 1) the id of the message
  • target: (the id of the last message in the provided dialogue) the target of the message
  • to: (the agent address associated with this skill) the receiver of the message
  • sender: (the counterparty in the provided dialogue) the sender of the message
  • kwargs: other attributes

Returns:

the created incoming message

"},{"location":"aea-framework-documentation/api/test_tools/test_skill/#prepare_skill_dialogue","title":"prepare_skill_dialogue","text":"
def prepare_skill_dialogue(\ndialogues: Dialogues,\nmessages: Tuple[DialogueMessage, ...],\ncounterparty: Optional[Address] = None,\nis_agent_to_agent_messages: Optional[bool] = None) -> Dialogue\n

Quickly create a dialogue.

The 'messages' argument is a tuple of DialogueMessages. For every DialogueMessage (performative, contents, is_incoming, target): - if 'is_incoming' is not provided: for the first message it is assumed False (outgoing), for any other message, it is the opposite of the one preceding it. - if 'target' is not provided: for the first message it is assumed 0, for any other message, it is the index of the message before it in the tuple of messages + 1.

Arguments:

  • dialogues: a dialogues class
  • counterparty: the message_id
  • messages: the dialogue_reference
  • is_agent_to_agent_messages: whether the dialogue is between agents or components

Returns:

the created incoming message

"},{"location":"aea-framework-documentation/api/test_tools/test_skill/#setup","title":"setup","text":"
@classmethod\ndef setup(cls, **kwargs: Any) -> None\n

Set up the skill test case.

"},{"location":"archives/redelegate-for-decentralization/","title":"How to Redelegate for Decentralization","text":"

Introduction

Decentralization is a key problem that we as a community need to tackle in order to provide more security to the network.

As part of this, we have requested that everyone currently staking on the Fetch.ai Mainnet check to see if they are only staking with a single validator or if they are staking within the top 10 validators.

You can tell if your validator is in the top 10 validators by going to the Validator Voting Power Distribution List.

If this is the case, then we would greatly appreciate it if you followed this guide on the importance of redelegating part of your stake outside the top 10 validators and how to do it.

Disclaimer - please note the following:

  1. We are asking you to redelegate your staked $FET, do not undelegate your $FET to transfer from one validator to another, otherwise this process will cause you to lose out on staking rewards for 21 days while you wait for the unbonding period to end.
  2. Redelegating, unlike undelegating, is an instant way for you to move part (or all) of your delegated $FET to different validators. Doing this is a very easy process and is what we will be outlining in this article.
"},{"location":"archives/redelegate-for-decentralization/#why-redelegating-is-important-right-now","title":"Why redelegating is important right now","text":"

The top 10 validators on the Fetch.ai network currently control roughly 50% of the total voting power on chain through delegations.

For the Fetch.ai network to be as secure as possible, we shouldn't have more than 33% of delegated FET staked within the top 10 validators. It should be made clear that lists of validators on block explorers are not the highest to lowest for reputation or benefits, it's simply outlining who has the most FET staked to them.

When it comes to reputation or how the validator has performed, it's up to you as a delegator to find that out. Delegating is meant to be an active role between you and a Validator (although commonly perceived incorrectly as \"passive income\").

When you select a validator to delegate with, that is essentially your vote to the network saying they are trustworthy, and you are given staking rewards for that.

So please ensure that you do your due diligence in ensuring you know as much about a validator as possible.

"},{"location":"archives/redelegate-for-decentralization/#to-redelegate-using-the-cosmostation-app","title":"To redelegate using the Cosmostation App","text":"
  1. On the main screen, click Delegate.
  2. Select the validator you're staking with that you wish to move your stake from.
  3. Select Redelegate.
  4. Enter the amount of FET you wish to redelegate and click Next.
  5. Choose a validator outside of the top 10 that you wish to redelegate to (see here for the voting power of the validators), and click Next.
  6. You'll be prompted to enter an optional memo, but you can leave it blank. Click Next.
  7. Choose the transaction fee for redelegating (low is fine). Click Next.
  8. Review the details of your redelegation. Click Confirm.
  9. Click Yes on the popup. Enter your PIN.
"},{"location":"archives/redelegate-for-decentralization/#to-redelegate-using-keystationcosmostation-web-wallet","title":"To redelegate using Keystation/Cosmostation Web Wallet","text":"
  1. Connect your Fetch.ai wallet to the Cosmostation web wallet.
  2. Select Reward from the options on the left side panel.
  3. Scroll to My Validators and select Redelegate.
  4. Pick the validator outside of the top 10 that you would like to Redelegate to.
  5. Enter the amount of FET you'd like to redelegate to them. Leave the memo blank.
  6. Click Generate & Sign Transaction. On the pop-up select Allow.
  7. Enter your PIN.
"},{"location":"archives/redelegate-for-decentralization/#to-redelegate-using-the-fetch-wallet","title":"To redelegate using the Fetch Wallet","text":"

First you must ensure that you have the Fetch.ai wallet that you're staking with connected to the Fetch Wallet.

  1. Connect to the Fetch Wallet.
  2. In the Fetch.ai Validator Explorer page., click Connect in the top right of the web page. Once you're connected, click on your wallet address that is now listed in the top right on the web page.
  3. From there, scroll down to the Delegations section. Find the validator you'd like to redelegate from.
  4. Click Transfer Stake. In the pop-up that appears, select a validator below the top 10 that you'd like to redelegate to.

    Please keep in mind they are not in order on the drop-down, so you will need to have the Validator Voting Power Distribution List open in another tab to see the order.

  5. Enter in the amount of FET you wish to redelegate to this new validator.

  6. Select Transfer Stake. You will then be prompted by a transaction request.

    Choose the gas fee (Low is fine)

  7. Click Approve.

    And then you're done.

"},{"location":"archives/redelegate-for-decentralization/#what-should-you-look-for-in-a-validator","title":"What should you look for in a validator?","text":"

People always ask what they should be looking for in a validator. So here is a short list of things you, as a delegator, can look for online or ask validators themselves.

  • Do they have a website?
  • Do they have social media? (Twitter, Reddit, Instagram)
  • Are they easy to contact? (Discord, Telegram)
  • Do they have FET self-bonded to their node?
  • Are they active in the community?
  • Do they have a good uptime?
  • Do they offer slashing & double sign protection?
  • Do they vote on proposals?
  • Do they have a mission and principles that align with your own?

Some of these questions can be answered through one of the Fetch.ai block explorers, such as our Native Block Explorer or Mintscan. But other things on this list will require you to reach out to validators for answers, which is a good opportunity to see if they are active frequently or not. It should also be noted that not every single one of these questions needs to be answered \"yes\" but if they are, then it's extremely helpful.

"},{"location":"basics/staking/different_ways_to_stake_fet/","title":"Different ways to stake FET","text":"

Below you can find different ways one can stake FET.

"},{"location":"basics/staking/different_ways_to_stake_fet/#stakingunstaking-on-fetch-mainnet-for-binance-users","title":"STAKING/UNSTAKING ON FETCH MAINNET FOR BINANCE USERS","text":"

Binance has integrated native FET which means you can send your tokens directly to your cosmostation address and start staking. Above is the user journey detailing the steps.

If you need further clarification, please feel free to refer to the section Applicable for users holding FET on Binance below.

"},{"location":"basics/staking/different_ways_to_stake_fet/#how-to-stake-fet-using-ledger-on-cosmostation-web-wallet-applicable-for-users-holding-fet-on-binance","title":"How to stake FET using Ledger on Cosmostation Web Wallet: applicable for users holding FET on Binance","text":"

Binance has completed the Fetch.ai (FET) mainnet integration. This means Binance has integrated Native FET tokens which can be staked easily on Cosmostation without relying on token bridge. Please follow the below steps:

  • Step 1: Please head over to Cosmostation Web Wallet (Fetch.ai chain) and create an address for Fetch.ai chain. There is a detailed pdf from the Cosmostation team explaining how to create an account using ledger and how to use the web wallet.

  • Step 2: Once done, send a small amount of FET as test from your Binance account to your generated Cosmostation address. Make sure the addresses are correct and above all the chain chosen for your generated address is Fetch.ai chain on Cosmostation web wallet.

  • Step 3: If the tokens arrive, send the rest.

  • Step 4: Delegate to your preferred validators and start staking.

"},{"location":"basics/staking/different_ways_to_stake_fet/#how-to-stake-on-fetch-mainnet-using-cosmostation-mobile-wallets-applicable-for-users-holding-fet-on-binance","title":"How to stake on Fetch Mainnet using Cosmostation Mobile Wallets: applicable for users holding FET on Binance","text":"

Binance has completed the Fetch.ai (FET) mainnet integration. This means Binance has integrated Native FET tokens which can be staked easily on Cosmostation without relying on token bridge. Please follow the below steps:

  • Step 1: Please consider if you wish to stake FET using Cosmostation iOS or Android mobile apps

For Mobile users: iOS and Android.

  • Step 2: Create Cosmostation address for Fetch.ai chain. There is a detailed pdf from the Cosmostation team explaining how to create an account.

  • Step 3: Send a small amount of $FET from your Binance account to your generated cosmostation address. Make sure the addresses are correct and above all the chain chosen for your generated address is Fetch.ai chain on Cosmostation.

  • Step 4: If the tokens arrive, send the rest.

  • Step 5: Delegate to your preferred validators and start staking.

"},{"location":"basics/staking/different_ways_to_stake_fet/#stakingunstaking-on-fetch-mainnet-for-users-with-access-to-migrated-tokens","title":"STAKING/UNSTAKING ON FETCH MAINNET FOR USERS WITH ACCESS TO MIGRATED TOKENS","text":"

Our staking program has moved from Ethereum and we have successfully migrated all the tokens on September 15 \u2014 which you can access on our Fetch.ai browser wallet. If you want to access your migrated tokens, here is how you do it.

For those who staked on [staking.fetch.ai] using only Metamask \u2014 Here is the guide

For those who staked on [staking.fetch.ai] using ledger \u2014 Here is the guide (refer to Key Migration Desktop)

IMPORTANT: BE SURE TO CREATE YOUR COSMOSTATION ADDRESS ONLY FOR FETCH.AI CHAIN.

Official guide from Cosmostation team on how to generate address on Cosmostation Mobile \u2014 pdf

Official guide from Cosmostation team on how to generate address on Cosmostation Web Wallet \u2014 pdf

Here is the user journey below detailing how to stake on our Mainnet if you have your tokens on the Fetch browser wallet.

To unstake, use this guide which explains how to send tokens back to your metamask using the Fetch browser wallet.

If you need further clarification, please feel free to refer to the section Applicable for users holding FET on Binance below.

"},{"location":"basics/staking/different_ways_to_stake_fet/#stakingunstaking-on-fetch-mainnet-if-you-have-erc20-tokens-on-other-exchanges","title":"STAKING/UNSTAKING ON FETCH MAINNET IF YOU HAVE ERC20 TOKENS ON OTHER EXCHANGES","text":"

With the exception of Binance and HitBTC all other exchanges including Coinbase currently hold ERC20 FET. To stake on Mainnet, you must use our token bridge and metamask.

Please feel free to refer to the section Applicable for users on exchanges with ERC20 FET below.

To unstake, use this guide which explains how to send tokens back to your metamask using the Fetch browser wallet.

For Ledger users: Staking on Fetch Mainnet using Ledger and Cosmostation Web Wallet.

"},{"location":"basics/staking/different_ways_to_stake_fet/#how-to-stake-fet-using-ledger-on-cosmostation-web-wallet-applicable-for-users-on-exchanges-with-erc20-fet","title":"How to stake FET using Ledger on Cosmostation Web Wallet: applicable for users on exchanges with ERC20 FET","text":"

Disclaimer: The guide is a courtesy of Cros-Net who is a validator on Fetch.ai Mainnet.

What do I need:

  • Ledger Nano and a Desktop PC, as mobile devices are not yet supported.
  • Ledger Live software installed on your PC and an empty MetaMask wallet on your Brave/Chrome Browser. FET ERC20 tokens (except HitBTC all other exchanges
  • Head over here \u2014 Cosmostation web application (Cosmos Web Wallet) to stake the ERC20 Fetch coins.

This guide also assumes you have Fetch.AI tokens stored in your Ledger Nano Wallet ready to be staked.

STEP 1 (Install Ethereum and Cosmos Ledger apps)

After setting up your device with a PIN and passphrase, you should install both the Ethereum and Cosmos wallets through the Ledger Live app.

STEP 2 (Set specific Ethereum app settings correctly)

Open the Ethereum app on your Ledger wallet. Within the app, go to settings; make sure \"Contract Data\" is set to \"Allow contract data in transactions\". By default this is turned off, it must be turned on so signing the transaction later on won\u2019t fail. Now exit the app.

STEP 3 (Obtaining Fetch.ai address)

  1. Open Cosmos app on your Ledger.
  2. Via Brave/Chrome, go to Cosmos Web Wallet, Cosmostation: https://wallet.cosmostation.io/
  3. On the top right corner, drop down menu, make sure it is set to \u201cfetch.ai\u201d:
  4. Click \"Connect Wallet\". Click \"Connect to Ledger\"
  5. Your fetch.ai address will appear in the middle of the screen
  6. Make a note of this address as it will be needed later when staking
  7. Logout of Cosmostation website
  8. Close down the Cosmos App on Ledger Wallet and open up Ethereum App

STEP 4 (Set MetaMask bridge with Ledger Live Wallet)

  1. On your Brave/Chrome browser, open the MetaMask wallet. You will now link this wallet with your Ledger via a bridge. Make sure that MetaMask is set to Ethereum Mainnet.
  2. Open the MetaMask wallet and click on top right corner where it has your profile icon:
  3. Choose \"Connect Hardware Wallet\", a dialog will appear asking you to \"Open Ledger Live\"
  4. Click \"Open Ledger Live\". Ledger Live will then ask you to login (if not done so already). Ledger Live will then display \"Expose your device accounts through websocket\"
  5. Click Open to expose the device via web socket. When doing this for the first time, you will receive a message asking if you want to allow Ledger to make outside connections. Click OK on this.
  6. Once the connection is established and the bridge is set, Ledger Live will display \u201cEthereum bridge opened\u201d. Now we can communicate between the two wallets in this process.
  7. You will also notice that your MetaMask will now have two accounts on Ethereum Mainnet, one which is your original account that was setup with it. The second account is a hardware account linked to Ledger Wallet with \"Hardware\" displayed next to it.

STEP 5 (Visit the bridge to move coins onto Cosmostation)

To complete this phase, visit the bridge at https://token-bridge.fetch.ai/.

  • Make sure MetaMask is unlocked so it can be connected onto this bridge.
  • Make sure MetaMask is set to the HARDWARE wallet and that you have enough Ethereum in there to sign the transaction and pay the fees for transfer.

STEP 6 (Open Wallet and check bridge details)

The source address (Ethereum address of HARDWARE wallet) will be already filled in, and you will see a blank field for the Native address (this is your Fetch native destination address from STEP 3).

STEP 7 (Enter Fetch address details and make transfer)

Paste your address starting with \"fetch1\", that you identified in STEP 2 into the \"Native Address\" field, enter the amount, and then click the \"Transfer\" button. The Ethereum address is connected to the site through Metamask or other browser extension, and needs to be the source from which your tokens will move to Mainnet. The Fetch (Native) address is your destination address, to which they will move on main-net.

STEP 8 (Sign the transaction \u2014 first one)

This will trigger a transaction that will ask you to \"approve\" the bridge contract for holding your tokens. After signing the transaction in your Brave/Chrome browser, be sure to also sign it on your physical device (Ledger Nano), and it will be submitted to the Ethereum blockchain. Once this step is done, you can check your Ledger Live and see that the Fetch Tokens have transferred.

As the first transaction is signed, make sure the bridge between Ledger Nano and MetaMask is still running. This is not needed for the second signing below.

STEP 9 (Sign the transaction \u2014 second one)

To complete the tokens transfer, you will be asked to sign a second transaction. In case this step fails, retry the process again from Step 8 above and you will only need to sign once (since Fetch tokens would have been transferred onto Mainnet).

STEP 10 (Check tokens transferred to validator area)

After completing these steps, your Fetch address will be credited with tokens that you can delegate to a validator of your choice to start earning staking rewards. Login to Cosmostation as you did in Step 3. Your coins should be present at the centre of the dashboard.

STEP 11 (Delegating stake)

After completing the transfer onto Fetch AI Mainnet and confirming that the coins are now visible on your dashboard, it is time to stake the coins.

  1. Make sure Ledger is unlocked and Cosmos App is running.
  2. Click on Wallet and select \"Reward\" (see left hand side of screen)
  3. This will show a list of validators to delegate to. (In case you get an error when trying to open the Reward section, make sure the Cosmos App is unlocked and running on your Ledger).
  4. Validators are listed at the bottom of the screen. Choose a validator to delegate to by clicking on the \u201cDelegate\u201d button. A dialog will appear.
  5. Enter the amount of Fetch tokens you want to delegate and then click on the \u201cGenerate & Sign Transaction\u201d button.
  6. Confirm this on your Ledger wallet and your coins will be sent to the validator for staking.
  7. Repeat the process if you wish to delegate to other validators.
  8. As each validator is added, this will be displayed in the UI of the page.
"},{"location":"basics/staking/different_ways_to_stake_fet/#how-to-stake-on-fetch-mainnet-using-cosmostation-mobile-wallets-applicable-for-users-on-exchanges-with-erc20-fet","title":"How to stake on Fetch Mainnet using Cosmostation Mobile Wallets: applicable for users on exchanges with ERC20 FET","text":"

How do I stake on Mainnet 2.0 (ELI5 version) What you will need:

  • A Metamask extension for your browser
  • A Cosmostation Wallet (iOS and Android)

Steps:

  1. If you do not have the metamask extension installed on your browser. Download it and create an account. Never give out your metamask private key, never give out your mnemonics, and store your password safe.
  2. Set your metamask to receive FET. Click on add token and click on \"custom\" and add the ERC20 FET address 0xaea46A60368A7bD060eec7DF8CBa43b7EF41Ad85.
  3. To send your tokens from your exchange to Metamask \u2014 We will take Binance as an example here.

    • Go to Wallet/Overview FIAT and SPOT.
    • Withdraw your FET and ETH to your metamask account. You will need some ETH to pay for withdrawal. You can also buy ETH on Metamask directly. To withdraw your FET, copy and paste your Metamask address into the field. Always use the ERC20 network to send your FET.

    If you\u2019re on any other exchange, please send your tokens directly to Metamask.

  4. Then you wait for Metamask to receive your FET and ETH which may take a few minutes. If you are stuck, please refresh your page but be patient. If you start to get worried, head over to etherscan.io and check your tx hash. If it is successful, it is down to ETH network congestion but your tokens are on their way.

  5. Download the Cosmostation Wallet for your phone. Create an account on it. Remember to choose Fetch Mainnet when you\u2019re asked to choose a Cosmo network. Once again you will be asked to store your mnemonics so write them down and keep them somewhere safe.

  6. After you have successfully created your wallet on the Cosmostation wallet app, you can go to https://token-bridge.fetch.ai/. Ensure your metamask is connected to the token bridge. Once you have your tokens, connect your metamask with https://token-bridge.fetch.ai/. A new window will pop up and you will be prompted to approve the request to connect.

  7. Enter your metamask address in the Ethereum Address, your Cosmostation Wallet address in the Native Address field. Enter the amount of FET you wish to transfer, note that you must send a minimum of 100 FET across the bridge.

    Enter the amount \u2014 click transfer \u2014 pay for the first little transaction with ETH to approve \u2014 once the transaction is approved, a metamask pop-up should come asking to pay for the swap. Please pay the second transaction with ETH.

    If the first transaction succeeds but nothing comes on the page to pay the second transaction : just refresh the page, copy-paste your addresses again, enter again the amount, and you should see the \"swap\" button now.

  8. Once you have transferred and swapped on step \"g\", check your Cosmostation wallet and wait for a few minutes before the transferred tokens show up.

  9. Once they have arrived you can delegate them to a validator of your choice. Congratulations you have successfully staked on Fetch.ai Mainnet 2.0.

"},{"location":"basics/staking/how_to_stake/","title":"How to Stake","text":"

On this page, you can find instructions on how to stake FET, remove your stake, and claim your staking rewards.

"},{"location":"basics/staking/how_to_stake/#to-stake","title":"To Stake","text":"
  1. Ensure you are logged into your Fetch wallet.
  2. On the wallet dashboard, select Stake. You should be redirected to the ledger browser. Here you will find a list of every active validator with whom you can stake your FETs. You can also see the amount of FET staked to each validator and their commission rates.

    Info

    The validators on this page are ordered according to the number of FETs delegated to them and not their reputation or benefits. See choosing a validator for more details.

  3. Connect your wallet, if it is not yet connected, by pressing Connect Wallet at the top right.

  4. Choose a validator to stake your FETs with and hit Stake.

    Tip

    To see details of any validator, such as their voting power, self-bonded rate, uptime, active/inactive status at any given time, and contact information, head over to this page. For a visualization of the validators' voting power (more is NOT better) check out this page.

  5. In the pop-up, select the amount of FETs you want to delegate to this validator and press Stake.

    Tip

    Don't forget to leave some FETs undelegated, as some amount is necessary to pay for transaction fees when submitting any transaction to the main network. The fee is very minimal, but it is still important to make sure you have some FETs to pay for it.

  6. Your Fetch Wallet shows you a summary of the transaction. Review it, select a transaction fee, and if you are happy, hit Approve to complete the operation.

"},{"location":"basics/staking/how_to_stake/#to-claim-your-rewards","title":"To Claim your Rewards","text":""},{"location":"basics/staking/how_to_stake/#using-the-fetch-wallet","title":"Using the Fetch Wallet","text":"
  1. Ensure you are logged into your Fetch wallet.
  2. From the wallet dashboard select Claim.

    Info

    This will claim the total rewards accrued for your stakes across every validator.

  3. The wallet shows you a summary of the transaction. Review it, select a transaction fee, and if you are happy, hit Approve to complete the operation.

You should now see the rewards added to your Total Balance.

"},{"location":"basics/staking/how_to_stake/#using-the-staking-dashboard","title":"Using the Staking Dashboard","text":"
  1. Go to the ledger browser page.
  2. Connect your wallet, if it is not yet connected, by pressing Connect Wallet at the top right.
  3. Click on your wallet address at the top right of the page to go to your staking dashboard.
  4. In the Rewards section, click Claim Rewards for any validator to withdraw the rewards from your stakes with this particular validator.
  5. The wallet shows you a summary of the transaction. Review it, select a transaction fee, and if you are happy, hit Approve to complete the operation.

You should now see the rewards added to your Total Balance.

Info

Rewards are paid on a per-block basis and added to the existing pending rewards.

"},{"location":"basics/staking/how_to_stake/#to-remove-your-stake","title":"To Remove your Stake","text":"
  1. Go to the ledger browser page.
  2. Connect your wallet, if it is not yet connected, by pressing Connect Wallet at the top right.
  3. Click on your wallet address at the top right of the page to go to your staking dashboard.
  4. In the Delegations section, click Remove Stake for the validator you wish to remove your stakes from.
  5. In the pop-up, enter the amount of FETs you wish to remove from your stakes with this validator and click Remove Stake.
  6. The wallet shows you a summary of the transaction. Review it, select a transaction fee, and if you are happy, hit Approve to complete the operation.

Info

When you remove your stake, there is an unbonding (also known as, cooldown) period of 21 days. In your staking dashboard, the Unbonding Delegations section shows you the stakes you have removed which are now in the unbonding period. You can also see the amount of FET unbonded and the number of days remaining from the unbonding period. This is how long you need to wait before being able to withdraw the funds to your wallet.

"},{"location":"basics/staking/redelegation/","title":"Re-Delegation","text":"

Re-delegation means moving some or all of the stakes you delegated with one validator to another.

Note

To re-delegate, do not manually remove your stake from one validator and stake with another. This will trigger the unbonding period, causing you to miss out on staking rewards for 21 days while you wait for the unbonding period to end. Re-delegation, unlike manually removing and adding stake, is an instant process for moving some or all of your staked FETs from one validator to another.

"},{"location":"basics/staking/redelegation/#why-re-delegate","title":"Why Re-Delegate","text":"

There may be different reasons why you might choose to re-delegate and redistribute your stakes. Some examples are:

  • To increase the decentralization and therefore security of the network: For the Fetch network to be as secure as possible, there should not be a large concentration of stakes (e.g. more than 33% of the delegated FETs) staked within only a small number of (e.g. 10) validators. If you see this is currently the case and that you have also contributed to it by delegating your stakes with one of those validators, you may want to consider redistributing your stakes to some of the other validators.
  • To reduce your staking risks: Remember that when you delegate your tokens with a validator, just as you share the rewards for their contribution to the network's consensus protocol, you also share the punishment they would receive if they misbehave and act against the network's protocol. If this happens, your stake with them will be slashed. To reduce this risk, you may choose to re-delegate parts of your stakes to other validators to have a wider stake distribution.
"},{"location":"basics/staking/redelegation/#to-re-delegate-your-stake","title":"To Re-Delegate your Stake","text":"
  1. Go to the ledger browser page.
  2. Connect your wallet, if it is not yet connected, by pressing Connect Wallet at the top right.
  3. Click on your wallet address at the top right of the page to go to your staking dashboard.
  4. In the Delegations section, click Transfer Stake for the validator you wish to re-delegate some or all of your stakes from.
  5. In the pop-up, select a validator that you'd like to re-delegate your stakes to.

    Info

    The validators are not in any order on the drop-down list. To see the validators' details see validator voting power distribution and validator details.

  6. Enter the amount of FETs you wish to re-delegate to the new validator and click Transfer Stake.

  7. The wallet shows you a summary of the transaction. Review it, select a transaction fee, and if you are happy, hit Approve to complete the operation.
"},{"location":"basics/staking/redelegation/#choosing-a-validator","title":"Choosing a Validator","text":"

Choosing a validator to delegate your stake with is an important decision which ultimately impacts the network's security and performance. When you choose a validator, you are essentially casting a vote in the network indicating their trustworthiness, and that it is beneficial to have them participate in the maintenance of the network's operation.

When it comes to choosing a validator, it is up to you as the delegator to do your own research and due diligence to find out about their reputation and how well they have performed so far. Delegation is meant to be an active role between you and a validator.

To help you choose a suitable validator, here is a list of criteria to look for:

  • Do they have a website?
  • Do they have an active presence on social media (e.g. Twitter, Reddit, Instagram, ...)?
  • Are they easy to contact (e.g. on Discord, Telegram, ...)?
  • Do they have FET self-bonded to their node?
  • Are they active in the community?
  • Do they have a high uptime?
  • Do they offer slashing & double sign protection?
  • Do they participate and vote on proposals?
  • Do they have a mission or set of principles that align with yours?

Some of these questions can be answered via our Native Block Explorer or Mintscan. Some of the others on this list will require you to reach out to validators for answers, which is a good opportunity to see if they are active or not.

"},{"location":"basics/staking/what_is_staking/","title":"What is Staking?","text":"

Staking means locking up some of your tokens in order to participate in a blockchain network's operation, increase its security, and earn some passive rewards.

Fetch is a public, decentralized blockchain network which uses proof-of-stake (PoS) as its consensus protocol. The operators of PoS-based networks, known as validators, must lock up some tokens in exchange for the right to earn rewards from their work.

There are certain conditions for becoming a validator, such as staking a minimum token amount. Users with lower amounts of tokens could still participate by delegating stake to a particular validator to earn a share of the rewards that they receive. Note that the more tokens locked up, the more decision power a validator has and thus the higher the chances of them earning rewards.

"},{"location":"basics/staking/what_is_staking/#why-stake","title":"Why Stake?","text":""},{"location":"basics/staking/what_is_staking/#benefits-to-the-user","title":"Benefits to the User","text":"
  • Earn Rewards: Validators in the Fetch network participate in the maintenance of network's operation and earn rewards for their work. As someone who staked tokens with validators, you are eligible to earn a share of their rewards. Note that your stake could also be slashed in case a validator misbehaves and acts against the network's protocol. Therefore, you must do your own research when choosing validators to stake your tokens with. It is therefore often a good practice to distribute your tokens and stake with multiple validators.
  • Voting Rights: As a stakeholder, you will be eligible to vote on proposals for the future improvement of the network. The more you stake, the greater your influence and voting power.
"},{"location":"basics/staking/what_is_staking/#benefits-to-the-network","title":"Benefits to the Network","text":"
  • Decentralization and Security: The level of decentralization in any highly secure blockchain network is dependent on the number of validators involved in the consensus or block production. The larger the number of validators and the more distributed the total stakes, the more decentralized or safe the network is. As a result, it's always a good idea to distribute your stakes across several validators, not only to prevent staking risks but also to keep the network decentralized and secure.
  • Network improvements, DAO and decentralized decision-making: Having a DAO (Decentralized Autonomous Organization) make network enhancement decisions is an important aspect of any decentralized network. As a stakeholder, you have the right to vote on proposals that could be crucial for the network's improvement and use-cases in general. The greater the number of stakeholders, the more individuals vote on network upgrade proposals, resulting in highly decentralized decisions.
"},{"location":"basics/wallet/getting_started/","title":"Getting Started","text":"

The Fetch Wallet allows you to interact with the Fetch network via your browser.

"},{"location":"basics/wallet/getting_started/#compatibility","title":"Compatibility","text":"

The Fetch Wallet works on all Chromium-based web browsers, including Chrome, Brave, Edge and Decentr.

"},{"location":"basics/wallet/getting_started/#get-the-wallet","title":"Get the Wallet","text":"

Install the Fetch wallet from the Chrome web store.

Info

At this time, you cannot run the Keplr and Fetch wallets together because they interfere. Please disable the Keplr wallet before using the Fetch wallet.

"},{"location":"basics/wallet/getting_started/#set-up","title":"Set up","text":"

After opening the wallet for the first time, you will see the option to:

  • Create a new account
  • Import an existing account
  • Connect your ledger hardware wallet
"},{"location":"basics/wallet/getting_started/#to-create-a-new-account","title":"To Create a New Account","text":"
  • Select Create new account
  • Backup your mnemonic seed securely.

    Warning

    KEEP IT SAFE! Anyone with your mnemonic seed can access your wallet and take your assets.

    Danger

    DON'T LOSE IT! Lost mnemonic seed cannot be recovered! If you lose your mnemonic seed you will lose access to your wallet.

  • Give your account a name and set a password. This password will be used the next time you want to use the wallet or make important changes to your account.

  • Rearrange the mnemonic phrases by clicking on them in the correct order to confirm your mnemonic seed.
"},{"location":"basics/wallet/getting_started/#to-import-an-existing-account","title":"To Import an Existing Account","text":"

If you have an account on the Fetch network, for example having had one already on the Fetch wallet and want to access it again, have an account on another wallet (e.g. Cosmostation, Keplr, ...) and wish to bring it to the Fetch wallet, or having created an address using one of our tools (e.g. the AEA framework), you can import it into the Fetch wallet:

  • Select Import existing account
  • Enter your mnemonic seed (set of words) or private key (hexadecimal)

    Warning

    KEEP IT SAFE! Anyone with your mnemonic seed or private key can access your wallet and take your assets.

  • Give your account a name and set a password. This password will be used the next time you want to use the wallet or make important changes to your account.

"},{"location":"basics/wallet/getting_started/#to-use-ledger-hardware-wallet","title":"To Use Ledger Hardware Wallet","text":"

If you have a Ledger hardware wallet and wish to keep your key and mnemonics on that device while using the Fetch wallet:

Info

Currently only ledger hardware wallets are supported.

  • Select Import ledger
  • Give your account a name and set a password. This password will be used the next time you want to use the wallet or make important changes to your account.
  • Follow the instructions on the popup to connect your device.

Warning

Please ensure you keep your mnemonic seed somewhere safe where others cannot access it. If you lose it, your wallet will be inaccessible once you log out. The password for your account should also be kept safe but is not necessary for recovery if you have your mnemonic seed.

Info

If you lose your password, you need to uninstall and re-install the Fetch wallet and select Import existing account. Then use the mnemonic seed for your account and choose a new password.

"},{"location":"basics/wallet/how_to_use_wallet/","title":"How to Use the Wallet","text":""},{"location":"basics/wallet/how_to_use_wallet/#deposit-tokens","title":"Deposit Tokens","text":"

To transfer funds to your account on the Fetch wallet:

In the wallet or application you are using to send the funds, use your account's address as the destination account to which the funds must go.

"},{"location":"basics/wallet/how_to_use_wallet/#to-copy-your-accounts-address","title":"To Copy Your Account's Address","text":"
  1. Either click on the account address at the top of the dashboard (under the account name):
  2. Or select Deposit and scan the QR code.

Once you send the tokens, the balance should be updated.

Failure

If your origin wallet says that the address (which should start with \"fetch\") is invalid, it is probably expecting an Ethereum address (beginning with \"0x\") and is most likely trying to send ERC20 FET. In this case, you need to use the token bridge to swap your ERC20 FET for native FET.

Warning

You should not send ERC20 FET to this wallet. If you do, you will lose your tokens. The Fetch wallet can only hold native FET tokens and not ERC20 FET tokens.

"},{"location":"basics/wallet/how_to_use_wallet/#send-tokens","title":"Send Tokens","text":"

To send tokens from your account:

  1. Select Send.
  2. Fill in the details of your transaction:

    • Recipient: the address you want to send the tokens to
    • Token: the token denomination or type
    • Amount: the number of tokens you want to send with this transaction (you can see your current balance above the Amount)
    • Memo (Optional): some transactions (e.g. to/from some exchanges) require a specific memo. If not needed, you can leave it blank.
    • Fee: the transaction fee. Choose from Low, Average and High

    Tip

    Usually, the lower the transaction fee, the longer you need to wait for your transaction to be settled on the network.

  3. Press Send.

  4. In the summary screen, review the details and if everything is correct, select Approve.

Tip

You can check the status of your transaction via the explorer.

"},{"location":"colearn/","title":"Welcome to the Fetch.ai Collective Learning Library","text":"

Colearn is a library that enables privacy-preserving decentralized machine learning tasks on the FET network.

This blockchain-mediated collective learning system enables multiple stakeholders to build a shared machine learning model without needing to rely on a central authority, and without revealing their dataset to the other stakeholders. This library is currently in development.

"},{"location":"colearn/#how-collective-learning-works","title":"How collective learning works","text":"

A group of learners comes together, each of whom have their own datasets and want to collaborate on training a machine learning model over a set number of rounds. We refer to this as an 'experiment'. In each round of collective learning:

  1. One learner is selected to train the model and propose a new set of model weights.
  2. The other learners vote on whether the weights are an improvement.
  3. If the majority vote that the new weights are better than the old ones then the new weights are accepted by all the learners. Otherwise the new weights are discarded.
  4. The next round begins. For more information on the Collective Learning Protocol see here.
"},{"location":"colearn/#current-version","title":"Current Version","text":"

We have released v.0.2.8 of the Colearn Machine Learning Interface, the first version of an interface that allows developers to define their own model architectures that can then be used in collective learning. Together with the interface we provide a simple backend for local experiments. This is a prototype backend with upcoming blockchain ledger based backends to follow. Future releases will use similar interfaces so that learners built with the current system will work on a different backend that integrates a distributed ledger and provides other improvements. The current framework will then be used mainly for model development and debugging. We invite all users to experiment with the framework, develop their own models, and provide feedback!

"},{"location":"colearn/#getting-started","title":"Getting Started","text":"

To use the latest stable release we recommend installing the package from PyPi

To install with support for Keras and Pytorch:

pip install colearn[all]\n

To install with just support for Keras or Pytorch:

pip install colearn[keras]\npip install colearn[pytorch]\n

For more installation options or get the latest (development) version see Installation

Then run the standalone demo:

python -m colearn_examples.ml_interface.run_demo\n

For plenty of other examples see the Examples.

"},{"location":"colearn/#writing-your-own-models","title":"Writing your own models","text":"

We encourage users to try out the system by writing their own models. Models need to implement the collective learning interface, which provides functions for training and voting on updates. More instructions can be found in the Getting Started section.

"},{"location":"colearn/about/","title":"How collective learning works","text":"

A Colearn experiment begins when a group of entities, referred to as learners, decide on a model architecture and begin learning. Together they will train a single global model. The goal is to train a model that performs better than any of the learners can produce by training on their private data set.

"},{"location":"colearn/about/#how-training-works","title":"How Training Works","text":"

Training occurs in rounds; during each round the learners attempt to improve the performance of the global shared model. To do so each round an update of the global model (for example new set of weights in a neural network) is proposed. The learners then validate the update and decide if the new model is better than the current global model. If enough learners approve the update then the global model is updated. After an update is approved or rejected a new round begins.

The detailed steps of a round updating a global model M are as follows:

  1. One of the learners is selected and proposes a new updated model M'
  2. The rest of the learners validate M'
  3. If M' has better performance than M against their private data set then the learner votes to approve
  4. If not, the learner votes to reject
  5. The total votes are tallied
  6. If more than some threshold (typically 50%) of learners approve then M' becomes the new global model. If not, M continues to be the global model
  7. A new round begins.

By using a decentralized ledger (a blockchain) this learning process can be run in a completely decentralized, secure and auditable way. Further security can be provided by using differential privacy to avoid exposing your private data set when generating an update.

"},{"location":"colearn/about/#learning-algorithms-that-work-for-collective-learning","title":"Learning algorithms that work for collective learning","text":"

Collective learning is not just for neural networks; any learning algorithm that can be trained on subsets of the data and which can use the results of previous training rounds as the basis for subsequent rounds can be used. Neural networks fit both these constraints: training can be done on mini-batches of data and each training step uses the weights of the previous training step as its starting point. More generally, any model that is trained using mini-batch stochastic gradient descent is fine. Other algorithms can be made to work with collective learning as well. For example, a random forest can be trained iteratively by having each learner add new trees (see example in mli_random_forest_iris.py). For more discussion, see here.

"},{"location":"colearn/about/#the-driver","title":"The driver","text":"

The driver implements the voting protocol, so it handles selecting a learner to train, sending the update out for voting, calculating the vote and accepting or declining the update. Here we have a very minimal driver that doesn't use networking or a blockchain. Eventually the driver will be a smart contract. This is the code that implements one round of voting:

def run_one_round(round_index: int, learners: Sequence[MachineLearningInterface],\nvote_threshold=0.5):\nproposer = round_index % len(learners)\nnew_weights = learners[proposer].mli_propose_weights()\nprop_weights_list = [ln.mli_test_weights(new_weights) for ln in learners]\napproves = sum(1 if v.vote else 0 for v in prop_weights_list)\nvote = False\nif approves >= len(learners) * vote_threshold:\nvote = True\nfor j, learner in enumerate(learners):\nlearner.mli_accept_weights(prop_weights_list[j])\nreturn prop_weights_list, vote\n

The driver has a list of learners, and each round it selects one learner to be the proposer. The proposer does some training and proposes an updated set of weights. The driver then sends the proposed weights to each of the learners, and they each vote on whether this is an improvement. If the number of approving votes is greater than the vote threshold the proposed weights are accepted, and if not they're rejected.

"},{"location":"colearn/about/#the-machine-learning-interface","title":"The Machine Learning Interface","text":"
# ------------------------------------------------------------------------------\n#\n#   Copyright 2021 Fetch.AI Limited\n#\n#   Licensed under the Creative Commons Attribution-NonCommercial International\n#   License, Version 4.0 (the \"License\"); you may not use this file except in\n#   compliance with the License. You may obtain a copy of the License at\n#\n#       http://creativecommons.org/licenses/by-nc/4.0/legalcode\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n#\n# ------------------------------------------------------------------------------\nimport abc\nfrom enum import Enum\nfrom typing import Any, Optional\nimport onnx\nimport onnxmltools\nimport sklearn\nimport tensorflow as tf\nimport torch\nfrom pydantic import BaseModel\nfrom tensorflow import keras\nmodel_classes_keras = (tf.keras.Model, keras.Model, tf.estimator.Estimator)\nmodel_classes_scipy = (torch.nn.Module)\nmodel_classes_sklearn = (sklearn.base.ClassifierMixin)\ndef convert_model_to_onnx(model: Any):\n\"\"\"\n    Helper function to convert a ML model to onnx format\n    \"\"\"\nif isinstance(model, model_classes_keras):\nreturn onnxmltools.convert_keras(model)\nif isinstance(model, model_classes_sklearn):\nreturn onnxmltools.convert_sklearn(model)\nif 'xgboost' in model.__repr__():\nreturn onnxmltools.convert_sklearn(model)\nif isinstance(model, model_classes_scipy):\nraise Exception(\"Pytorch models not yet supported to onnx\")\nelse:\nraise Exception(\"Attempt to convert unsupported model to onnx: {model}\")\nclass DiffPrivBudget(BaseModel):\ntarget_epsilon: float\ntarget_delta: float\nconsumed_epsilon: float\nconsumed_delta: float\nclass ErrorCodes(Enum):\nDP_BUDGET_EXCEEDED = 1\nclass TrainingSummary(BaseModel):\ndp_budget: Optional[DiffPrivBudget]\nerror_code: Optional[ErrorCodes]\nclass Weights(BaseModel):\nweights: Any\ntraining_summary: Optional[TrainingSummary]\nclass DiffPrivConfig(BaseModel):\ntarget_epsilon: float\ntarget_delta: float\nmax_grad_norm: float\nnoise_multiplier: float\nclass ProposedWeights(BaseModel):\nweights: Weights\nvote_score: float\ntest_score: float\nvote: Optional[bool]\nclass ModelFormat(Enum):\nPICKLE_WEIGHTS_ONLY = 1\nONNX = 2\nclass ColearnModel(BaseModel):\nmodel_format: ModelFormat\nmodel_file: Optional[str]\nmodel: Optional[Any]\ndef deser_model(model: Any) -> onnx.ModelProto:\n\"\"\"\n    Helper function to recover a onnx model from its deserialized form\n    \"\"\"\nreturn onnx.load_model_from_string(model)\nclass MachineLearningInterface(abc.ABC):\n@abc.abstractmethod\ndef mli_propose_weights(self) -> Weights:\n\"\"\"\n        Trains the model. Returns new weights. Does not change the current weights of the model.\n        \"\"\"\npass\n@abc.abstractmethod\ndef mli_test_weights(self, weights: Weights) -> ProposedWeights:\n\"\"\"\n        Tests the proposed weights and fills in the rest of the fields\n        \"\"\"\n@abc.abstractmethod\ndef mli_accept_weights(self, weights: Weights):\n\"\"\"\n        Updates the model with the proposed set of weights\n        :param weights: The new weights\n        \"\"\"\npass\n@abc.abstractmethod\ndef mli_get_current_weights(self) -> Weights:\n\"\"\"\n        Returns the current weights of the model\n        \"\"\"\npass\n@abc.abstractmethod\ndef mli_get_current_model(self) -> ColearnModel:\n\"\"\"\n        Returns the current model\n        \"\"\"\npass \n

There are four methods that need to be implemented:

  1. propose_weights causes the model to do some training and then return a new set of weights that are proposed to the other learners. This method shouldn't change the current weights of the model - that only happens when accept_weights is called.
  2. test_weights - the models takes some new weights and returns a vote on whether the new weights are an improvement. As with propose_weights, this shouldn't change the current weights of the model - that only happens when accept_weights is called.
  3. accept_weights - the models accepts some weights that have been voted on and approved by the set of learners. The old weights of the model are discarded and replaced by the new weights.
  4. current_weights should return the current weights of the model.

For more details about directly implementing the machine learning interface see the tutorial here

"},{"location":"colearn/demo/","title":"How to run the demo","text":"

You can try collective learning for yourself using the simple demo in run_demo. This demo creates n learners for one of six learning tasks and co-ordinates the collective learning between them.

There are six potential models for the demo

  • KERAS_MNIST is the Tensorflow implementation of a small model for the standard handwritten digits recognition dataset
  • KERAS_MNIST_RESNET is the Tensorflow implementation of a Resnet model for the standard handwritten digits recognition dataset
  • KERAS_CIFAR10 is the Tensorflow implementation of the classical image recognition dataset
  • PYTORCH_XRAY is Pytorch implementation of a binary classification task that requires predicting pneumonia from images of chest X-rays. The data need to be downloaded from Kaggle
  • PYTORCH_COVID_XRAY is Pytorch implementation of a 3 class classification task that requires predicting no finding, covid or pneumonia from images of chest X-rays. This dataset is not currently publicly available.
  • FRAUD The fraud dataset consists of information about credit card transactions, and the task is to predict whether transactions are fraudulent or not. The data need to be downloaded from Kaggle

Use the -h flag to see the options:

python -m colearn_examples.ml_interface.run_demo -h\n

Arguments to run the demo:

--data_dir:       Directory containing training data, not required for MNIST and CIFAR10\n--test_dir:       Optional directory containing test data. A fraction of the training set will be used as a test set when not specified\n--model:          Model to train, options are KERAS_MNIST KERAS_MNIST_RESNET KERAS_CIFAR10 PYTORCH_XRAY PYTORCH_COVID_XRAY FRAUD\n--n_learners:     Number of individual learners\n--n_rounds:       Number of training rounds\n--vote_threshold: Minimum fraction of positive votes to accept the new model\n--train_ratio:    Fraction of training dataset to be used as test-set when no test-set is specified\n--seed:           Seed for initialising model and shuffling datasets\n--learning_rate:  Learning rate for optimiser\n--batch_size:     Size of training batch\n
"},{"location":"colearn/demo/#running-mnist","title":"Running MNIST","text":"

The simplest task to run is MNIST because the data are downloaded automatically from tensorflow_datasets. The command below runs the MNIST task with five learners for 15 rounds.

python -m colearn_examples.ml_interface.run_demo --model KERAS_MNIST --n_learners 5 --n_rounds 15\n

You should see a graph of the vote score and the test score (the score used here is categorical accuracy). The new model is accepted if the fraction of positive votes (green colour) is higher than 0.5. The new model is rejected if the fraction of negative votes (red color) is lower than 0.5.

As you can see, there are five learners, and initially they perform poorly. In round one, learner 0 is selected to propose a new set of weights.

"},{"location":"colearn/demo/#other-datasets","title":"Other datasets","text":"

To run the CIFAR10 dataset:

python -m colearn_examples.ml_interface.run_demo --model KERAS_CIFAR10 --n_learners 5 --n_rounds 15\n

The Fraud and X-ray datasets need to be downloaded from kaggle (this requires a kaggle account). To run the fraud dataset:

python -m colearn_examples.ml_interface.run_demo --model FRAUD --n_learners 5 --n_rounds 15 --data_dir ./data/fraud\n

To run the X-ray dataset:

python -m colearn_examples.ml_interface.run_demo --model PYTORCH_XRAY --n_learners 5 --n_rounds 15 --data_dir ./data/xray\n
"},{"location":"colearn/dev_notes/","title":"Developer Notes","text":"

These are some notes for developers working on the colearn code repo

"},{"location":"colearn/dev_notes/#google-cloud-storage","title":"Google Cloud Storage","text":"

To have access to the google cloud storage you need to set up your google authentication and have the $GOOGLE_APPLICATION_CREDENTIALS set up correctly. For more details ask or see the contract-learn documentation

"},{"location":"colearn/dev_notes/#build-image","title":"Build image","text":"

To build ML server image and push to google cloud use the following command:

cd docker\npython3 ./build.py --publish --allow_dirty\n# Check this worked correctly\ndocker images\n
"},{"location":"colearn/differential_privacy/","title":"What is differential privacy?","text":"

To make a machine learning system that protects privacy we first need to have a definition of what privacy is. Differential privacy (DP) is one such definition. First we need to have three concepts: the database is a collection of data about individuals (for example, their medical records), and we want to make a query about that data (for example \"How much does smoking increase someone's risk of cancer?\"). DP says that privacy is preserved if the result of the query cannot be used to determine if any particular individual is present in the database.

So if person A has their medical data in a database, and the query that we want to make on that database is \"How much does smoking increase someone's risk of cancer\" then the result of that query shouldn't disclose whether or not person A's details are in the database.

From this comes the idea of sensitivity of a query. The sensitivity of a query determines how much the result of the query depends on an individual's data. For example, the query \"How much does smoking increase the risk of cancer for adults in the UK?\" is less sensitive than the query \"How much does smoking increase the risk of cancer for men aged 50-55 in Cambridge?\" because the second query uses a smaller set of individuals.

"},{"location":"colearn/differential_privacy/#epsilon-differential-privacy","title":"Epsilon-differential privacy","text":"

EDP is a scheme for preserving differential privacy. In EDP all queries have random noise added to them, so they are no longer deterministic. So if the query was \"What fraction of people in the database are male\", and the true result is 0.5 then the results of calling this query three times might be 0.53, 0.49 and 0.51. This makes it harder to tell if an individual's data is in the database, because the effect of adding a person can't be distinguished from the effect of the random noise. Intuitively this is a bit like blurring an image: adding noise obscures personal information. The amount of personal information that is revealed isn't zero, but it is guaranteed to be below a certain threshold.

The level of privacy that is provided is controlled by the parameter epsilon; the greater epsilon is the more noise is added and the more privacy is preserved. Queries that are more sensitive have more noise added, because they reveal more information about individuals. It is important to add as little noise as possible, because adding more noise obscures the patterns that you want to extract from the data.

"},{"location":"colearn/differential_privacy/#differential-privacy-when-training-neural-networks","title":"Differential privacy when training neural networks","text":"

Each training step for a neural network can be though of as a complicated query on a database of training data. Differential privacy mechanisms tell you how much noise you need to add to guarantee a certain level of privacy. The opacus and tensorflow-privacy libraries implement epsilon-differential privacy for training neural networks for pytorch and keras respectively.

"},{"location":"colearn/differential_privacy/#how-to-use-differential-privacy-with-colearn","title":"How to use differential privacy with colearn","text":"

By using opacus and tensorflow-privacy we can make collective learning use differential privacy. The learner that is proposing weights does so using a DP-enabled optimiser.

To see an example of using this see dp_pytorch and dp_keras.

"},{"location":"colearn/examples/","title":"Examples that use Collective Learning","text":"

This is a list of examples that we've implemented to show you how to use Collective Learning locally. See and example of the gRPC server for the next step towards decentralized Colearn.

"},{"location":"colearn/examples/#mnist","title":"Mnist","text":"

Uses the standard Mnist database of handwritten images

  • mnist_keras. Uses the KerasLearner helper class. Discussed in more detail here.
  • mnist_pytorch. Uses the PytorchLearner helper class. Discussed in more detail here.
"},{"location":"colearn/examples/#fraud","title":"Fraud","text":"

The fraud dataset consists of information about credit card transactions. The task is to predict whether transactions are fraudulent or not. The data needs to be downloaded from Kaggle, and the data directory passed in with the flag --data_dir.

  • fraud_mli. Uses the MachineLearningInterface directly and detects fraud in bank transactions.
  • fraud_keras. Loads data from numpy arrays and uses KerasLearner.
"},{"location":"colearn/examples/#cifar10","title":"Cifar10","text":"

Uses the standard Cifar10 database of images

  • cifar_keras. Uses the KerasLearner helper class.
  • cifar_pytorch. Uses the PytorchLearner helper class.
"},{"location":"colearn/examples/#xray","title":"Xray","text":"

A binary classification task that requires predicting pneumonia from images of chest X-rays. The data need to be downloaded from Kaggle, and the data directory passed in with the flag --data_dir

  • xray_keras. Uses the KerasLearner helper class.
  • xray_pytorch. Uses the PytorchLearner helper class.
"},{"location":"colearn/examples/#iris","title":"Iris","text":"

Uses the standard Iris dataset. The aim of this task is to classify examples into one of three iris species based on measurements of the flower.

  • iris_random_forest. Uses the MachineLearningInterface directly and a random forest for classification.
"},{"location":"colearn/grpc_examples/","title":"Mnist gRPC Example","text":"

To run the Keras Mnist gRPC example run:

python -m colearn_examples.grpc.run_grpc_demo --n_learners 5 --dataloader_tag KERAS_MNIST --model_tag KERAS_MNIST \\\n--data_locations /tmp/mnist/0,/tmp/mnist/1,/tmp/mnist/2,/tmp/mnist/3,/tmp/mnist/4\n

Note

This requires colearn[keras]

You can verify that the example is working correctly by running the probe:

python -m colearn_grpc.scripts.probe_grpc_server --port 9995\n

For more about the gRPC components of Colearn see the gRPC Tutorial

"},{"location":"colearn/grpc_tutorial/","title":"gRPC tutorial","text":"

This tutorial explains how to set up the gRPC learner server. It assumes that you can already run colearn locally, and that you have already defined your own models and dataloaders (if you're going to do so). If you haven't done this then see the tutorials in the Getting Started section.

"},{"location":"colearn/grpc_tutorial/#architecture-of-colearn","title":"Architecture of colearn","text":"

There are two main parts to a collective learning system: the learner and the backend. The backend controls the learner, and manages the smart contracts and IPFS, and acts as a control hub for all the associated learners. The learner is the part that executes machine learning code. This consists of proposing, evaluating and accepting new weights as detailed in the Machine Learning Interface. The learner and the backend communicate via gRPC; the learner runs a gRPC server, and the backend runs a gRPC client that makes requests of the learner. This separation means that the learner can run on specialised hardware (e.g. a compute server) and does not need to be co-located with the backend.

"},{"location":"colearn/grpc_tutorial/#architecture-of-grpc-server","title":"Architecture of gRPC server","text":"

The gRPC interface is defined in colearn_grpc/proto/interface.proto. This defines the functions that the gRPC server exposes and the format for messages between the server and the client.

As we covered in the earlier tutorials, the machine learning part of colearn is contained inside the MachineLearningInterface (MLI). To recap: the MLI provides methods for proposing, evaluating and accepting weights. If you want to use your own models with colearn then you need to write an object that implements the MLI (for example, an instance of a python class that inherits from MachineLearningInterface). For more about the MLI see the MLI tutorial.

The gRPC server has an MLI factory, and it uses its MLI factory to make objects that implement the MachineLearningInterface. The MLI factory needs to implement the MLI factory interface. You could write your own MLI factory, but it's easier to use the one we provide. Below we will discuss the MLI factory interface and then talk about how to use the example factory.

"},{"location":"colearn/grpc_tutorial/#mli-factory-interface","title":"MLI Factory interface","text":"

The MLI Factory (as the name suggests) is a factory class for creating objects that implement the machine learning interface:

# ------------------------------------------------------------------------------\n#\n#   Copyright 2021 Fetch.AI Limited\n#\n#   Licensed under the Creative Commons Attribution-NonCommercial International\n#   License, Version 4.0 (the \"License\"); you may not use this file except in\n#   compliance with the License. You may obtain a copy of the License at\n#\n#       http://creativecommons.org/licenses/by-nc/4.0/legalcode\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n#\n# ------------------------------------------------------------------------------\nimport abc\nfrom typing import Dict, Set, Any\nimport os.path\nfrom pkg_resources import get_distribution, DistributionNotFound\nfrom colearn.ml_interface import MachineLearningInterface\nclass MliFactory(abc.ABC):\n\"\"\"\n    Interface a class must implement to be used as a factory by the GRPC Server\n    \"\"\"\n_version = \"0.0.0\"\n# https://stackoverflow.com/questions/17583443\ntry:\n_dist = get_distribution('colearn')\n# Normalize case for Windows systems\ndist_loc = os.path.normcase(_dist.location)\nhere = os.path.normcase(__file__)\nif not here.startswith(os.path.join(dist_loc, 'colearn')):\n# not installed, but there is another version that *is*\nraise DistributionNotFound\nexcept DistributionNotFound:\npass\nelse:\n_version = _dist.version\ndef get_version(self) -> str:\n\"\"\"\n        Returns the version of this library....\n        \"\"\"\nreturn self._version\n@abc.abstractmethod\ndef get_models(self) -> Dict[str, Dict[str, Any]]:\n\"\"\"\n        Returns the models this factory produces.\n        The key is the name of the model and the values are their default parameters\n        \"\"\"\npass\n@abc.abstractmethod\ndef get_dataloaders(self) -> Dict[str, Dict[str, Any]]:\n\"\"\"\n        Returns the dataloaders this factory produces.\n        The key is the name of the dataloader and the values are their default parameters\n        \"\"\"\npass\n@abc.abstractmethod\ndef get_compatibilities(self) -> Dict[str, Set[str]]:\n\"\"\"\n        A model is compatible with a dataloader if they can be used together to\n        construct a MachineLearningInterface with the get_MLI function.\n        Returns a dictionary that defines which model is compatible\n        with which dataloader.\n        \"\"\"\npass\n@abc.abstractmethod\ndef get_mli(self,\nmodel_name: str, model_params: str,\ndataloader_name: str, dataset_params: str) -> MachineLearningInterface:\n\"\"\"\n        @param model_name: name of a model, must be in the set return by get_models\n        @param model_params: user defined parameters for the model\n        @param dataloader_name: name of a dataloader to be used:\n            - must be in the set returned by get_dataloaders\n            - must be compatible with model_name as defined by get_compatibilities\n        @param dataset_params: user defined parameters for the dataset\n        @return: Instance of MachineLearningInterface\n        Constructs an object that implements MachineLearningInterface whose\n        underlying model is model_name and dataset is loaded by dataloader_name.\n        \"\"\"\npass \n

The MLI Factory stores the constructors for dataloaders and models and also a list of the dataloaders that are compatible with each model. Each constructor is stored under a specific name. For example, \"KERAS_MNIST_MODEL\" is the model for keras mnist. The gRPC server uses the MLI factory to construct MLI objects. The MLI Factory needs to implement four methods:

  • get_models - returns the names of the models that are registered with the factory and their parameters.
  • get_dataloaders - returns the names of the dataloaders that are registered with the factory and their parameters.
  • get_compatibilities - returns a list of dataloaders for each model that can be used with that model.
  • get_mli - takes the name and parameters for the model and dataloader and constructs the MLI object. Returns the MLI object.
"},{"location":"colearn/grpc_tutorial/#using-the-example-mli-factory","title":"Using the example MLI Factory","text":"

The example MLI factory is defined in colearn_grpc/example_mli_factory.py. It stores the models and dataloaders that it knows about in factoryRegistry.py To add a new model and dataloader to the factory you need to do the following things:

  1. Define a function that loads the dataset given the location of the dataset.
  2. Define a function that takes in the dataset and loads the MLI model.
  3. Register both these functions with the factory registry.

Registering a dataloader looks like this:

@FactoryRegistry.register_dataloader(dataloader_tag)\ndef prepare_data_loaders(location: str,\ntrain_ratio: float = 0.9,\nbatch_size: int = 32) -> Tuple[PrefetchDataset, PrefetchDataset]:\n

Registering a model is similar, but you additionally have to specify the dataloaders that this model is compatible with.

@FactoryRegistry.register_model_architecture(model_tag, [dataloader_tag])\ndef prepare_learner(data_loaders: Tuple[PrefetchDataset, PrefetchDataset],\nsteps_per_epoch: int = 100,\nvote_batches: int = 10,\nlearning_rate: float = 0.001\n) -> KerasLearner:\n

You can see an example of how to do this in colearn_examples/grpc/mnist_grpc.py. The FactoryRegistry decorators get evaluated when the functions are imported, so ensure that the functions are imported before constructing the gRPC server (more on that later).

Constraints on the dataloader function:

  1. The first parameter should be a mandatory parameter called \"location\" which stores the location of the dataset.
  2. The subsequent parameters should have default arguments.
  3. The return type should be specified with a type annotation, and this should be the same type that is expected by the model functions that use this dataloader.
  4. The arguments that you pass to the dataloader function must be JSON-encodable. Native python types are fine (e.g. str, dict, list, float).

Constraints on the model function:

  1. The first parameter should be a mandatory parameter called \"data_loaders\". This must have the same type as the return type of the compatible dataloaders.
  2. The subsequent parameters should have default arguments.
  3. The return type of model_function should be MachineLearningInterface or a subclass of it (e.g. KerasLearner).
  4. The dataloaders listed as being compatible with the model should already be registered with FactoryRegistry before the model is registered.
  5. The arguments that you pass to the model function must be JSON-encodable. Native python types are fine (e.g. str, dict, list, float).
"},{"location":"colearn/grpc_tutorial/#making-it-all-work-together","title":"Making it all work together","text":"

It can be challenging to ensure that all the parts talk to each other, so we have provided some examples and helper scripts. It is recommended to first make an all-in-one script following the example of colearn_examples/grpc/mnist_grpc.py. Once this is working you can run colearn_grpc/scripts/run_n_servers.py or colearn_grpc/scripts/run_grpc_server.py to run the server(s). The script colearn_grpc/scripts/probe_grpc_server.py will connect to a gRPC server and print the dataloaders and models that are registered on it (pass in the address as a parameter). The client side of the gRPC communication can then be run using colearn_examples/grpc/run_grpc_demo.py. More details are given below.

A note about running tensorflow in multiple processes: on a system with a GPU, tensorflow will try to get all the GPU memory when it starts up. This means that running tensorflow in multiple processes on the same machine will fail. To prevent this happening, tensorflow should be told to use only the CPU by setting the environment variable CUDA_VISIBLE_DEVIES to -1. This can be done in a python script (before importing tensorflow) by using:

import os\nos.environ[\"CUDA_VISIBLE_DEVICES\"] = \"-1\"\n
"},{"location":"colearn/grpc_tutorial/#testing-locally-with-an-all-in-one-script","title":"Testing locally with an all-in-one script","text":"

You can test this locally by following the example in colearn_examples/grpc/mnist_grpc.py. Define your dataloader and model functions as specified above, and register them with the factory. Then create n_learners gRPC servers:

n_learners = 5\nfirst_server_port = 9995\n# make n servers\nfor i in range(n_learners):\nport = first_server_port + i\nserver = GRPCServer(mli_factory=ExampleMliFactory(),\nport=port)\nserver_process = Process(target=server.run)\nserver_process.start()\n

And then create n_learners gRPC clients:

all_learner_models = []\nfor i in range(n_learners):\nport = first_server_port + i\nml_system = ExampleGRPCLearnerClient(f\"client {i}\", f\"127.0.0.1:{port}\")\nml_system.start()\ndataloader_params = {\"location\": data_folders[i]}\nml_system.setup_ml(dataset_loader_name=dataloader_tag,\ndataset_loader_parameters=json.dumps(dataloader_params),\nmodel_arch_name=model_tag,\nmodel_parameters=json.dumps({}))\nall_learner_models.append(ml_system)\n

ExampleGRPCLearnerClient inherits from the MachineLearningInterface so you can use it with the training functions as before:

for round_index in range(n_rounds):\nresults.data.append(\ncollective_learning_round(all_learner_models,\nvote_threshold, round_index)\n)\n
"},{"location":"colearn/grpc_tutorial/#testing-remotely","title":"Testing remotely","text":"

We expect that the gRPC learner part will often be on a compute cluster and be separate from the gRPC client side. To test the gRPC in a setup like this you can start the servers on the computer side and the client part separately. For one gRPC server:

python3 ./colearn_grpc/scripts/run_grpc_server.py --port 9995 --metrics_port 9091\n

For multiple gRPC servers:

python3 ./colearn_grpc/scrips/run_n_grpc_servers.py --n_learners 5 --port 9995 --metrics_port 9091\n

The servers by default will start on port 9995 and use subsequent ports from there, so if three servers are required they will run on ports 9995, 9996 and 9997.

If you have written your own dataloaders and models then you need to make sure that those functions are defined or imported before the server is created. These are the imports of the default dataloaders and models in colearn_grpc/scripts/run_grpc_server.py:

# These are imported so that they are registered in the FactoryRegistry\nimport colearn_keras.keras_mnist\nimport colearn_keras.keras_cifar10\nimport colearn_pytorch.pytorch_xray\nimport colearn_pytorch.pytorch_covid_xray\nimport colearn_other.fraud_dataset\n

Once the gRPC server(s) are running, set up whatever networking and port forwarding is required. You can check that the gRPC server is accessible by using the probe script:

python3 ./colearn_grpc/scripts/probe_grpc_server.py --port 9995\n

If the connection is successful this will print a list of the models and datasets registered on the server. These are the defaults that are registered:

info: Attempt number 0 to connect to 127.0.0.1:9995\ninfo: Successfully connected to 127.0.0.1:9995!\n{'compatibilities': {'FRAUD': ['FRAUD'],\n                     'KERAS_CIFAR10': ['KERAS_CIFAR10'],\n                     'KERAS_MNIST': ['KERAS_MNIST'],\n                     'KERAS_MNIST_RESNET': ['KERAS_MNIST'],\n                     'PYTORCH_COVID_XRAY': ['PYTORCH_COVID_XRAY'],\n                     'PYTORCH_XRAY': ['PYTORCH_XRAY']},\n 'data_loaders': {'FRAUD': '{\"train_ratio\": 0.8}',\n                  'KERAS_CIFAR10': '{\"train_ratio\": 0.9, \"batch_size\": 32}',\n                  'KERAS_MNIST': '{\"train_ratio\": 0.9, \"batch_size\": 32}',\n                  'PYTORCH_COVID_XRAY': '{\"train_ratio\": 0.8, \"batch_size\": 8, '\n                                        '\"no_cuda\": false}',\n                  'PYTORCH_XRAY': '{\"test_location\": null, \"train_ratio\": 0.96, '\n                                  '\"batch_size\": 8, \"no_cuda\": false}'},\n 'model_architectures': {'FRAUD': '{}',\n                         'KERAS_CIFAR10': '{\"steps_per_epoch\": 100, '\n                                          '\"vote_batches\": 10, '\n                                          '\"learning_rate\": 0.001}',\n                         'KERAS_MNIST': '{\"steps_per_epoch\": 100, '\n                                        '\"vote_batches\": 10, \"learning_rate\": '\n                                        '0.001}',\n                         'KERAS_MNIST_RESNET': '{\"steps_per_epoch\": 100, '\n                                               '\"vote_batches\": 10, '\n                                               '\"learning_rate\": 0.001}',\n                         'PYTORCH_COVID_XRAY': '{\"learning_rate\": 0.001, '\n                                               '\"steps_per_epoch\": 40, '\n                                               '\"vote_batches\": 10, \"no_cuda\": '\n                                               'false, \"vote_on_accuracy\": '\n                                               'true}',\n                         'PYTORCH_XRAY': '{\"learning_rate\": 0.001, '\n                                         '\"steps_per_epoch\": 40, '\n                                         '\"vote_batches\": 10, \"no_cuda\": '\n                                         'false, \"vote_on_accuracy\": true}'}}\n

Then run python -m colearn_examples.grpc.run_grpc_demo on the other side to run the usual demo. The script takes as arguments the model name and dataset name that should be run, along with the number of learners and the data location for each learner.

python -m colearn_examples.grpc.run_grpc_demo --n_learners 5 --dataloader_tag KERAS_MNIST --model_tag KERAS_MNIST \\\n--data_locations /tmp/mnist/0,/tmp/mnist/1,/tmp/mnist/2,/tmp/mnist/3,/tmp/mnist/4\n
"},{"location":"colearn/grpc_tutorial/#using-the-mli-factory-interface","title":"Using the MLI Factory interface","text":"

An alternative method of using your own dataloaders and models with the gRPC server is to use the MLI Factory interface. This is defined in colearn_grpc/mli_factory_interface.py. An example is given in colearn_examples/grpc/mlifactory_grpc_mnist.py. The MLI Factory is implemented as shown:

dataloader_tag = \"KERAS_MNIST_EXAMPLE_DATALOADER\"\nmodel_tag = \"KERAS_MNIST_EXAMPLE_MODEL\"\nclass SimpleFactory(MliFactory):\ndef get_dataloaders(self) -> Dict[str, Dict[str, Any]]:\nreturn {dataloader_tag: dict(train_ratio=0.9,\nbatch_size=32)}\ndef get_models(self) -> Dict[str, Dict[str, Any]]:\nreturn {model_tag: dict(steps_per_epoch=100,\nvote_batches=10,\nlearning_rate=0.001)}\ndef get_compatibilities(self) -> Dict[str, Set[str]]:\nreturn {model_tag: {dataloader_tag}}\ndef get_mli(self, model_name: str, model_params: str, dataloader_name: str,\ndataset_params: str) -> MachineLearningInterface:\ndataloader_params = json.loads(dataset_params)\ndata_loaders = prepare_data_loaders(**dataloader_params)\nmodel_params = json.loads(model_params)\nmli_model = prepare_learner(data_loaders=data_loaders, **model_params)\nreturn mli_model\n

An instance of the SimpleFactory class needs to be passed to the gRPC server on creation:

n_learners = 5\nfirst_server_port = 9995\n# make n servers\nserver_processes = []\nfor i in range(n_learners):\nport = first_server_port + i\nserver = GRPCServer(mli_factory=SimpleFactory(),\nport=port)\nserver_process = Process(target=server.run)\nprint(\"starting server\", i)\nserver_process.start()\nserver_processes.append(server_process)\n

The rest of the example follows the grpc_mnist.py example.

"},{"location":"colearn/installation/","title":"Installation","text":"

The core package, colearn, contains only the MachineLearningInterface and a simple driver that implements the Collective Learning Protocol. To install only the core package:

pip install colearn\n

To make collective learning easier to use we have defined extra packages with helpers for model development in Keras and Pytorch.

To install with Keras/Pytorch extras:

pip install colearn[keras]\npip install colearn[pytorch]\n

To install both the Keras and Pytorch extras use:

pip install colearn[all]\n

To run stand-alone examples:

 python -m colearn_examples.ml_interface.run_demo\n

For more examples see the Examples Page

"},{"location":"colearn/installation/#installing-from-source","title":"Installing From Source","text":"

Alternatively, to install the latest code from the repo:

  1. Download the source code from github:
git clone https://github.com/fetchai/colearn.git && cd colearn\n
  1. Create and launch a clean virtual environment with Python 3.7. (This library has currently only been tested with Python 3.7).
pipenv --python 3.7 && pipenv shell\n
  1. Install the package from source:

    pip install -e .[all]\n
  2. Run one of the examples:

    python colearn_examples/ml_interface/pytorch_mnist.py\n

If you are developing the colearn library then install it in editable mode so that new changes are effective immediately:

pip install -e .[all]\n
"},{"location":"colearn/installation/#running-the-tests","title":"Running the tests","text":"

Tests can be run with:

tox\n
"},{"location":"colearn/installation/#documentation","title":"Documentation","text":"

To run the documentation, first install mkdocs and plugins:

pip install .[docs] 

Then run:

mkdocs serve\n
"},{"location":"colearn/intro_tutorial_keras/","title":"Using collective learning with keras","text":"

This tutorial is a simple guide to trying out the collective learning protocol with your own machine learning code. Everything runs locally.

The most flexible way to use the collective learning backends is to make a class that implements the Collective Learning MachineLearningInterface defined in ml_interface.py. For more details on how to use the MachineLearningInterface see here

However, the simpler way is to use one of the helper classes that we have provided that implement most of the interface for popular ML libraries. In this tutorial we are going to walk through using the KerasLearner. First we are going to define the model architecture, then we are going to load the data and configure the model, and then we will run Collective Learning.

A standard script for machine learning with Keras looks like the one below

# ------------------------------------------------------------------------------\n#\n#   Copyright 2021 Fetch.AI Limited\n#\n#   Licensed under the Creative Commons Attribution-NonCommercial International\n#   License, Version 4.0 (the \"License\"); you may not use this file except in\n#   compliance with the License. You may obtain a copy of the License at\n#\n#       http://creativecommons.org/licenses/by-nc/4.0/legalcode\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n#\n# ------------------------------------------------------------------------------\nimport tensorflow as tf\nimport tensorflow_datasets as tfds\nfrom colearn_keras.utils import normalize_img\nn_rounds = 20\nwidth = 28\nheight = 28\nn_classes = 10\nl_rate = 0.001\nbatch_size = 64\n# Load the data\ntrain_dataset, info = tfds.load('mnist', split='train', as_supervised=True, with_info=True)\nn_train = info.splits['train'].num_examples\ntest_dataset = tfds.load('mnist', split='test', as_supervised=True)\ntrain_dataset = train_dataset.map(normalize_img,\nnum_parallel_calls=tf.data.experimental.AUTOTUNE)\ntrain_dataset = train_dataset.shuffle(n_train)\ntrain_dataset = train_dataset.batch(batch_size)\ntest_dataset = test_dataset.map(normalize_img,\nnum_parallel_calls=tf.data.experimental.AUTOTUNE)\ntest_dataset = test_dataset.batch(batch_size)\n# Define the model\ninput_img = tf.keras.Input(shape=(width, height, 1), name=\"Input\")\nx = tf.keras.layers.Conv2D(64, (3, 3), activation=\"relu\", padding=\"same\", name=\"Conv1_1\")(input_img)\nx = tf.keras.layers.BatchNormalization(name=\"bn1\")(x)\nx = tf.keras.layers.MaxPooling2D((2, 2), name=\"pool1\")(x)\nx = tf.keras.layers.Conv2D(128, (3, 3), activation=\"relu\", padding=\"same\", name=\"Conv2_1\")(x)\nx = tf.keras.layers.BatchNormalization(name=\"bn4\")(x)\nx = tf.keras.layers.MaxPooling2D((2, 2), name=\"pool2\")(x)\nx = tf.keras.layers.Flatten(name=\"flatten\")(x)\nx = tf.keras.layers.Dense(n_classes, activation=\"softmax\", name=\"fc1\")(x)\nmodel = tf.keras.Model(inputs=input_img, outputs=x)\nopt = tf.keras.optimizers.Adam(lr=l_rate)\nmodel.compile(\nloss=\"sparse_categorical_crossentropy\",\nmetrics=[tf.keras.metrics.SparseCategoricalAccuracy()],\noptimizer=opt)\n# Train and evaluate model\nfor round in range(n_rounds):\nmodel.fit(train_dataset, steps_per_epoch=40)\nresult = model.evaluate(x=test_dataset, return_dict=True, steps=10)\nprint(f\"Performance at round {round} is {result}\")\n

There are three steps:

  1. Load the data
  2. Define the model
  3. Train the model

In this tutorial we are going to see how to modify each step to use collective learning. We'll end up with code like this:

# ------------------------------------------------------------------------------\n#\n#   Copyright 2021 Fetch.AI Limited\n#\n#   Licensed under the Creative Commons Attribution-NonCommercial International\n#   License, Version 4.0 (the \"License\"); you may not use this file except in\n#   compliance with the License. You may obtain a copy of the License at\n#\n#       http://creativecommons.org/licenses/by-nc/4.0/legalcode\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n#\n# ------------------------------------------------------------------------------\nimport os\nimport tensorflow as tf\nimport tensorflow_datasets as tfds\nfrom colearn.training import initial_result, collective_learning_round, set_equal_weights\nfrom colearn.utils.plot import ColearnPlot\nfrom colearn.utils.results import Results, print_results\nfrom colearn_keras.keras_learner import KerasLearner\nfrom colearn_keras.utils import normalize_img\n\"\"\"\nMNIST training example using Keras\nUsed dataset:\n- MNIST is set of 60 000 black and white hand written digits images of size 28x28x1 in 10 classes\nWhat script does:\n- Loads MNIST dataset from Keras\n- Sets up a Keras learner\n- Randomly splits dataset between multiple learners\n- Does multiple rounds of learning process and displays plot with results\n\"\"\"\nn_learners = 5\nvote_threshold = 0.5\nvote_batches = 2\ntesting_mode = bool(os.getenv(\"COLEARN_EXAMPLES_TEST\", \"\"))  # for testing\nn_rounds = 20 if not testing_mode else 1\nwidth = 28\nheight = 28\nn_classes = 10\nl_rate = 0.001\nbatch_size = 64\n# Load data for each learner\ntrain_dataset, info = tfds.load('mnist', split='train', as_supervised=True, with_info=True)\nn_datapoints = info.splits['train'].num_examples\ntrain_datasets = [train_dataset.shard(num_shards=n_learners, index=i) for i in range(n_learners)]\ntest_dataset = tfds.load('mnist', split='test', as_supervised=True)\nvote_datasets = [test_dataset.shard(num_shards=2 * n_learners, index=i) for i in range(n_learners)]\ntest_datasets = [test_dataset.shard(num_shards=2 * n_learners, index=i) for i in range(n_learners, 2 * n_learners)]\nfor i in range(n_learners):\ntrain_datasets[i] = train_datasets[i].map(\nnormalize_img, num_parallel_calls=tf.data.experimental.AUTOTUNE)\ntrain_datasets[i] = train_datasets[i].shuffle(n_datapoints // n_learners)\ntrain_datasets[i] = train_datasets[i].batch(batch_size)\nvote_datasets[i] = vote_datasets[i].map(\nnormalize_img, num_parallel_calls=tf.data.experimental.AUTOTUNE)\nvote_datasets[i] = vote_datasets[i].batch(batch_size)\ntest_datasets[i] = test_datasets[i].map(\nnormalize_img, num_parallel_calls=tf.data.experimental.AUTOTUNE)\ntest_datasets[i] = test_datasets[i].batch(batch_size)\n# Define model\ndef get_model():\ninput_img = tf.keras.Input(\nshape=(width, height, 1), name=\"Input\"\n)\nx = tf.keras.layers.Conv2D(\n64, (3, 3), activation=\"relu\", padding=\"same\", name=\"Conv1_1\"\n)(input_img)\nx = tf.keras.layers.BatchNormalization(name=\"bn1\")(x)\nx = tf.keras.layers.MaxPooling2D((2, 2), name=\"pool1\")(x)\nx = tf.keras.layers.Conv2D(\n128, (3, 3), activation=\"relu\", padding=\"same\", name=\"Conv2_1\"\n)(x)\nx = tf.keras.layers.BatchNormalization(name=\"bn4\")(x)\nx = tf.keras.layers.MaxPooling2D((2, 2), name=\"pool2\")(x)\nx = tf.keras.layers.Flatten(name=\"flatten\")(x)\nx = tf.keras.layers.Dense(\nn_classes, activation=\"softmax\", name=\"fc1\"\n)(x)\nmodel = tf.keras.Model(inputs=input_img, outputs=x)\nopt = tf.keras.optimizers.Adam(lr=l_rate)\nmodel.compile(\nloss=\"sparse_categorical_crossentropy\",\nmetrics=[tf.keras.metrics.SparseCategoricalAccuracy()],\noptimizer=opt)\nreturn model\nall_learner_models = []\nfor i in range(n_learners):\nall_learner_models.append(KerasLearner(\nmodel=get_model(),\ntrain_loader=train_datasets[i],\nvote_loader=vote_datasets[i],\ntest_loader=test_datasets[i],\ncriterion=\"sparse_categorical_accuracy\",\nminimise_criterion=False,\nmodel_evaluate_kwargs={\"steps\": vote_batches},\n))\nset_equal_weights(all_learner_models)\n# Train the model using Collective Learning\nresults = Results()\nresults.data.append(initial_result(all_learner_models))\nplot = ColearnPlot(score_name=all_learner_models[0].criterion)\nfor round_index in range(n_rounds):\nresults.data.append(\ncollective_learning_round(all_learner_models,\nvote_threshold, round_index)\n)\nprint_results(results)\nplot.plot_results_and_votes(results)\nplot.block()\nprint(\"Colearn Example Finished!\")\n

The first thing is to modify the data loading code. Each learner needs to have their own training and testing set from the data. This is easy to do with keras:

train_datasets = [train_dataset.shard(num_shards=n_learners, index=i) for i in range(n_learners)]\n

The model definition is very similar too, except that each learner will need its own copy of the model, so we've moved it into a function.

To use collective learning, we need to create an object that implements the MachineLearningInterface. To make it easier to use the MachineLearningInterface with keras, we've defined KerasLearner. KerasLearner implements standard training and evaluation routines as well as the MachineLearningInterface methods.

# ------------------------------------------------------------------------------\n#\n#   Copyright 2021 Fetch.AI Limited\n#\n#   Licensed under the Creative Commons Attribution-NonCommercial International\n#   License, Version 4.0 (the \"License\"); you may not use this file except in\n#   compliance with the License. You may obtain a copy of the License at\n#\n#       http://creativecommons.org/licenses/by-nc/4.0/legalcode\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n#\n# ------------------------------------------------------------------------------\nfrom inspect import signature\nfrom typing import Optional\ntry:\nimport tensorflow as tf\nexcept ImportError:\nraise Exception(\"Tensorflow is not installed. To use the tensorflow/keras \"\n\"add-ons please install colearn with `pip install colearn[keras]`.\")\nfrom tensorflow import keras\nfrom colearn.ml_interface import MachineLearningInterface, Weights, ProposedWeights, ColearnModel, ModelFormat, convert_model_to_onnx\nfrom colearn.ml_interface import DiffPrivBudget, DiffPrivConfig, TrainingSummary, ErrorCodes\nfrom tensorflow_privacy.privacy.analysis.compute_dp_sgd_privacy import compute_dp_sgd_privacy\nfrom tensorflow_privacy.privacy.optimizers.dp_optimizer_keras import make_keras_optimizer_class\nclass KerasLearner(MachineLearningInterface):\n\"\"\"\n    Tensorflow Keras learner implementation of machine learning interface\n    \"\"\"\ndef __init__(self, model: keras.Model,\ntrain_loader: tf.data.Dataset,\nvote_loader: tf.data.Dataset,\ntest_loader: Optional[tf.data.Dataset] = None,\nneed_reset_optimizer: bool = True,\nminimise_criterion: bool = True,\ncriterion: str = 'loss',\nmodel_fit_kwargs: Optional[dict] = None,\nmodel_evaluate_kwargs: Optional[dict] = None,\ndiff_priv_config: Optional[DiffPrivConfig] = None):\n\"\"\"\n        :param model: Keras model used for training\n        :param train_loader: Training dataset\n        :param test_loader: Optional test set. Subset of training set will be used if not specified.\n        :param need_reset_optimizer: True to clear optimizer history before training, False to kepp history.\n        :param minimise_criterion: Boolean - True to minimise value of criterion, False to maximise\n        :param criterion: Function to measure model performance\n        :param model_fit_kwargs: Arguments to be passed on model.fit function call\n        :param model_evaluate_kwargs: Arguments to be passed on model.evaluate function call\n        :param diff_priv_config: Contains differential privacy (dp) budget related configuration\n        \"\"\"\nself.model: keras.Model = model\nself.train_loader: tf.data.Dataset = train_loader\nself.vote_loader: tf.data.Dataset = vote_loader\nself.test_loader: Optional[tf.data.Dataset] = test_loader\nself.need_reset_optimizer = need_reset_optimizer\nself.minimise_criterion: bool = minimise_criterion\nself.criterion = criterion\nself.model_fit_kwargs = model_fit_kwargs or {}\nself.diff_priv_config = diff_priv_config\nself.cumulative_epochs = 0\nif self.diff_priv_config is not None:\nself.diff_priv_budget = DiffPrivBudget(\ntarget_epsilon=self.diff_priv_config.target_epsilon,\ntarget_delta=self.diff_priv_config.target_delta,\nconsumed_epsilon=0.0,\n# we will always use the highest available delta now\nconsumed_delta=self.diff_priv_config.target_delta\n)\nif 'epochs' in self.model_fit_kwargs.keys():\nself.epochs_per_proposal = self.model_fit_kwargs['epochs']\nelse:\nself.epochs_per_proposal = signature(self.model.fit).parameters['epochs'].default\nif model_fit_kwargs:\n# check that these are valid kwargs for model fit\nsig = signature(self.model.fit)\ntry:\nsig.bind_partial(**self.model_fit_kwargs)\nexcept TypeError:\nraise Exception(\"Invalid arguments for model.fit\")\nself.model_evaluate_kwargs = model_evaluate_kwargs or {}\nif model_evaluate_kwargs:\n# check that these are valid kwargs for model evaluate\nsig = signature(self.model.evaluate)\ntry:\nsig.bind_partial(**self.model_evaluate_kwargs)\nexcept TypeError:\nraise Exception(\"Invalid arguments for model.evaluate\")\nself.vote_score: float = self.test(self.vote_loader)\ndef reset_optimizer(self):\n\"\"\"\n        Recompiles the Keras model. This way the optimizer history get erased,\n        which is needed before a new training round, otherwise the outdated history is used.\n        \"\"\"\ncompile_args = self.model._get_compile_args()  # pylint: disable=protected-access\nopt_config = self.model.optimizer.get_config()\nif self.diff_priv_config is not None:\n# tensorflow_privacy optimizers get_config() miss the additional parameters\n# was fixed here: https://github.com/tensorflow/privacy/commit/49db04e3561638fc02795edb5774d322cdd1d7d1\n# but it is not yet in the stable version, thus I need here to do the same.\nopt_config.update({\n'l2_norm_clip': self.model.optimizer._l2_norm_clip,  # pylint: disable=protected-access\n'noise_multiplier': self.model.optimizer._noise_multiplier,  # pylint: disable=protected-access\n'num_microbatches': self.model.optimizer._num_microbatches,  # pylint: disable=protected-access\n})\nnew_opt = make_keras_optimizer_class(\ngetattr(keras.optimizers, opt_config['name'])\n).from_config(opt_config)\ncompile_args['optimizer'] = new_opt\nelse:\ncompile_args['optimizer'] = getattr(keras.optimizers,\nopt_config['name']).from_config(opt_config)\nself.model.compile(**compile_args)\ndef mli_propose_weights(self) -> Weights:\n\"\"\"\n        Trains model on training set and returns new weights after training\n        - Current model is reverted to original state after training\n        :return: Weights after training\n        \"\"\"\ncurrent_weights = self.mli_get_current_weights()\nif self.diff_priv_config is not None:\nepsilon_after_training = self.get_privacy_budget()\nif epsilon_after_training > self.diff_priv_budget.target_epsilon:\nreturn Weights(\nweights=current_weights,\ntraining_summary=TrainingSummary(\ndp_budget=self.diff_priv_budget,\nerror_code=ErrorCodes.DP_BUDGET_EXCEEDED\n)\n)\nself.train()\nnew_weights = self.mli_get_current_weights()\nself.set_weights(current_weights)\nif self.diff_priv_config is not None:\nself.diff_priv_budget.consumed_epsilon = epsilon_after_training\nself.cumulative_epochs += self.epochs_per_proposal\nnew_weights.training_summary = TrainingSummary(dp_budget=self.diff_priv_budget)\nreturn new_weights\ndef mli_test_weights(self, weights: Weights) -> ProposedWeights:\n\"\"\"\n        Tests given weights on training and test set and returns weights with score values\n        :param weights: Weights to be tested\n        :return: ProposedWeights - Weights with vote and test score\n        \"\"\"\ncurrent_weights = self.mli_get_current_weights()\nself.set_weights(weights)\nvote_score = self.test(self.vote_loader)\nif self.test_loader:\ntest_score = self.test(self.test_loader)\nelse:\ntest_score = 0\nvote = self.vote(vote_score)\nself.set_weights(current_weights)\nreturn ProposedWeights(weights=weights,\nvote_score=vote_score,\ntest_score=test_score,\nvote=vote,\n)\ndef vote(self, new_score) -> bool:\n\"\"\"\n        Compares current model score with proposed model score and returns vote\n        :param new_score: Proposed score\n        :return: bool positive or negative vote\n        \"\"\"\nif self.minimise_criterion:\nreturn new_score < self.vote_score\nelse:\nreturn new_score > self.vote_score\ndef mli_accept_weights(self, weights: Weights):\n\"\"\"\n        Updates the model with the proposed set of weights\n        :param weights: The new weights\n        \"\"\"\nself.set_weights(weights)\nself.vote_score = self.test(self.vote_loader)\ndef get_train_batch_size(self) -> int:\n\"\"\"\n        Calculates train batch size.\n        \"\"\"\nif hasattr(self.train_loader, '_batch_size'):\nreturn self.train_loader._batch_size  # pylint: disable=protected-access\nelse:\nreturn self.train_loader._input_dataset._batch_size  # pylint: disable=protected-access\ndef get_privacy_budget(self) -> float:\n\"\"\"\n        Calculates, what epsilon will apply after another model training.\n        Need to calculate it in advance to see if another training would result in privacy budget violation.\n        \"\"\"\nbatch_size = self.get_train_batch_size()\niterations_per_epoch = tf.data.experimental.cardinality(self.train_loader).numpy()\nn_samples = batch_size * iterations_per_epoch\nplanned_epochs = self.cumulative_epochs + self.epochs_per_proposal\nepsilon, _ = compute_dp_sgd_privacy(\nn=n_samples,\nbatch_size=batch_size,\nnoise_multiplier=self.diff_priv_config.noise_multiplier,  # type: ignore\nepochs=planned_epochs,\ndelta=self.diff_priv_budget.target_delta\n)\nreturn epsilon\ndef mli_get_current_weights(self) -> Weights:\n\"\"\"\n        :return: The current weights of the model\n        \"\"\"\nreturn Weights(weights=self.model.get_weights())\ndef mli_get_current_model(self) -> ColearnModel:\n\"\"\"\n        :return: The current model and its format\n        \"\"\"\nreturn ColearnModel(\nmodel_format=ModelFormat(ModelFormat.ONNX),\nmodel_file=\"\",\nmodel=convert_model_to_onnx(self.model),\n)\ndef set_weights(self, weights: Weights):\n\"\"\"\n        Rewrites weight of current model\n        :param weights: Weights to be stored\n        \"\"\"\nself.model.set_weights(weights.weights)\ndef train(self):\n\"\"\"\n        Trains the model on the training dataset\n        \"\"\"\nif self.need_reset_optimizer:\n# erase the outdated optimizer memory (momentums mostly)\nself.reset_optimizer()\nself.model.fit(self.train_loader, **self.model_fit_kwargs)\ndef test(self, loader: tf.data.Dataset) -> float:\n\"\"\"\n        Tests performance of the model on specified dataset\n        :param loader: Dataset for testing\n        :return: Value of performance metric\n        \"\"\"\nresult = self.model.evaluate(x=loader, return_dict=True,\n**self.model_evaluate_kwargs)\nreturn result[self.criterion]\n

We create a set of KerasLearners by passing in the model and the datasets:

all_learner_models = []\nfor i in range(n_learners):\nall_learner_models.append(KerasLearner(\nmodel=get_model(),\ntrain_loader=train_datasets[i],\nvote_loader=vote_datasets[i],\ntest_loader=test_datasets[i],\ncriterion=\"sparse_categorical_accuracy\",\nminimise_criterion=False,\nmodel_evaluate_kwargs={\"steps\": vote_batches},\n))\n

Then we give all the models the same weights to start off with:

set_equal_weights(all_learner_models)\n

And then we can move on to the final stage, which is training with Collective Learning. The function collective_learning_round performs one round of collective learning. One learner is selected to train and propose an update. The other learners vote on the update, and if the vote passes then the update is accepted. Then a new round begins.

# Train the model using Collective Learning\nresults = Results()\nresults.data.append(initial_result(all_learner_models))\nfor round in range(n_rounds):\nresults.data.append(\ncollective_learning_round(all_learner_models,\nvote_threshold, round)\n)\nplot_results(results, n_learners, block=False,\nscore_name=all_learner_models[0].criterion)\nplot_votes(results, block=False)\nplot_results(results, n_learners, block=False,\nscore_name=all_learner_models[0].criterion)\nplot_votes(results, block=True)\n
"},{"location":"colearn/intro_tutorial_mli/","title":"Using collective learning","text":"

This tutorial is a simple guide to trying out the collective learning protocol with your own machine learning code. Everything runs locally.

The most flexible way to use the collective learning backends is to make a class that implements the Collective Learning MachineLearningInterface defined in ml_interface.py. This tutorial will walk through implementing the MachineLearningInterface. If you're already using keras or pytorch you might find it easier to use the KerasLearner or Pytorchlearner classes. See the other tutorials for details of how to do that.

"},{"location":"colearn/intro_tutorial_mli/#the-machinelearninginterface","title":"The MachineLearningInterface","text":"
# ------------------------------------------------------------------------------\n#\n#   Copyright 2021 Fetch.AI Limited\n#\n#   Licensed under the Creative Commons Attribution-NonCommercial International\n#   License, Version 4.0 (the \"License\"); you may not use this file except in\n#   compliance with the License. You may obtain a copy of the License at\n#\n#       http://creativecommons.org/licenses/by-nc/4.0/legalcode\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n#\n# ------------------------------------------------------------------------------\nimport abc\nfrom enum import Enum\nfrom typing import Any, Optional\nimport onnx\nimport onnxmltools\nimport sklearn\nimport tensorflow as tf\nimport torch\nfrom pydantic import BaseModel\nfrom tensorflow import keras\nmodel_classes_keras = (tf.keras.Model, keras.Model, tf.estimator.Estimator)\nmodel_classes_scipy = (torch.nn.Module)\nmodel_classes_sklearn = (sklearn.base.ClassifierMixin)\ndef convert_model_to_onnx(model: Any):\n\"\"\"\n    Helper function to convert a ML model to onnx format\n    \"\"\"\nif isinstance(model, model_classes_keras):\nreturn onnxmltools.convert_keras(model)\nif isinstance(model, model_classes_sklearn):\nreturn onnxmltools.convert_sklearn(model)\nif 'xgboost' in model.__repr__():\nreturn onnxmltools.convert_sklearn(model)\nif isinstance(model, model_classes_scipy):\nraise Exception(\"Pytorch models not yet supported to onnx\")\nelse:\nraise Exception(\"Attempt to convert unsupported model to onnx: {model}\")\nclass DiffPrivBudget(BaseModel):\ntarget_epsilon: float\ntarget_delta: float\nconsumed_epsilon: float\nconsumed_delta: float\nclass ErrorCodes(Enum):\nDP_BUDGET_EXCEEDED = 1\nclass TrainingSummary(BaseModel):\ndp_budget: Optional[DiffPrivBudget]\nerror_code: Optional[ErrorCodes]\nclass Weights(BaseModel):\nweights: Any\ntraining_summary: Optional[TrainingSummary]\nclass DiffPrivConfig(BaseModel):\ntarget_epsilon: float\ntarget_delta: float\nmax_grad_norm: float\nnoise_multiplier: float\nclass ProposedWeights(BaseModel):\nweights: Weights\nvote_score: float\ntest_score: float\nvote: Optional[bool]\nclass ModelFormat(Enum):\nPICKLE_WEIGHTS_ONLY = 1\nONNX = 2\nclass ColearnModel(BaseModel):\nmodel_format: ModelFormat\nmodel_file: Optional[str]\nmodel: Optional[Any]\ndef deser_model(model: Any) -> onnx.ModelProto:\n\"\"\"\n    Helper function to recover a onnx model from its deserialized form\n    \"\"\"\nreturn onnx.load_model_from_string(model)\nclass MachineLearningInterface(abc.ABC):\n@abc.abstractmethod\ndef mli_propose_weights(self) -> Weights:\n\"\"\"\n        Trains the model. Returns new weights. Does not change the current weights of the model.\n        \"\"\"\npass\n@abc.abstractmethod\ndef mli_test_weights(self, weights: Weights) -> ProposedWeights:\n\"\"\"\n        Tests the proposed weights and fills in the rest of the fields\n        \"\"\"\n@abc.abstractmethod\ndef mli_accept_weights(self, weights: Weights):\n\"\"\"\n        Updates the model with the proposed set of weights\n        :param weights: The new weights\n        \"\"\"\npass\n@abc.abstractmethod\ndef mli_get_current_weights(self) -> Weights:\n\"\"\"\n        Returns the current weights of the model\n        \"\"\"\npass\n@abc.abstractmethod\ndef mli_get_current_model(self) -> ColearnModel:\n\"\"\"\n        Returns the current model\n        \"\"\"\npass \n

There are four methods that need to be implemented:

  1. propose_weights causes the model to do some training and then return a new set of weights that are proposed to the other learners. This method shouldn't charge the current weights of the model - that only happens when accept_weights is called.
  2. test_weights - the models takes some new weights and returns a vote on whether the new weights are an improvement. As in propose_weights, this shouldn't change the current weights of the model - that only happens when accept_weights is called.
  3. accept_weights - the model accepts some weights that have been voted on and approved by the set of learners. The old weighs of the model are discarded and replaced by the new weights.
  4. current_weights should return the current weights of the model.
"},{"location":"colearn/intro_tutorial_mli/#algorithms-that-work-with-colearn","title":"Algorithms that work with colearn","text":"

These conditions need to be fulfilled for algorithms to work with collective learning:

  • Model fitting must be incremental so that the previous model is used as the starting point for training. This is easy to achieve for neural networks because neural network training is always iterative, but for other learning algorithms more care must be taken. Some examples of getting this wrong:
from sklearn.linear_model import LinearRegression\nmodel = LinearRegression()\nmodel.fit(X, y)\n
from sklearn.ensemble import RandomForestClassifier\nmodel = RandomForestClassifier(n_estimators=10)  # it would be okay with warm_start=True\nmodel.fit(X, y)\n
from xgboost import XGBRegressor\nmodel = XGBRegressor()\nmodel.fit(X, y)\n

None of the training methods here use the previous result when fit is called for a second time; instead they start again from scratch. Good examples of incremental training can be seen in the examples. Many sklearn models have a warm_start parameter which can be set to True to use the previous training result. XGBoost has an xgb_model parameter for passing in the previous training results.

  • The model mustn't overfit when propose_weights() is called. You should limit training so that a learner will not overfit their training data in one round. For example, if a learner overfits their own training data then the other learners will reject the proposed update because it is not a good fit for their data. For a neural network a good approach is to restrict the number of batches that are used each round; for random forest, restrict the trees that are added each round.
"},{"location":"colearn/intro_tutorial_mli/#implementation-for-fraud-detection-task","title":"Implementation for fraud detection task","text":"

Here is the class that implements the MachineLearningInterface for the task of detecting fraud in bank transactions.

class FraudSklearnLearner(MachineLearningInterface):\ndef __init__(self, train_data, train_labels, test_data, test_labels,\nbatch_size: int = 10000,\nsteps_per_round: int = 1):\nself.steps_per_round = steps_per_round\nself.batch_size = batch_size\nself.train_data = train_data\nself.train_labels = train_labels\nself.test_data = test_data\nself.test_labels = test_labels\nself.class_labels = np.unique(train_labels)\nself.train_sampler = infinite_batch_sampler(train_data.shape[0], batch_size)\nself.model = SGDClassifier(max_iter=1, verbose=0, loss=\"modified_huber\")\nself.model.partial_fit(self.train_data[0:1], self.train_labels[0:1],\nclasses=self.class_labels)  # this needs to be called before predict\nself.vote_score = self.test(self.train_data, self.train_labels)\ndef mli_propose_weights(self) -> Weights:\ncurrent_weights = self.mli_get_current_weights()\nfor i in range(self.steps_per_round):\nbatch_indices = next(self.train_sampler)\ntrain_data = self.train_data[batch_indices]\ntrain_labels = self.train_labels[batch_indices]\nself.model.partial_fit(train_data, train_labels, classes=self.class_labels)\nnew_weights = self.mli_get_current_weights()\nself.set_weights(current_weights)\nreturn new_weights\ndef mli_test_weights(self, weights: Weights) -> ProposedWeights:\ncurrent_weights = self.mli_get_current_weights()\nself.set_weights(weights)\nvote_score = self.test(self.train_data, self.train_labels)\ntest_score = self.test(self.test_data, self.test_labels)\nvote = self.vote_score <= vote_score\nself.set_weights(current_weights)\nreturn ProposedWeights(weights=weights,\nvote_score=vote_score,\ntest_score=test_score,\nvote=vote\n)\ndef mli_accept_weights(self, weights: Weights):\nself.set_weights(weights)\nself.vote_score = self.test(self.train_data, self.train_labels)\ndef mli_get_current_weights(self):\n# return Weights(weights=copy.deepcopy(self.model))\nreturn Weights(weights=dict(coef_=self.model.coef_,\nintercept_=self.model.intercept_))\ndef set_weights(self, weights: Weights):\n# self.model = weights.weights\nself.model.coef_ = weights.weights['coef_']\nself.model.intercept_ = weights.weights['intercept_']\ndef test(self, data, labels):\ntry:\nreturn self.model.score(data, labels)\nexcept sklearn.exceptions.NotFittedError:\nreturn 0\n

Let's step through this and see how it works. The propose_weights method saves the current weights of the model. Then it performs some training of the model, and gets the new weights. It returns the new weights, and resets the model weights to be the old weights.

    def mli_propose_weights(self) -> Weights:\ncurrent_weights = self.mli_get_current_weights()\nfor i in range(self.steps_per_round):\nbatch_indices = next(self.train_sampler)\ntrain_data = self.train_data[batch_indices]\ntrain_labels = self.train_labels[batch_indices]\nself.model.partial_fit(train_data, train_labels, classes=self.class_labels)\nnew_weights = self.mli_get_current_weights()\nself.set_weights(current_weights)\nreturn new_weights\n

The test_weights method takes as a parameter the proposed weights that it needs to vote on. It saves the current weights of the model, and then sets the model weights to be the proposed weights. It tests the model and votes based on whether the score that it is monitoring has improved. The vote score can be any metric that you like. You could use loss, accuracy, mean squared error or any custom metric. If the vote score is the loss then the model would only vote True if the score has decreased. Here we're using accuracy, so the vote is true if the score increases. This method then resets the weights to the old values and returns the vote along with some scores for monitoring purposes.

    def mli_test_weights(self, weights: Weights) -> ProposedWeights:\ncurrent_weights = self.mli_get_current_weights()\nself.set_weights(weights)\nvote_score = self.test(self.train_data, self.train_labels)\ntest_score = self.test(self.test_data, self.test_labels)\nvote = self.vote_score <= vote_score\nself.set_weights(current_weights)\nreturn ProposedWeights(weights=weights,\nvote_score=vote_score,\ntest_score=test_score,\nvote=vote\n)\n

The accept_weights method sets the weights of the model to be the new weights. It also updates the vote score to be the current performance.

Note

You could implement a cache here. These weights will already have been tested in test_weights, so the vote score could be retrieved from the cache instead of recomputed.

    def mli_accept_weights(self, weights: Weights):\nself.set_weights(weights)\nself.vote_score = self.test(self.train_data, self.train_labels)\n

The final method is the simplest - get_current_weights just returns the current weights of the model. These weights are wrapped inside a Weights object.

    def mli_get_current_weights(self):\nreturn Weights(weights=dict(coef_=self.model.coef_,\nintercept_=self.model.intercept_))\n
"},{"location":"colearn/intro_tutorial_mli/#the-rest-of-the-example","title":"The rest of the example","text":"

The data is loaded and preprocessed and then split into equal parts for each learner. Then a list of FraudLearner instances is created, each with its own dataset.

    all_learner_models = []\nfor i in range(n_learners):\nall_learner_models.append(\nFraudLearner(\ntrain_data=learner_train_data[i],\ntrain_labels=learner_train_labels[i],\ntest_data=learner_test_data[i],\ntest_labels=learner_test_labels[i]\n))\n

Then we give all the models the same weights to start off with:

set_equal_weights(all_learner_models)\n

And then we can move on to the final stage, which is training with Collective Learning. The function collective_learning_round performs one round of collective learning. One learner is selected to train and propose an update. The other learners vote on the update, and if the vote passes then the update is accepted. Then a new round begins.

# Train the model using Collective Learning\nresults = Results()\nresults.data.append(initial_result(all_learner_models))\nfor round in range(n_rounds):\nresults.data.append(\ncollective_learning_round(all_learner_models,\nvote_threshold, round)\n)\nplot_results(results, n_learners, block=False,\nscore_name=all_learner_models[0].criterion)\nplot_votes(results, block=False)\nplot_results(results, n_learners, block=False,\nscore_name=all_learner_models[0].criterion)\nplot_votes(results, block=True)\n
"},{"location":"colearn/intro_tutorial_pytorch/","title":"Using collective learning with pytorch","text":"

This tutorial is a simple guide to trying out the collective learning protocol with your own machine learning code. Everything runs locally.

The most flexible way to use the collective learning backends is to make a class that implements the Collective Learning MachineLearningInterface defined in ml_interface.py. For more details on how to use the MachineLearningInterface see here

However, the simpler way is to use one of the helper classes that we have provided that implement most of the interface for popular ML libraries. In this tutorial we are going to walk through using the PytorchLearner. First we are going to define the model architecture, then we are going to load the data and configure the model, and then we will run Collective Learning.

A standard script for machine learning with Pytorch looks like the one below

# ------------------------------------------------------------------------------\n#\n#   Copyright 2021 Fetch.AI Limited\n#\n#   Licensed under the Creative Commons Attribution-NonCommercial International\n#   License, Version 4.0 (the \"License\"); you may not use this file except in\n#   compliance with the License. You may obtain a copy of the License at\n#\n#       http://creativecommons.org/licenses/by-nc/4.0/legalcode\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n#\n# ------------------------------------------------------------------------------\nfrom torchsummary import summary\nfrom torchvision import transforms, datasets\nimport torch.utils.data\nimport torch.nn as nn\nimport torch.nn.functional as nn_func\n# define some constants\nbatch_size = 64\nseed = 42\nn_rounds = 20\ntrain_fraction = 0.9\nlearning_rate = 0.001\nheight = 28\nwidth = 28\nn_classes = 10\nnum_test_batches = 10\nno_cuda = False\ncuda = not no_cuda and torch.cuda.is_available()\ndevice = torch.device(\"cuda\" if cuda else \"cpu\")\nkwargs = {'num_workers': 1, 'pin_memory': True} if cuda else {}\n# Load the data\ndata = datasets.MNIST('/tmp/mnist', transform=transforms.ToTensor(), download=True)\nn_train = int(train_fraction * len(data))\nn_test = len(data) - n_train\ntrain_data, test_data = torch.utils.data.random_split(data, [n_train, n_test])\ntrain_dataloader = torch.utils.data.DataLoader(train_data, batch_size=batch_size, shuffle=True, **kwargs)\ntest_dataloader = torch.utils.data.DataLoader(test_data, batch_size=batch_size, shuffle=True, **kwargs)\n# Define the model\nclass Net(nn.Module):\ndef __init__(self):\nsuper(Net, self).__init__()\nself.conv1 = nn.Conv2d(1, 20, 5, 1)\nself.conv2 = nn.Conv2d(20, 50, 5, 1)\nself.fc1 = nn.Linear(4 * 4 * 50, 500)\nself.fc2 = nn.Linear(500, n_classes)\ndef forward(self, x):\nx = nn_func.relu(self.conv1(x.view(-1, 1, height, width)))\nx = nn_func.max_pool2d(x, 2, 2)\nx = nn_func.relu(self.conv2(x))\nx = nn_func.max_pool2d(x, 2, 2)\nx = x.view(-1, 4 * 4 * 50)\nx = nn_func.relu(self.fc1(x))\nx = self.fc2(x)\nreturn nn_func.log_softmax(x, dim=1)\nmodel = Net()\nopt = torch.optim.Adam(model.parameters(), lr=learning_rate)\ncriterion = torch.nn.NLLLoss()\n# Train and evaluate the model\nfor round in range(n_rounds):\n# train model\nmodel.train()\nfor batch_idx, (data, labels) in enumerate(train_dataloader):\nopt.zero_grad()\n# Data needs to be on same device as model\ndata = data.to(device)\nlabels = labels.to(device)\noutput = model(data)\nloss = criterion(output, labels)\nloss.backward()\nopt.step()\n# evaluate model\nmodel.eval()\ntotal_score = 0\nall_labels = []\nall_outputs = []\nwith torch.no_grad():\nfor batch_idx, (data, labels) in enumerate(test_dataloader):\nif batch_idx == num_test_batches:\nbreak\ndata = data.to(device)\nlabels = labels.to(device)\noutput = model(data)\ntotal_score += criterion(output, labels)\navg_loss = float(total_score / (num_test_batches * batch_size))\nprint(f\"Average loss at round {round} is {avg_loss}\")\n

There are three steps:

  1. Load the data
  2. Define the model
  3. Train the model

In this tutorial we are going to see how to modify each step to use collective learning. We'll end up with code like this:

# ------------------------------------------------------------------------------\n#\n#   Copyright 2021 Fetch.AI Limited\n#\n#   Licensed under the Creative Commons Attribution-NonCommercial International\n#   License, Version 4.0 (the \"License\"); you may not use this file except in\n#   compliance with the License. You may obtain a copy of the License at\n#\n#       http://creativecommons.org/licenses/by-nc/4.0/legalcode\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n#\n# ------------------------------------------------------------------------------\nimport os\nfrom typing_extensions import TypedDict\nimport torch.nn as nn\nimport torch.nn.functional as nn_func\nimport torch.utils.data\nfrom torchsummary import summary\nfrom torchvision import transforms, datasets\nfrom colearn.training import initial_result, collective_learning_round, set_equal_weights\nfrom colearn.utils.plot import ColearnPlot\nfrom colearn.utils.results import Results, print_results\nfrom colearn_pytorch.utils import categorical_accuracy\nfrom colearn_pytorch.pytorch_learner import PytorchLearner\n\"\"\"\nMNIST training example using PyTorch\nUsed dataset:\n- MNIST is set of 60 000 black and white hand written digits images of size 28x28x1 in 10 classes\nWhat script does:\n- Loads MNIST dataset from torchvision.datasets\n- Randomly splits dataset between multiple learners\n- Does multiple rounds of learning process and displays plot with results\n\"\"\"\n# define some constants\nn_learners = 5\nbatch_size = 64\ntesting_mode = bool(os.getenv(\"COLEARN_EXAMPLES_TEST\", \"\"))  # for testing\nn_rounds = 20 if not testing_mode else 1\nvote_threshold = 0.5\ntrain_fraction = 0.9\nvote_fraction = 0.05\nlearning_rate = 0.001\nheight = 28\nwidth = 28\nn_classes = 10\nvote_batches = 2\nscore_name = \"categorical accuracy\"\nno_cuda = False\ncuda = not no_cuda and torch.cuda.is_available()\ndevice = torch.device(\"cuda\" if cuda else \"cpu\")\nDataloaderKwargs = TypedDict('DataloaderKwargs', {'num_workers': int, 'pin_memory': bool}, total=False)\nkwargs: DataloaderKwargs = {'num_workers': 1, 'pin_memory': True} if cuda else {}\n# Load the data and split for each learner.\nDATA_DIR = os.environ.get('PYTORCH_DATA_DIR',\nos.path.expanduser(os.path.join('~', 'pytorch_datasets')))\ndata = datasets.MNIST(DATA_DIR, transform=transforms.ToTensor(), download=True)\nn_train = int(train_fraction * len(data))\nn_vote = int(vote_fraction * len(data))\nn_test = len(data) - n_train - n_vote\ntrain_data, vote_data, test_data = torch.utils.data.random_split(data, [n_train, n_vote, n_test])\ndata_split = [len(train_data) // n_learners] * n_learners\nlearner_train_data = torch.utils.data.random_split(train_data, data_split)\nlearner_train_dataloaders = [torch.utils.data.DataLoader(\nds,\nbatch_size=batch_size, shuffle=True, **kwargs) for ds in learner_train_data]\ndata_split = [len(vote_data) // n_learners] * n_learners\nlearner_vote_data = torch.utils.data.random_split(vote_data, data_split)\nlearner_vote_dataloaders = [torch.utils.data.DataLoader(\nds,\nbatch_size=batch_size, shuffle=True, **kwargs) for ds in learner_vote_data]\ndata_split = [len(test_data) // n_learners] * n_learners\nlearner_test_data = torch.utils.data.random_split(test_data, data_split)\nlearner_test_dataloaders = [torch.utils.data.DataLoader(\nds,\nbatch_size=batch_size, shuffle=True, **kwargs) for ds in learner_test_data]\n# Define the model\nclass Net(nn.Module):\ndef __init__(self):\nsuper(Net, self).__init__()\nself.conv1 = nn.Conv2d(1, 20, 5, 1)\nself.conv2 = nn.Conv2d(20, 50, 5, 1)\nself.fc1 = nn.Linear(4 * 4 * 50, 500)\nself.fc2 = nn.Linear(500, n_classes)\ndef forward(self, x):\nx = nn_func.relu(self.conv1(x.view(-1, 1, height, width)))\nx = nn_func.max_pool2d(x, 2, 2)\nx = nn_func.relu(self.conv2(x))\nx = nn_func.max_pool2d(x, 2, 2)\nx = x.view(-1, 4 * 4 * 50)\nx = nn_func.relu(self.fc1(x))\nx = self.fc2(x)\nreturn nn_func.log_softmax(x, dim=1)\n# Make n instances of PytorchLearner with model and torch dataloaders\nall_learner_models = []\nfor i in range(n_learners):\nmodel = Net().to(device)\nopt = torch.optim.Adam(model.parameters(), lr=learning_rate)\nlearner = PytorchLearner(\nmodel=model,\ntrain_loader=learner_train_dataloaders[i],\nvote_loader=learner_vote_dataloaders[i],\ntest_loader=learner_test_dataloaders[i],\ndevice=device,\noptimizer=opt,\ncriterion=torch.nn.NLLLoss(),\nnum_test_batches=vote_batches,\nvote_criterion=categorical_accuracy,\nminimise_criterion=False\n)\nall_learner_models.append(learner)\n# Ensure all learners starts with exactly same weights\nset_equal_weights(all_learner_models)\nsummary(all_learner_models[0].model, input_size=(width, height), device=str(device))\n# Train the model using Collective Learning\nresults = Results()\nresults.data.append(initial_result(all_learner_models))\nplot = ColearnPlot(score_name=score_name)\nfor round_index in range(n_rounds):\nresults.data.append(\ncollective_learning_round(all_learner_models,\nvote_threshold, round_index)\n)\nprint_results(results)\nplot.plot_results_and_votes(results)\nplot.block()\nprint(\"Colearn Example Finished!\")\n

The first thing is to modify the data loading code. Each learner needs to have their own training and testing set from the data. This is easy to do with the pytorch random_split utility:

data_split = [len(test_data) // n_learners] * n_learners\nlearner_test_data = torch.utils.data.random_split(test_data, data_split)\n

The model definition is the same as before. To use collective learning, we need to create an object that implements the MachineLearningInterface. To make it easier to use the MachineLearningInterface with pytorch, we've defined PytorchLearner. PytorchLearner implements standard training and evaluation routines as well as the MachineLearningInterface methods.

# ------------------------------------------------------------------------------\n#\n#   Copyright 2021 Fetch.AI Limited\n#\n#   Licensed under the Creative Commons Attribution-NonCommercial International\n#   License, Version 4.0 (the \"License\"); you may not use this file except in\n#   compliance with the License. You may obtain a copy of the License at\n#\n#       http://creativecommons.org/licenses/by-nc/4.0/legalcode\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n#\n# ------------------------------------------------------------------------------\nfrom typing import Optional, Callable\nfrom collections import OrderedDict, defaultdict\ntry:\nimport torch\nexcept ImportError:\nraise Exception(\n\"Pytorch is not installed. To use the pytorch \"\n\"add-ons please install colearn with `pip install colearn[pytorch]`.\"\n)\nimport torch.nn\nimport torch.optim\nimport torch.utils\nimport torch.utils.data\nfrom torch.nn.modules.loss import _Loss\nfrom colearn.ml_interface import (\nMachineLearningInterface,\nWeights,\nProposedWeights,\nColearnModel,\nconvert_model_to_onnx,\nModelFormat,\nDiffPrivBudget,\nDiffPrivConfig,\nTrainingSummary,\nErrorCodes,\n)\nfrom opacus import PrivacyEngine\n_DEFAULT_DEVICE = torch.device(\"cpu\")\nclass PytorchLearner(MachineLearningInterface):\n\"\"\"\n    Pytorch learner implementation of machine learning interface\n    \"\"\"\ndef __init__(\nself,\nmodel: torch.nn.Module,\noptimizer: torch.optim.Optimizer,\ntrain_loader: torch.utils.data.DataLoader,\nvote_loader: torch.utils.data.DataLoader,\ntest_loader: Optional[torch.utils.data.DataLoader] = None,\nneed_reset_optimizer: bool = True,\ndevice=_DEFAULT_DEVICE,\ncriterion: Optional[_Loss] = None,\nminimise_criterion=True,\nvote_criterion: Optional[Callable[[torch.Tensor, torch.Tensor], float]] = None,\nnum_train_batches: Optional[int] = None,\nnum_test_batches: Optional[int] = None,\ndiff_priv_config: Optional[DiffPrivConfig] = None,\n):\n\"\"\"\n        :param model: Pytorch model used for training\n        :param optimizer: Training optimizer\n        :param train_loader: Train dataset\n        :param test_loader: Optional test dataset - subset of training set will be used if not specified\n        :param need_reset_optimizer: True to clear optimizer history before training, False to kepp history.\n        :param device: Pytorch device - CPU or GPU\n        :param criterion: Loss function\n        :param minimise_criterion: True to minimise value of criterion, False to maximise\n        :param vote_criterion: Function to measure model performance for voting\n        :param num_train_batches: Number of training batches\n        :param num_test_batches: Number of testing batches\n        :param diff_priv_config: Contains differential privacy (dp) budget related configuration\n        \"\"\"\n# Model has to be on same device as data\nself.model: torch.nn.Module = model.to(device)\nself.optimizer: torch.optim.Optimizer = optimizer\nself.criterion = criterion\nself.train_loader: torch.utils.data.DataLoader = train_loader\nself.vote_loader: torch.utils.data.DataLoader = vote_loader\nself.test_loader: Optional[torch.utils.data.DataLoader] = test_loader\nself.need_reset_optimizer = need_reset_optimizer\nself.device = device\nself.num_train_batches = num_train_batches or len(train_loader)\nself.num_test_batches = num_test_batches\nself.minimise_criterion = minimise_criterion\nself.vote_criterion = vote_criterion\nself.dp_config = diff_priv_config\nself.dp_privacy_engine = PrivacyEngine()\nif diff_priv_config is not None:\n(\nself.model,\nself.optimizer,\nself.train_loader,\n) = self.dp_privacy_engine.make_private(\nmodule=self.model,\noptimizer=self.optimizer,\ndata_loader=self.train_loader,\nmax_grad_norm=diff_priv_config.max_grad_norm,\nnoise_multiplier=diff_priv_config.noise_multiplier,\n)\nself.vote_score = self.test(self.vote_loader)\ndef mli_get_current_weights(self) -> Weights:\n\"\"\"\n        :return: The current weights of the model\n        \"\"\"\ncurrent_state_dict = OrderedDict()\nfor key in self.model.state_dict():\ncurrent_state_dict[key] = self.model.state_dict()[key].clone()\nw = Weights(\nweights=current_state_dict, training_summary=self.get_training_summary()\n)\nreturn w\ndef mli_get_current_model(self) -> ColearnModel:\n\"\"\"\n        :return: The current model and its format\n        \"\"\"\nreturn ColearnModel(\nmodel_format=ModelFormat(ModelFormat.ONNX),\nmodel_file=\"\",\nmodel=convert_model_to_onnx(self.model),\n)\ndef set_weights(self, weights: Weights):\n\"\"\"\n        Rewrites weight of current model\n        :param weights: Weights to be stored\n        \"\"\"\nself.model.load_state_dict(weights.weights)\ndef reset_optimizer(self):\n\"\"\"\n        Clear optimizer state, such as number of iterations, momentums.\n        This way, the outdated history can be erased.\n        \"\"\"\nself.optimizer.__setstate__({\"state\": defaultdict(dict)})\ndef train(self):\n\"\"\"\n        Trains the model on the training dataset\n        \"\"\"\nif self.need_reset_optimizer:\n# erase the outdated optimizer memory (momentums mostly)\nself.reset_optimizer()\nself.model.train()\nfor batch_idx, (data, labels) in enumerate(self.train_loader):\nif batch_idx == self.num_train_batches:\nbreak\nself.optimizer.zero_grad()\n# Data needs to be on same device as model\ndata = data.to(self.device)\nlabels = labels.to(self.device)\noutput = self.model(data)\nloss = self.criterion(output, labels)\nloss.backward()\nself.optimizer.step()\ndef mli_propose_weights(self) -> Weights:\n\"\"\"\n        Trains model on training set and returns new weights after training\n        - Current model is reverted to original state after training\n        :return: Weights after training\n        \"\"\"\ncurrent_weights = self.mli_get_current_weights()\ntraining_summary = current_weights.training_summary\nif (\ntraining_summary is not None\nand training_summary.error_code is not None\nand training_summary.error_code == ErrorCodes.DP_BUDGET_EXCEEDED\n):\nreturn current_weights\nself.train()\nnew_weights = self.mli_get_current_weights()\nself.set_weights(current_weights)\ntraining_summary = new_weights.training_summary\nif (\ntraining_summary is not None\nand training_summary.error_code is not None\nand training_summary.error_code == ErrorCodes.DP_BUDGET_EXCEEDED\n):\ncurrent_weights.training_summary = training_summary\nreturn current_weights\nreturn new_weights\ndef mli_test_weights(self, weights: Weights) -> ProposedWeights:\n\"\"\"\n        Tests given weights on training and test set and returns weights with score values\n        :param weights: Weights to be tested\n        :return: ProposedWeights - Weights with vote and test score\n        \"\"\"\ncurrent_weights = self.mli_get_current_weights()\nself.set_weights(weights)\nvote_score = self.test(self.vote_loader)\nif self.test_loader:\ntest_score = self.test(self.test_loader)\nelse:\ntest_score = 0\nvote = self.vote(vote_score)\nself.set_weights(current_weights)\nreturn ProposedWeights(\nweights=weights, vote_score=vote_score, test_score=test_score, vote=vote\n)\ndef vote(self, new_score) -> bool:\n\"\"\"\n        Compares current model score with proposed model score and returns vote\n        :param new_score: Proposed score\n        :return: bool positive or negative vote\n        \"\"\"\nif self.minimise_criterion:\nreturn new_score < self.vote_score\nelse:\nreturn new_score > self.vote_score\ndef test(self, loader: torch.utils.data.DataLoader) -> float:\n\"\"\"\n        Tests performance of the model on specified dataset\n        :param loader: Dataset for testing\n        :return: Value of performance metric\n        \"\"\"\nif not self.criterion:\nraise Exception(\"Criterion is unspecified so test method cannot be used\")\nself.model.eval()\ntotal_score = 0\nall_labels = []\nall_outputs = []\nbatch_idx = 0\ntotal_samples = 0\nwith torch.no_grad():\nfor batch_idx, (data, labels) in enumerate(loader):\ntotal_samples += labels.shape[0]\nif self.num_test_batches and batch_idx == self.num_test_batches:\nbreak\ndata = data.to(self.device)\nlabels = labels.to(self.device)\noutput = self.model(data)\nif self.vote_criterion is not None:\nall_labels.append(labels)\nall_outputs.append(output)\nelse:\ntotal_score += self.criterion(output, labels).item()\nif batch_idx == 0:\nraise Exception(\"No batches in loader\")\nif self.vote_criterion is None:\nreturn float(total_score / total_samples)\nelse:\nreturn self.vote_criterion(\ntorch.cat(all_outputs, dim=0), torch.cat(all_labels, dim=0)\n)\ndef mli_accept_weights(self, weights: Weights):\n\"\"\"\n        Updates the model with the proposed set of weights\n        :param weights: The new weights\n        \"\"\"\nself.set_weights(weights)\nself.vote_score = self.test(self.vote_loader)\ndef get_training_summary(self) -> Optional[TrainingSummary]:\n\"\"\"\n        Differential Privacy Budget\n        :return: the target and consumed epsilon so far\n        \"\"\"\nif self.dp_config is None:\nreturn None\ndelta = self.dp_config.target_delta\ntarget_epsilon = self.dp_config.target_epsilon\nconsumed_epsilon = self.dp_privacy_engine.get_epsilon(delta)\nbudget = DiffPrivBudget(\ntarget_epsilon=target_epsilon,\nconsumed_epsilon=consumed_epsilon,\ntarget_delta=delta,\nconsumed_delta=delta,  # delta is constatnt per training\n)\nerr = (\nErrorCodes.DP_BUDGET_EXCEEDED\nif consumed_epsilon >= target_epsilon\nelse None\n)\nreturn TrainingSummary(\ndp_budget=budget,\nerror_code=err,\n)\n

We create a set of PytorchLearners by passing in the model and the datasets:

all_learner_models = []\nfor i in range(n_learners):\nmodel = Net()\nopt = torch.optim.Adam(model.parameters(), lr=learning_rate)\nlearner = PytorchLearner(\nmodel=model,\ntrain_loader=learner_train_dataloaders[i],\nvote_loader=learner_vote_dataloaders[i],\ntest_loader=learner_test_dataloaders[i],\ndevice=device,\noptimizer=opt,\ncriterion=torch.nn.NLLLoss(),\nnum_test_batches=vote_batches,\nvote_criterion=categorical_accuracy,\nminimise_criterion=False\n)\nall_learner_models.append(learner)\n

Then we give all the models the same weights to start off with:

set_equal_weights(all_learner_models)\n

And then we can move on to the final stage, which is training with Collective Learning. The function collective_learning_round performs one round of collective learning. One learner is selected to train and propose an update. The other learners vote on the update, and if the vote passes then the update is accepted. Then a new round begins.

# Train the model using Collective Learning\nresults = Results()\nresults.data.append(initial_result(all_learner_models))\nfor round in range(n_rounds):\nresults.data.append(\ncollective_learning_round(all_learner_models,\nvote_threshold, round)\n)\nplot_results(results, n_learners, score_name=score_name)\nplot_votes(results)\n# Plot the final result with votes\nplot_results(results, n_learners, score_name=score_name)\nplot_votes(results, block=True)\n
"},{"location":"colearn/mli_factory/","title":"MLI Factory","text":"

The machine learning interface factory are the minimum methods a client needs to implement to work with the GRPC Server (and become a Learner).

There are two main types of functions:

  • Supported Systems (get_models, get_dataloaders, get_compatibilities)
  • Get a MachineLearningInterface (get_mli)

When the GRPC server is connected to the Orchestrator, it will query the supported system functions to know what the MLI Factory can serve.

Later when the Orchestrator wants to run something on this Learner it will call get_mli with a model_arch_name, a dataloader_name and more parameters for both. The object returned is then used to run the experiment through the MLI.

"},{"location":"colearn/mli_factory/#supported-systems","title":"Supported Systems","text":"

The supported systems functions get_models and get_dataloaders should return a set of which will be stored (not currently implemented) in the api database. The idea being that the user can change these values on the UI while preparing to start/join an experiment."},{"location":"colearn/mli_factory/#examplemlifactory","title":"ExampleMliFactory","text":"

An example MLIFactory that will implement all the tasks in run_demo. This is the one used by contract_learn.

"},{"location":"colearn/tasks/","title":"1. CIFAR10 dataset","text":""},{"location":"colearn/tasks/#11-information-and-installation","title":"1.1. Information and installation","text":""},{"location":"colearn/tasks/#111-information-about-the-dataset","title":"1.1.1. Information about the dataset","text":"
  • The CIFAR-10 dataset consists of 60000 32x32x3 colour images in 10 classes, with 6000 images per class.
  • The 10 different classes represent airplanes, cars, birds, cats, deer, dogs, frogs, horses, ships, and trucks
  • Input for NN are raw 32x32 3 channels GRB images
  • NN output is distribution of probabilities for each class i.e. 10 values that sums up to 1

  • Code folder: here

  • Invoke parameter: -t CIFAR10
"},{"location":"colearn/tasks/#112-requirements","title":"1.1.2. Requirements","text":"
  • Cifar dataset is loaded from tensorflow.keras.datasets.cifar10 and no stored data are required
"},{"location":"colearn/tasks/#12-models","title":"1.2. Models","text":""},{"location":"colearn/tasks/#121-cifar10conv-keras-model","title":"1.2.1. CIFAR10Conv Keras model","text":"
_________________________________________________________________\nLayer (type)                    Output Shape        Param #   \n=================================================================\nInput (InputLayer)              (32, 32, 3)         0             \n_________________________________________________________________\nConv1_1 (Conv2D)                (32, 32, 64)        1792          \nbn1_1 (BatchNormalization)      (32, 32, 64)        256           \nConv1_2 (Conv2D)                (32, 32, 64)        36928         \nbn1_2 (BatchNormalization)      (32, 32, 64)        256           \npool1 (MaxPooling2D)            (16, 16, 64)        0             \n_________________________________________________________________\nConv2_1 (Conv2D)                (16, 16, 128        73856         \nbn2_1 (BatchNormalization)      (16, 16, 128        512           \nConv2_2 (Conv2D)                (16, 16, 128        147584    \nbn2_2 (BatchNormalization)      (16, 16, 128        512           \npool2 (MaxPooling2D)            (8, 8, 128)         0             \n_________________________________________________________________\nConv3_1 (Conv2D)                (8, 8, 256)         295168    \nbn3_1 (BatchNormalization)      (8, 8, 256)         1024          \nConv3_2 (Conv2D)                (8, 8, 256)         590080    \nbn3_2 (BatchNormalization)      (8, 8, 256)         1024          \nConv3_3 (Conv2D)                (8, 8, 256)         590080    \nbn3_3 (BatchNormalization)      (8, 8, 256)         1024          \n_________________________________________________________________\nflatten (Flatten)               (16384)             0             \nfc1 (Dense)                     (100)               1638500   \nfc2 (Dense)                     (10)                1010          \n=================================================================\nTotal params: 3,379,606\nTrainable params: 3,377,302\nNon-trainable params: 2,304\n_________________________________________________________________\n
"},{"location":"colearn/tasks/#122-cifar10conv2-keras-model","title":"1.2.2. CIFAR10Conv2 Keras model","text":"
_________________________________________________________\nLayer (type)                Output Shape        Param #   \n=========================================================\nInput (InputLayer)          (32, 32, 3)         0             \n_________________________________________________________\nConv1_1 (Conv2D)            (32, 32, 32)        896           \nConv1_2 (Conv2D)            (32, 32, 32)        9248          \npool1 (MaxPooling2D)        (16, 16, 32)        0             \n_________________________________________________________\nConv2_1 (Conv2D)            (16, 16, 64)        18496         \nConv2_2 (Conv2D)            (16, 16, 64)        36928         \npool2 (MaxPooling2D)        (8, 8, 64)          0             \n_________________________________________________________\nConv3_1 (Conv2D)            (8, 8, 128)         73856         \nConv3_2 (Conv2D)            (8, 8, 128)         147584    \npool3 (MaxPooling2D)        (4, 4, 128)         0             \n_________________________________________________________\nflatten (Flatten)           (2048)              0             \nfc1 (Dense)                 (128)               262272    \nfc2 (Dense)                 (10)                1290          \n=========================================================\nTotal params: 550,570\nTrainable params: 550,570\nNon-trainable params: 0\n_________________________________________________________\n
"},{"location":"colearn/tasks/#123-cifar10resnet50-keras-model","title":"1.2.3. CIFAR10Resnet50 Keras model","text":"
________________________________________________________\nLayer (type)                 Output Shape     Param #   \n========================================================\nInput (InputLayer)           (32, 32, 3)]     0             \n________________________________________________________\nresnet50 (Model)             (1, 1, 2048)     23587712  \n________________________________________________________\nGlobal_average_pooling2d     (2048)           0             \nflatten (Flatten)            (2048)           0             \nfc1 (Dense)                  (10)             20490         \n========================================================\nTotal params: 23,608,202\nTrainable params: 23,555,082\nNon-trainable params: 53,120\n________________________________________________________\n
"},{"location":"colearn/tasks/#2-covid-x-ray-dataset","title":"2. Covid X-RAY dataset","text":""},{"location":"colearn/tasks/#21-information-and-installation","title":"2.1. Information and installation","text":""},{"location":"colearn/tasks/#211-information-about-the-dataset","title":"2.1.1. Information about the dataset","text":"
  • The Covid X-Ray dataset consists of grayscale images, there are 478 covid images and 203 normal images.
  • To increase the number of images normal/pneumonia dataset is added
  • Final dataset, which is a combination of two previously mentioned datasets, contains 1434 images, 478 images for each class.
  • Images are cropped and resized to 512x512 pixel and spatial domain (Texture, GLDM, GLCM) and frequency domain (FFT and Wavelet) features are used to create 256 dimensional vector representation of each image. PCA is applied after to reduce dimensionality to 64 values which represents the first 64 highest eigenvalues of the covariance matrix.
  • Input for NN are 64 values for each image
  • NN output is distribution of probabilities for each class i.e. 3 values
  • Code folder: here
  • Invoke parameter: -t COVID
"},{"location":"colearn/tasks/#212-requirements","title":"2.1.2 Requirements","text":"
  • Download Covid dataset: here
  • Download pneumonia dataset: here
"},{"location":"colearn/tasks/#22-models","title":"2.2. Models","text":""},{"location":"colearn/tasks/#221-covid-xray-keras-model","title":"2.2.1. Covid XRAY Keras model","text":"
_________________________________________________________\nLayer (type)              Output Shape        Param #   \n=========================================================\ninput_1 (InputLayer)      (64)                0             \n_________________________________________________________\ndense (Dense)             (128)               8320          \ndropout (Dropout)         (128)               0             \n_________________________________________________________\ndense_1 (Dense)           (16)                2064          \ndropout_1 (Dropout)       (16)                0             \n_________________________________________________________\ndense_2 (Dense)           (3)                 51            \n=========================================================\nTotal params: 10,435\nTrainable params: 10,435\nNon-trainable params: 0\n_________________________________________________________\n
"},{"location":"colearn/tasks/#3-fraud-dataset","title":"3. FRAUD dataset","text":""},{"location":"colearn/tasks/#31-information-and-installation","title":"3.1. Information and installation","text":""},{"location":"colearn/tasks/#311-information-about-the-dataset","title":"3.1.1. Information about the dataset","text":"
  • EEE-CIS Fraud Detection, contains multiple files with credit card transactions
  • Raw dataset files are automatically merged and pre-processed and input files for neural network are created
  • X.csv with data - has 431 values for each transaction
  • Y.csv with labels - v has 1 value for each transaction

    • 0 = not a fraud
    • 1 = fraud
  • Code folder: here

  • Invoke parameter: -t FRAUD
"},{"location":"colearn/tasks/#312-requirements","title":"3.1.2. Requirements","text":"
  • Download dataset: here
"},{"location":"colearn/tasks/#32-models","title":"3.2. Models","text":""},{"location":"colearn/tasks/#321-frauddense1-keras-model","title":"3.2.1. FraudDense1 Keras model","text":"
_________________________________________________________\nLayer (type)             Output Shape          Param #   \n=========================================================\nInput (InputLayer)       (431)                 0             \n_________________________________________________________\ndense (Dense)            (512)                 221184    \nBatch_normalization      (512)                 2048          \n_________________________________________________________\ndense_1 (Dense)          (512)                 262656    \nBatch_normalization_1    (512)                 2048          \n_________________________________________________________\ndense_2 (Dense)          (512)                 262656    \nBatch_normalization_2    (512)                 2048          \n_________________________________________________________\nfc1 (Dense)              (1)                   513           \n=========================================================\nTotal params: 753,153\nTrainable params: 750,081\nNon-trainable params: 3,072\n_________________________________________________________\n
"},{"location":"colearn/tasks/#322-fraudsvm-scikit-learn-model","title":"3.2.2. FraudSVM Scikit-learn model","text":"
  • Model is defined as SGDClassifier(max_iter=1, verbose=0, loss=\"modified_huber\")
  • Which is support vector machine linear classifier
"},{"location":"colearn/tasks/#4-mnist","title":"4. MNIST","text":""},{"location":"colearn/tasks/#41-information-and-installation","title":"4.1. Information and installation","text":""},{"location":"colearn/tasks/#411-information-about-the-dataset","title":"4.1.1. Information about the dataset","text":"
  • This is a dataset of 70,000 28x28x1 grayscale images of the 10 digits
  • Input for NN are raw 28x28 1 channel images
  • NN output is distribution of probabilities for each class i.e. 10 values that sums up to 1

  • Code folder: here

  • Invoke parameter: -t MNIST
"},{"location":"colearn/tasks/#412-requirements","title":"4.1.2 Requirements","text":"
  • MNIST dataset is loaded from tensorflow.keras.datasets.cifar10 and no stored data are required
"},{"location":"colearn/tasks/#42-models","title":"4.2. Models","text":""},{"location":"colearn/tasks/#421-mnistconv-keras-model","title":"4.2.1. MNISTConv Keras model","text":"
_________________________________________________________\nLayer (type)                   Output Shape       Param #   \n=========================================================\nInput (InputLayer)             (28, 28, 1)        0             \n_________________________________________________________\nConv1_1 (Conv2D)               (28, 28, 64)       640           \nbn1 (BatchNormalization)       (28, 28, 64)       256           \npool1 (MaxPooling2D)           (14, 14, 64)       0             \n_________________________________________________________\nConv2_1 (Conv2D)               (14, 14, 128)      73856         \nbn4 (BatchNormalization)       (14, 14, 128)      512           \npool2 (MaxPooling2D)           (7, 7, 128)        0             \n_________________________________________________________\nflatten (Flatten)              (6272)             0             \nfc1 (Dense)                    (10)               62730         \n=========================================================\nTotal params: 137,994\nTrainable params: 137,610\nNon-trainable params: 384\n_________________________________________________________\n
"},{"location":"colearn/tasks/#422-mnist-pytorch-model","title":"4.2.2. MNIST Pytorch model","text":"
---------------------------------------------------------\nLayer (type)           Output Shape             Param #\n=========================================================\nInput                  [28,28,1]                0\nConv2d-1               [20, 24, 24]             520\nConv2d-2               [50, 8, 8]               25,050\nLinear-3               [500]                    400,500\nLinear-4               [10]                     5,010\n=========================================================\nTotal params: 431,080\nTrainable params: 431,080\nNon-trainable params: 0\n---------------------------------------------------------\n
"},{"location":"colearn/tasks/#423-mnistsupermini-keras-model","title":"4.2.3. MNISTSupermini Keras model","text":"
________________________________________________________________________________________\nLayer (type)                Output Shape    Param #      Connected to                         \n========================================================================================\ninput_1 (InputLayer)        (28, 28, 1)     0                                                \n________________________________________________________________________________________\nconv2d (Conv2D)             (26, 26, 8)     80           input_1[0][0]                        \nBatch_normalization         (26, 26, 8)     32           conv2d[0][0]                         \nMax_pooling2d               (13, 13, 8)     0            batch_normalization[0][0]            \ndropout (Dropout)           (13, 13, 8)     0            max_pooling2d[0][0]                  \n________________________________________________________________________________________\nSeparable_conv2d            (11, 11, 26)    306          dropout[0][0]                        \nbatch_normalization_1       (11, 11, 26)    104          separable_conv2d[0][0]               \ndropout_1 (Dropout)         (11, 11, 26)    0            batch_normalization_1[0][0]          \n________________________________________________________________________________________\nSeparable_conv2d_1          (11, 11, 26)    936          dropout_1[0][0]                      \n                                                         dropout_2[0][0]                      \n                                                         dropout_3[0][0]                      \n________________________________________________________________________________________\nBatch_normalization_2        (11, 11, 26)   104          separable_conv2d_1[0][0]             \ndropout_2 (Dropout)          (11, 11, 26)   0            batch_normalization_2[0][0]          \n________________________________________________________________________________________\nBatch_normalization_3        (11, 11, 26)   104          separable_conv2d_1[1][0]             \ndropout_3 (Dropout)          (11, 11, 26)   0            batch_normalization_3[0][0]          \n________________________________________________________________________________________\nBatch_normalization_4        (11, 11, 26)   104          separable_conv2d_1[2][0]             \ndropout_4 (Dropout)          (11, 11, 26)   0            batch_normalization_4[0][0]          \n________________________________________________________________________________________\nGlobal_average_pooling2d     (26)           0            dropout_4[0][0]                      \ndense (Dense)                (16)           432          global_average_pooling2d[0][0]   \nBatch_normalization_5        (16)           64           dense[0][0]                          \ndropout_5 (Dropout)          (16)           0            batch_normalization_5[0][0]          \ndense_1 (Dense)              (10)           170          dropout_5[0][0]                      \n========================================================================================\nTotal params: 2,436\nTrainable params: 2,180\nNon-trainable params: 256\n________________________________________________________________________________________\n
"},{"location":"colearn/tasks/#5-pneumonia-xray","title":"5. Pneumonia XRAY","text":""},{"location":"colearn/tasks/#51-information-and-installation","title":"5.1. Information and installation","text":""},{"location":"colearn/tasks/#511-information-about-the-dataset","title":"5.1.1. Information about the dataset","text":"
  • The Chest X-Ray Images (Pneumonia) dataset consists of 5856 grayscale images of various sizes in 2 classes (normal/pneumonia).
  • Labels are determined by folder name - NORMAL or PNEUMONIA
  • Input for NN are raw resized 128x128 1 channel images
  • NN output is distribution of probabilities for each class i.e. 2 values

  • Code folder: here

  • Invoke parameter: -t XRAY
"},{"location":"colearn/tasks/#512-requirements","title":"5.1.2 Requirements","text":"
  • Download dataset: here
"},{"location":"colearn/tasks/#52-models","title":"5.2. Models","text":""},{"location":"colearn/tasks/#521-xraysupermini-keras-model","title":"5.2.1. XraySupermini Keras model","text":"
_________________________________________________________________\nLayer (type)                     Output Shape            Param #   \n=================================================================\nInput (InputLayer)               [(128, 128, 1)]         0             \n_________________________________________________________________\nConv1_1 (Conv2D)                 (128, 128, 32)          320           \n_________________________________________________________________\nbn1 (BatchNormalization)         (128, 128, 32)          128           \n_________________________________________________________________\npool1 (MaxPooling2D)             (32, 32, 32)            0             \n_________________________________________________________________\nConv2_1 (Conv2D)                 (32, 32, 64)            18496         \n_________________________________________________________________\nbn2 (BatchNormalization)         (32, 32, 64)            256           \n_________________________________________________________________\nGlobal_max_pooling2d             (64)                    0             \n_________________________________________________________________\nfc1 (Dense)                      (1)                     65            \n=================================================================\nTotal params: 19,265\nTrainable params: 19,073\nNon-trainable params: 192\n_________________________________________________________________\n
"},{"location":"colearn/tasks/#522-xrayresnet50-keras-model","title":"5.2.2. XrayResnet50 Keras model","text":"
_________________________________________________________________\nLayer (type)                     Output Shape            Param #   \n=================================================================\nInput (InputLayer)               [(128, 128, 1)]         0             \n_________________________________________________________________\nresnet50 (Model)                 (4, 4, 2048)            23581440  \n_________________________________________________________________\nglobal_average_pooling2d         (2048)                  0             \n_________________________________________________________________\nflatten (Flatten)                (2048)                  0             \n_________________________________________________________________\nfc1 (Dense)                      (1)                     2049          \n=================================================================\nTotal params: 23,583,489\nTrainable params: 23,530,369\nNon-trainable params: 53,120\n_________________________________________________________________\n
"},{"location":"colearn/tasks/#523-xraypretrainedresnet50-keras-model","title":"5.2.3. XrayPretrainedResnet50 Keras model","text":"
_____________________________________________________________________________________\nLayer (type)                Output Shape    Param #   Connected to                \n=====================================================================================\nInput (InputLayer)          (128, 128, 1)   0                                       \n_____________________________________________________________________________________\nconcatenate (Concatenate)   (128, 128, 3)   0         Input[0][0]                                                                                     Input[0][0]                                                                                    Input[0][0]                 \n_____________________________________________________________________________________\ntf_op_layer_mul             (128, 128, 3)  0          concatenate[0][0]           \ntf_op_layer_strided_slice   (128, 128, 3)  0          tf_op_layer_mul[0][0]       \ntf_op_layer_BiasAdd         (128, 128, 3)  0          tf_op_layer_strided_slice[0][0]  \n_____________________________________________________________________________________\nresnet50 (Model)            (4, 4, 2048)   23587712   tf_op_layer_BiasAdd[0][0]            \n_____________________________________________________________________________________\nglobal_average_pooling2d    (2048)         0          resnet50[1][0]              \nflatten (Flatten)           (2048)         0          global_average_pooling2d[0][0]   \n_____________________________________________________________________________________\nfc1 (Dense)                 (1)            2049       flatten[0][0]              \n=====================================================================================\nTotal params: 23,589,761\nTrainable params: 23,536,641\nNon-trainable params: 53,120\n_____________________________________________________________________________________\n
"},{"location":"colearn/tasks/#524-xraydropout-keras-model","title":"5.2.4. XrayDropout Keras model","text":"
_________________________________________________________________\nLayer (type)                     Output Shape            Param #   \n=================================================================\nInput (InputLayer)               [(128, 128, 1)]         0             \n_________________________________________________________________\nConv1_1 (Conv2D)                 (128, 128, 128)         1280          \nbn1 (BatchNormalization)         (128, 128, 128)         512           \npool1 (MaxPooling2D)             (32, 32, 128)           0             \n_________________________________________________________________\nConv2_1 (Conv2D)                 (32, 32, 256)           295168    \nbn2 (BatchNormalization)         (32, 32, 256)           1024          \npool2 (MaxPooling2D)             (8, 8, 256)             0             \n_________________________________________________________________\nflatten (Flatten)                (16384)                 0             \nfc1 (Dense)                      (128)                   2097280   \nbn3 (BatchNormalization)         (128)                   512           \ndropout (Dropout)                (128)                   0             \n_________________________________________________________________\nfc2 (Dense)                      (64)                    8256          \nbn4 (BatchNormalization)         (64)                    256           \ndropout_1 (Dropout)              (64)                    0             \n_________________________________________________________________\nfc3 (Dense)                      (1)                     65            \n=================================================================\nTotal params: 2,404,353\nTrainable params: 2,403,201\nNon-trainable params: 1,152\n_________________________________________________________________\n
"},{"location":"colearn/tasks/#525-xraydropout2-keras-model","title":"5.2.5. XrayDropout2 Keras model","text":"
_________________________________________________________________\nLayer (type)                     Output Shape            Param #   \n=================================================================\nInput (InputLayer)               (128, 128, 1)           0             \n_________________________________________________________________\nConv1_1 (Conv2D)                 (128, 128, 64)          640           \nbn1 (BatchNormalization)         (128, 128, 64)          256           \npool1 (MaxPooling2D)             (64, 64, 64)            0             \n_________________________________________________________________\nConv2_1 (Conv2D)                 (64, 64, 128)           73856         \nbn2 (BatchNormalization)         (64, 64, 128)           512           \npool2 (MaxPooling2D)             (32, 32, 128)           0             \n_________________________________________________________________\nConv3_1 (Conv2D)                 (32, 32, 256)           295168    \nbn3 (BatchNormalization)         (32, 32, 256)           1024          \npool3 (MaxPooling2D)             (16, 16, 256)           0             \n_________________________________________________________________\nConv4_1 (Conv2D)                 (16, 16, 512)           1180160   \nbn4 (BatchNormalization)         (16, 16, 512)           2048          \npool4 (MaxPooling2D)             (8, 8, 512)             0             \n_________________________________________________________________\nConv5_1 (Conv2D)                 (8, 8, 512)             2359808   \nbn5 (BatchNormalization)         (8, 8, 512)             2048          \npool5 (MaxPooling2D)             (4, 4, 512)             0             \n_________________________________________________________________\nflatten (Flatten)                (8192)                  0             \nfc1 (Dense)                      (256)                   2097408   \nbn6 (BatchNormalization)         (256)                   1024          \ndropout (Dropout)                (256)                   0             \n_________________________________________________________________\nfc2 (Dense)                      (128)                   32896         \nbn7 (BatchNormalization)         (128)                   512           \ndropout_1 (Dropout)              (128)                   0             \n_________________________________________________________________\nfc3 (Dense)                      (64)                    8256          \nbn8 (BatchNormalization)         (64)                    256           \ndropout_2 (Dropout)              (64)                    0             \n_________________________________________________________________\nfc4 (Dense)                      (1)                     65            \n=================================================================\nTotal params: 6,055,937\nTrainable params: 6,052,097\nNon-trainable params: 3,840\n_________________________________________________________________\n
"},{"location":"colearn/tasks/#526-xrayvgg16-keras-model","title":"5.2.6. XrayVGG16 Keras model","text":"
_____________________________________________________________________________________\nLayer (type)                 Output Shape     Param #    Connected to                \n=====================================================================================\nInput (InputLayer)           (128, 128, 1)    0                  \n_____________________________________________________________________________________\nconcatenate (Concatenate)    (128, 128, 3)    0          Input[0][0]                 \n                                                         Input[0][0]                 \n                                                         Input[0][0]                 \n_____________________________________________________________________________________\ntf_op_layer_mul               (128, 128, 3)    0         concatenate[0][0]           \nTf_op_layer_strided_slice     (28, 128, 3)     0         tf_op_layer_mul[0][0]       \ntf_op_layer_BiasAdd           (128, 128, 3)    0         tf_op_layer_strided_slice[0][0]  \n_____________________________________________________________________________________\nvgg16 (Model)                 (4, 4, 512)      14714688  tf_op_layer_BiasAdd[0][0]            \n_____________________________________________________________________________________\nflatten (Flatten)             (8192)           0         vgg16[1][0]                  \nfc1 (Dense)                   (1)              8193      flatten[0][0]                \n=====================================================================================\nTotal params: 14,722,881\nTrainable params: 14,722,881\nNon-trainable params: 0\n_____________________________________________________________________________________\n
"},{"location":"colearn/tasks/#527-xraymini-keras-model","title":"5.2.7. XrayMini Keras model","text":"
_________________________________________________________________\nLayer (type)                     Output Shape            Param #   \n=================================================================\nInput (InputLayer)               [(128, 128, 1)]         0             \n_________________________________________________________________\nConv1_1 (Conv2D)                 (128, 128, 128)         1280          \nbn1 (BatchNormalization)         (128, 128, 128)         512           \npool1 (MaxPooling2D)             (32, 32, 128)           0             \n_________________________________________________________________\nConv2_1 (Conv2D)                 (32, 32, 256)           295168    \nbn2 (BatchNormalization)         (32, 32, 256)           1024          \npool2 (MaxPooling2D)             (8, 8, 256)             0             \n_________________________________________________________________\nflatten (Flatten)                (16384)                 0             \nfc1 (Dense)                      (1)                     16385         \n=================================================================\nTotal params: 314,369\nTrainable params: 313,601\nNon-trainable params: 768\n_________________________________________________________________\n
"},{"location":"colearn/tasks/#527-xrayonemb-keras-model","title":"5.2.7. XrayOneMB Keras model","text":"
_________________________________________________________________\nLayer (type)                   Output Shape            Param #   \n=================================================================\nInput (InputLayer)             (128, 128, 1)           0             \n_________________________________________________________________\nConv1_1 (Conv2D)               (128, 128, 64)          640           \nbn1_1 (BatchNormalization)     (128, 128, 64)          256           \nConv1_2 (Conv2D)               (128, 128, 64)          36928         \nbn1_2 (BatchNormalization)     (128, 128, 64)          256           \npool1 (MaxPooling2D)           (64, 64, 64)            0             \n_________________________________________________________________\nConv2_1 (Conv2D)               (64, 64, 64)            36928         \nbn2_1 (BatchNormalization)     (64, 64, 64)            256           \nConv2_2 (Conv2D)               (64, 64, 64)            36928         \nbn2_2 (BatchNormalization)     (64, 64, 64)            256           \npool2 (MaxPooling2D)           (32, 32, 64)            0             \n_________________________________________________________________\nConv3_1 (Conv2D)               (32, 32, 128)           73856         \nbn3_1 (BatchNormalization)     (32, 32, 128)           512           \nConv3_2 (SeparableConv2D)      (32, 32, 128)           17664         \nbn3_2 (BatchNormalization)     (32, 32, 128)           512           \npool3 (MaxPooling2D)           (16, 16, 128)           0             \n_________________________________________________________________\nConv4_1 (SeparableConv2D)      (16, 16, 128)           17664         \nbn4_1 (BatchNormalization)     (16, 16, 128)           512           \n_________________________________________________________________\nConv4_2 (SeparableConv2D)      (16, 16, 128)           17664         \nbn4_2 (BatchNormalization)     (16, 16, 128)           512           \n_________________________________________________________________\npool4 (AveragePooling2D)       (4, 4, 128)             0             \nflatten (Flatten)              (2048)                  0             \n_________________________________________________________________\nfc1 (Dense)                    (1)                     2049          \n=================================================================\nTotal params: 243,393\nTrainable params: 241,857\nNon-trainable params: 1,536\n_________________________________________________________________\n
"},{"location":"create-react-app/example_dapp/","title":"\"Hello Cosmos\"","text":"

\"Hello World\" but with friends!

Projects generated from the template will include a fully functional example DApp frontend, integrating with the current Fetch test-net directly as well as a cosmwasm contract deployed to the test-net.

The \"Hello Cosmos\" example DApp demonstrates the following kinds of interactions:

  • CosmWasm contract query
  • CosmWasm contract call
  • Native token transfer
  • Fetch / Keplr wallet integration
"},{"location":"create-react-app/example_dapp/#features","title":"Features","text":"
  • Print greetings submitted by other users
  • Connect with Fetch wallet
    • Print address
    • Print balance
  • Submit greeting
    • (one greeting per address)
  • Tip other users
    • (half testnet balance)
"},{"location":"create-react-app/introduction/","title":"Create React App Template","text":"

Create React App is a CLI utility which facilitates generation of react app boilerplate via templates.

In order to expedite distributed application (DApp) development, we maintain a create-react-app template: cra-template-cosmjs-keplr. Projects generated from this template include all the necessary dependencies and build configuration needed to:

  • Interact with the Fetch or Keplr wallet
    • fetchai/fetch-wallet (github)
  • Interact with cosmos-based networks
    • @cosmjs/stargate (npm)
    • @cosmjs/cosmwasm-stargate (npm)

To generate a new project:

npx create-react-app --template cosmjs-keplr\n

The generated project also comes with an example DApp, \"Hello Cosmos\", which demonstrates Cosmjs and Keplr API usage in the form of a simple frontend.

"},{"location":"fetch-wallet/","title":"Getting Started","text":"

The Fetch Wallet, originally forked from the Keplr wallet, is a generic wallet for interacting with the Fetch blockchain network and other ledgers built using the Cosmos-SDK, and supports the Inter-Blockchain Communication (IBC) protocol.

Some of its highlights include:

  • Private keys and mnemonics are encrypted (using scrypt) and are stored locally on device. This means neither Fetch nor the websites visited have access to these sensitive data.
  • Multiple accounts on the Fetch ledger and other Cosmos-based ledgers.
  • Support for Ledger hardware wallets for enhanced security.
  • Native and IBC token transfers.
"},{"location":"fetch-wallet/#compatibility","title":"Compatibility","text":"

The Fetch wallet works on all Chromium-based web browsers, including Chrome, Brave, Edge and Decentr.

"},{"location":"fetch-wallet/#get-the-wallet","title":"Get the Wallet","text":"

Install the Fetch wallet from the Chrome web store.

Info

At this time, you cannot run the Keplr and Fetch wallets together because they interfere. Please disable the Keplr wallet before using the Fetch wallet.

"},{"location":"fetch-wallet/#version","title":"Version","text":""},{"location":"fetch-wallet/#first-time-use","title":"First-time use","text":"

The first time you open the wallet, you will see the following options:

  • Create a new account
  • Import existing account
  • Import ledger
  • Migrate from ETH

After account creation or import is completed, the wallet will be accessible from your browser's extensions.

"},{"location":"fetch-wallet/#how-to-contribute","title":"How to contribute","text":"

You can contribute to this project by engaging with its repository on GitHub:

GitHub repository

"},{"location":"fetch-wallet/account_management/","title":"Account Management","text":""},{"location":"fetch-wallet/account_management/#welcome-page","title":"Welcome Page","text":"

You can create a new account, import an existing account, connect your hardware wallet, or recover migrated Ethereum accounts from the welcome page. To get there:

"},{"location":"fetch-wallet/account_management/#first-time-use","title":"First time Use","text":"

The first time you open the wallet, you will be presented with the welcome page.

"},{"location":"fetch-wallet/account_management/#not-the-first-time","title":"Not the First Time","text":"
  1. Ensure you are logged into the wallet.
  2. Click the account icon in the top right corner of the dashboard, then + Add Account.

Info

In some contexts, the term account refers to an address that has, at some point in time, had a balance (and therefore a state on the ledger). In this context, it is not necessary for an account to have a balance, for example for it to be imported.

"},{"location":"fetch-wallet/account_management/#creating-a-new-account","title":"Creating a new account","text":"

Using the wallet, you can create a new account and address on the Fetch ledger:

  1. On the welcome page, click Create new account.
  2. Choose a 12 or 24 word long mnemonic seed and securely back it up.

    Warning

    KEEP IT SAFE! Anyone with your mnemonic seed can access your wallet and take your assets.

    Danger

    DON'T LOSE IT! Lost mnemonic seed cannot be recovered! If you lose your mnemonic seed you will lose access to your wallet.

  3. Give your account a name and set a password if one is not set (i.e. if it is the first time you open the wallet, or in case you have removed all of your accounts). The password will be used the next time you want to use the wallet or make important changes to your account. Hit Next.

  4. Rearrange the mnemonic phrases by clicking on them in the correct order to confirm your mnemonic seed. Then click Register.
"},{"location":"fetch-wallet/account_management/#existing-account","title":"Existing account","text":""},{"location":"fetch-wallet/account_management/#importing-an-existing-account","title":"Importing an existing account","text":"

If you have an account on the Fetch network, for example having had one already on the Fetch wallet and want to access it again, have an account on another wallet (e.g. Cosmostation, Keplr, ...) and wish to bring it to the Fetch wallet, or having created an address using one of our tools (e.g. the AEA framework), you can import it into the Fetch wallet:

  1. On the welcome page, click Import existing account.
  2. Enter your mnemonic seed (set of words) or private key (hexidecimal).

    Warning

    KEEP IT SAFE! Anyone with your mnemonic seed or private key can access your wallet and take your assets.

    Tip

    It is more convenient, and preferable, to use the mnemonic over private keys.

  3. Give your account a name and set a password if one is not set (i.e. if it is the first time you open the wallet, or in case you have removed all of your accounts). The password will be used the next time you want to use the wallet or make important changes to your account. Hit Next.

"},{"location":"fetch-wallet/account_management/#using-a-hardware-wallet","title":"Using a Hardware Wallet","text":"

If you have a Ledger hardware wallet and wish to keep your key and mnemonics on that device while using the Fetch wallet:

Info

Currently only ledger hardware wallets are supported.

  1. On the welcome page, click Import ledger.
  2. Give your account a name and set a password if one is not set (i.e. if it is the first time you open the wallet, or in case you have removed all of your accounts). The password will be used the next time you want to use the wallet or make important changes to your account. Hit Next.
  3. Follow the instructions on the popup to connect your device.

Warning

Please ensure you keep your mnemonic seed somewhere safe where others cannot access it. If you lose it, your wallet will be inaccessible once you log out. The password for your account should also be kept safe but is not necessary for recovery if you have your mnemonic seed.

Info

If you lose your password, you need to uninstall and re-install the Fetch wallet and select Import existing account. Then use the mnemonic seed for your account and choose a new password.

"},{"location":"fetch-wallet/account_management/#switching-accounts","title":"Switching accounts","text":"

If you have multiple accounts set up on the Fetch wallet, to switch between them:

  1. Ensure you are logged into the wallet.
  2. Click the account icon in the top right corner of the dashboard.
  3. Select the account you want to switch to.
"},{"location":"fetch-wallet/account_management/#removing-an-account","title":"Removing an Account","text":"

To remove an account from your Fetch wallet:

  1. Ensure you are logged into the wallet.
  2. Click the account icon in the top right corner of the dashboard.
  3. Hit ... (the three dots icon) for the account you want to remove and choose Delete Account.
  4. Enter your wallet password.

    Warning

    If you have not yet backed up your mnemonic seed, click on Back-up account and enter you password to view it. Then back it up safely. If you lose your mnemonic seed you will lose access to your account.

  5. Click Confirm to remove the account from your wallet.

"},{"location":"fetch-wallet/address_book/","title":"Address Book","text":"

The Fetch wallet's Address Book makes it easy to transfer funds to accounts you frequently use. To add an address to the address book:

  1. From the wallet dashboard click the icon in the top left, then Address Book.
  2. Select the network this address belongs to, then + Add New.
  3. Fill in the details:
    • Name: the name you give to this account
    • Address: the account's address
    • Default memo: the memo used by default when sending funds to this account
  4. Hit Save.

Tip

When sending funds, instead of writing the address of the recipient you can hit the icon to the right to select an account from the address book.

"},{"location":"fetch-wallet/connections/","title":"Connections","text":"

Some webpages can connect to your wallet and enable interactions that are otherwise not possible.

Example

By default, the fetch ledger browser only shows a list of active validators for the Fetch network along with some staking-related details (e.g. the validators' commission rates and number of delegated FETs).

However if you have your wallet set up on the browser, you will see a button for connecting your wallet in the top right of the page, and if you do, you will get the option of staking your FETs with any of the validators on the list and accessing your staking dashboard. To learn more, visit the staking guide.

If you view such webpages using a browser with the Fetch wallet, you will see additional options. If you use a browser with no wallet, those options will not be shown.

You can manage these connections on the wallet:

  1. From the wallet dashboard, click the icon in the top left, then Settings.
  2. Click on Manage Connections.

    Info

    Here you will see a list of webpages which have been connected to your wallet. Visiting these pages will not ask you for a connection with your wallet again; the page will automatically connect.

    You have the option of removing and thus revoking these connections. If you do, the next time you visit that page, it will not automatically connect to your wallet.

  3. To remove a connection, click the icon on the connection you wish to remove.

"},{"location":"fetch-wallet/deposit/","title":"Deposit Tokens","text":"

To transfer funds to your account on the Fetch wallet:

In the wallet or application you are using to send the funds, use your account's address as the destination account to which the funds must go.

"},{"location":"fetch-wallet/deposit/#to-copy-your-accounts-address","title":"To copy your account's address","text":"
  1. Ensure you are logged into the wallet.
  2. Either click on the account address at the top of the dashboard (under the account name):
  3. Or select Deposit and scan the QR code.

Once you send the tokens, the balance should be updated.

Failure

If your origin wallet says that the address (which should start with \"fetch\") is invalid, it is probably expecting an Ethereum address (beginning with \"0x\") and is most likely trying to send ERC20 FET. In this case, you need to use the token bridge to swap your ERC20 FET for native FET.

Warning

You should not send ERC20 FET to this wallet. If you do, you will lose your tokens. The Fetch wallet can only hold native FET tokens and not ERC20 FET tokens.

"},{"location":"fetch-wallet/migrate_erc20/","title":"Recover Migrated Ethereum Account","text":"

Prior to the transition to our native Cosmos SDK-based ledger, ERC20 FETs staked using accounts on Ethereum were migrated to native accounts on the Fetch main network. These accounts correspond to the private key of their associated Ethereum accounts.

These native accounts can be accessed by transforming the original Ethereum keypair into a native Fetch keypair and address:

  1. On the welcome page, click Migrate from ETH.
  2. In the next page, click Migrate a Metamask Private Key.
  3. Enter the address and private key of the Ethereum account which staked ERC20 FETs.
  4. Give your account a name and set a password if one is not set (i.e. if it is the first time you open the wallet, or in case you have removed all of your accounts). The password will be used the next time you want to use the wallet or make important changes to your account. Hit Next.
"},{"location":"fetch-wallet/send_tokens/","title":"Send Tokens","text":"

On this page, you can find instructions on how to send tokens using native transfers and the Inter-Blockchain Communication (IBC) protocol.

"},{"location":"fetch-wallet/send_tokens/#native-network-transfer","title":"Native network transfer","text":"
  1. Ensure you are logged into the wallet.
  2. Select Send.
  3. Fill in the details of your transaction:
    • Recipient: the address you want to send the tokens to
    • Token: the token denomination or type
    • Amount: the number of tokens you want to send with this transaction (you can see your current balance above the Amount)
    • Memo (Optional): some transactions (e.g. to/from some exchanges) require a specific memo. If not needed, you can leave it blank.
    • Fee: the transaction fee. Choose from Low, Average and High

    Tip

    Usually, the lower the transaction fee, the longer you need to wait for your transaction to be settled on the network.

  4. Press Send.
  5. In the summary screen, review the details and if everything is correct, select Approve.

Info

You can check the status of your transaction via the explorer.

"},{"location":"fetch-wallet/send_tokens/#ibc-transfer","title":"IBC transfer","text":"
  1. Ensure you are logged into the wallet.
  2. Make sure IBC transfers are enabled and that your selected network supports it.

    Tip

    To enable IBC transfers in the wallet:

    From dashboard, click the icon in the top left, then Settings. Toggle the Show Advanced IBC Transfers switch on.

    Note

    If your selected network does not support IBC transfers, you will not see an IBC Transfer section in the dashboard.

  3. Click the Transfer button in the IBC Transfer section.

  4. Fill in the details of your transaction:

    • Destination Chain: the destination blockchain.

      Info

      If you do not see your desired chain, you need to set up IBC channels first.

    • Recipient: the address you want to send the tokens to

    • Memo (Optional): an optional memo.
  5. Press Next.

  6. In the next page, provide the following:
    • Token: the denomination or type of the tokens being sent
    • Amount: the number of tokens to be sent
    • Fee: the transaction fee. Choose from Low, Average, High.
  7. Hit Submit.
  8. The wallet now shows you a summary of the transaction. Review it and if you are happy, hit Approve to complete the transfer.

Warning

Do not send tokens via IBC directly to an exchange. In most cases, this will result in the loss of your funds.

"},{"location":"fetch-wallet/send_tokens/#first-time-origindestination-transfer","title":"First-time origin/destination transfer","text":"

Before being able to make an IBC transfer between any two chains for the first time, an IBC channel must be configured in the wallet:

  1. Follow the instructions for making an IBC transfer, up to Step 4.
  2. Click the Select Chain drop-down.
  3. Select + New IBC Transfer Channel.
  4. Select the Destination Chain and enter the source Channel ID (e.g. channel-100).

    Tip

    To find out the IBC channel ID between any two chains:

    1. Head over to this page
    2. At the top, select the sending chain, for example Fetch.AI. Then below it, click IBC RELAYERS.
    3. Select the destination chain, for example OSMOSIS.
    4. You can now see the channels between the two chains. Select an active channel (in green) and note the sending chain's channel ID. For Fetch.AI to Osmosis, the sending chain's (Fetch.AI's) channel ID is channel-10.
    5. Enter the channel ID in the Fetch wallet (channel-10 in our example).

    Info

    Remember to write the channel ID in lower case (i.e. channel-X)

    Warning

    If there are no green channels, the relayers are temporarily inactive. You need to wait until one becomes active again.

  5. Click Save.

Failure

If you input an incorrect Channel ID, either the wallet will not accept it and shows you an error, or your transaction could get stuck in an inactive channel.

"},{"location":"fetch-wallet/stake/","title":"Staking","text":"

Clicking the Stake button on the Fetch wallet dashboard opens the Fetch ledger browser. From here, you can connect your wallet and stake your FETs. To learn how, follow the staking guide.

"},{"location":"fetch-wallet/stake/#claim-rewards","title":"Claim rewards","text":"

If your account has any tokens staked, you can withdraw your rewards from the wallet:

  1. Ensure you are logged into the wallet.
  2. From the wallet dashboard, select Claim.

    Info

    This will claim the total rewards accrued for your stakes across every validator.

  3. The wallet shows you a summary of the transaction. Review it, select a transaction fee, and if you are happy, hit Approve to complete the operation.

You should now see the rewards added to your Total Balance.

Info

To claim rewards from each validator individually, see the claim rewards using the staking dashboard.

"},{"location":"learn_the_concepts/agent-based_and_multi-agents_systems/","title":"Agent-Based and Multi-Agents Systems","text":"

An agent is a piece of software that represents an entity (individual, organisation, or object) and acts continuously and autonomously (with limited or no interference) on their behalf.

The most important defining characteristic of an agent is its autonomy, that is the ability to act on its own without external direction from its owner in response to situations it encounters.

Note

What differentiates agents with other software paradigms (e.g. smart contracts, web apps) is that as well as being reactive, meaning they respond to other agents and changes in their environments, they are also proactive, which means that they take the initiative and perform actions to achieve their goals.

"},{"location":"learn_the_concepts/agent-based_and_multi-agents_systems/#key-features-of-agents","title":"Key features of agents","text":"
  1. Representation: Agents are owned by and operate on behalf of an entity, for example an individual, family, company, government, or object, and look after their owner's interests.

  2. Autonomy: Agents operate with limited or no interference and do not need to be constantly told what to do. They perform actions continuously according to their internal reasoning system.

  3. Self-Interested: Each agent primarily looks after its own interests (which is aligned with those of its owner) and not necessarily the interests of other agents.

  4. Proactive: Agents have goals to achieve allowing them to take the initiative and perform actions that get them closer to achieving their goals. This means an agent typically compares the outcome of different actions relative to its goals and selects the one that takes it closer to them.

  5. Reactive: Agents respond to other agents, services, etc., and changes in their environments. A great example of this behaviour is a heating system with a thermostat that constantly monitors its environment and turns the heating on or off when the temperature changes.

Note

Agent characteristics and behaviors may vary in their extent and sophistication. An agent may differ from other agents, in the amount of information in its decisions process, its internal models of the external world, its view of the possible reactions of other agents in response to its actions, and the size of the memory of past events the agent retains and uses in making its decisions. Further instances of differentiating factors are the amounts of resources used by agents or accumulated as a result of their interactions.

"},{"location":"learn_the_concepts/agent-based_and_multi-agents_systems/#agent-based-modelling","title":"Agent-Based Modelling","text":"

In agent-based modelling (ABM), a system is represented as a group of independent decision-making units, i.e. agents. Each agent evaluates its own circumstances and makes decisions based on a set of rules. Agents can act in a variety of ways that are suitable for the system they are representing (e.g. producing, consuming, or selling). An agent-based model, at its most basic form, consists of a system of agents and the connections between them. Even a straightforward agent-based model has the potential to display intricate behavioural patterns and provide important details about the behaviour of the emulated real-world system.

Agent-based modelling is used in various application areas, spanning the physical, biological, social, and management sciences. Agent-based models have also been developed in the fields of economics, sociology, anthropology, and cognitive science. Various social phenomena have been investigated using agent-based models that are not easily modeled using other approaches.

Example

Agent-based models can be used to analyze existing and hypothetical markets, for instance, modelling possible futures for a market directed towards space tourism, to analyze how companies represented by agents would compete and offer products to customers in this hypothetical market.

"},{"location":"learn_the_concepts/agent-based_and_multi-agents_systems/#multi-agents-systems","title":"Multi-Agents Systems","text":"

A Multi-Agents System (MAS) is a group of agents that interact with each other and the environment to achieve specific goals. In such systems, agents may not have full knowledge of both the environment and the internal state of other agents.

Note

Interactions between agents is an important feature that enables them to use knowledge of other agents and learn more about the environment in a compressed time period. This type of interaction may be cooperative or competitive. In a cooperative interaction, agents work with each other towards a common goal. The aim of this interaction is to enable agents to distribute and share their knowledge and use the intelligence and capabilities of each other to solve problems. In a competitive interaction, agents may compete to obtain individual resources and achieve individual goals.

"},{"location":"learn_the_concepts/agent-based_and_multi-agents_systems/#benefits","title":"Benefits","text":"
  1. Decentralization: multi-agents systems are decentralized. The authority to make management decisions about the system is distributed among the participants. A multi-agents system is run for its participants by its participants.

  2. Heterogeneity: agents in multi-agent systems are owned and operated by different entities and so are typically not uniformly designed. For instance, the agents may be composed of different hardware or software components.

  3. Scalability: multi-agents systems are easy to scale. You simply add agents to the system to grow the system.

  4. Robustness: multi-agents systems are intrinsically very robust as no single failure point takes down the system.

  5. Adaptability: multi-agents systems adapt to changing circumstances effectively as each agent autonomously adjusts to changing circumstances.

"},{"location":"learn_the_concepts/agent-based_and_multi-agents_systems/#drawbacks","title":"Drawbacks","text":"
  1. Complexity: multi-agents systems are quite complex to set up, maintain and troubleshoot. You have to use a pre-existing framework or build one, and getting all the agents to communicate effectively together can be a challenge. As these are decentralized systems (i.e. no central authority), managing the system is more complicated than a centralized one, as participants need to be involved in management decisions. The complexity of MASs can increase rapidly with the number of agents, the interactions among them, and the complexity of their behavior.

  2. Unpredictability: with every agent acting in their own interest, it might be easy to predict what an individual agent is trying to achieve. However, it is much more difficult to predict the direction the whole system moves towards, as this is the result of the many interactions between every individual agent in the system.

Multi-agent systems have received tremendous attention from scholars in different disciplines, ranging from computer science to civil engineering, in order to solve complex problems by subdividing them into smaller tasks. The individual tasks are allocated to autonomous agents, and each one of them decides on a proper action to solve the task using multiple inputs.

"},{"location":"learn_the_concepts/glossary/","title":"Glossary","text":"
  • ACN (Agent Communication Network): it is a peer-to-peer communication network for autonomous economic agents.

  • Address: an address is a string of alphanumeric characters (that may also be seen as a QR code that can be read by a smartphone) and it is used on a blockchain network to transmit and receive transactions. The address in Ethereum starts with 0x (e.g. ) and is also called public key.

  • AEA (Autonomous Economic Agent): it is an intelligent agent acting on an owner's behalf, with limited or no interference, and whose goal is to generate economic value to its owner. AEAs are a special type of agent.

  • AEA Registry: it is a repository of packages for the Autonomous Economic Agent (AEA) framework. Five types of packages are currently supported: entire agents and four agent components, including skills, connections, protocols and contracts.

  • Airdrop: it is a mean of distributing tokens to wallet addresses using digital money or tokens. Airdrops are occasionally used for marketing in return for easy actions like sharing, referring, or downloading an app.

  • Altcoin: any cryptocurrency alternative to Bitcoin. Many alternative cryptocurrencies are modified forks of Bitcoin (e.g. Litecoin).

  • AML (Anti-Money Laundering): this refers to a group of international rules implemented to reduce the likelihood that criminal organizations or individuals would use money laundering. In different countries, these guidelines and legislation are applied to cryptocurrencies with diverse results.

  • Arbitrage: arbitrage is the simultaneous purchase and sale of the same asset in different markets in order to profit from tiny differences in the asset's listed price.

  • API (Application Programming Interface): it is a software bridge that enables communication between two independent programmes. APIs specify how different components will communicate with one another.

  • Automated Market Maker: an automated market maker (AMM) is a system that automatically facilitates buy and sell orders on a decentralized exchange. In contrast to regular market makers, AMMs function by using self-executing computer programs, also known as smart contracts. These smart contracts automatically clear transactions between buyers and sellers.

  • BEP-20: according to Binance Academy, \"BEP-20 is a token standard on Binance Smart Chain that extends ERC-20, the most common Ethereum token standard. You can think of it as a blueprint for tokens that defines how they can be spent, who can spend them, and other rules for their usage. Due to its similarity to Binance Chain\u2019s BEP-2 and Ethereum\u2019s ERC-20, it\u2019s compatible with both.BEP-20 was conceived as a technical specification for Binance Smart Chain, with the goal of providing a flexible format for developers to launch a range of different tokens. These could represent anything from shares in a business to dollars stored in a bank vault\".

  • Bid and Ask Prices: the term bid and ask refers to a two-way price quotation that indicates the best potential price at which a security can be sold and bought at a given point in time. The bid price represents the maximum price that a buyer is willing to pay for a share of stock or other security. The ask price represents the minimum price that a seller is willing to take for that same security. A trade or transaction occurs when a buyer in the market is willing to pay the best offer available, or is willing to sell at the highest bid. The difference between bid and ask prices, or the spread, is a key indicator of the liquidity of the asset. In general, the smaller the spread, the better the liquidity.

  • Block: it is an essential part of every blockchain. Imagine a blockchain as a continuously updated ledger that is synchronized between any number of distinct nodes (in fact, the term \"distributed ledger technology\" is also used to describe it). A block of transactions is cryptographically locked together and formally recorded when a predetermined number of transactions have been added to the ledger and consensus has been obtained among the nodes that the transactions are authentic. This \"block\" serves as the foundation for the following one. As a result, they are all connected in a chain, thus the name blockchain.

  • Block Depth: A block's position index in the blockchain relative to the latest (most recently added) block. For instance, a block that is five blocks before the latest block will have a block depth of 5.

  • Block Explorer: it is a software with a graphical user interface (GUI), that allows users to read and analyze the data contained on a blockchain.

  • Block Height: a block's position index in the blockchain relative to the genesis (zeroeth) block. It represents the number of blocks on a blockchain that are interconnected. For instance, the initial block (i.e. Genesis Block), would have height 0 whereas the 5th block added to a chain would have a block height of 5.

  • Block Reward: the payment made to a miner for successfully hashing a block of transactions. Cryptocurrencies and transaction fees are two possible types of block rewards. Whether or not all of the coins have previously been successfully mined depends on the policy followed by the relevant cryptocurrency.

  • Block Period: the length of time it takes for a block of transactions to be confirmed by the network, either by miners under PoW or by validators under PoS, is referred to as \"block time.\" Also see \"Proof of Work\" and \"Proof of Stake\".

  • Blockchain: the blockchain is a digital ledger of all the transactions ever made in a particular cryptocurrency. It\u2019s composed of individual blocks that are chained to each other through a cryptographic signature. Each time a block\u2019s capacity is reached, a new block is added to the chain. The blockchain is repeatedly copied and saved onto thousands of computers all around the world, and it must always match each copy. As there is no master copy stored in one location, it\u2019s considered decentralized.

  • Bug: mistakes in software or other aspects of a program. These mistakes may produce an error in the form of unexpected results or erratic behavior. In the best case, a bug may only affect software performance. In the worst case, it may make the software crash.

  • Bug Bounty: a reward given for disclosing bugs and vulnerabilities in computer programming.

  • Circulating Supply: this term refers to the number of cryptocurrency coins or tokens that are publicly available and circulating in a specific market. The circulating supply of a cryptocurrency can increase or decrease over time.

  • Cold Wallet: a physical storage device used to store cryptocurrencies offline (e.g. a flash drive, hard disk).

  • Collateral: a collateral is something of value given as a guarantee to obtain something else. For instance, a borrower may offer their car as a collateral to a lender when taking out a loan. The vehicle acts as a safeguard or warranty in case the borrower fails to pay their debts. Usually, collateralized loans present much lower interest rates when compared to non-collateralized ones. Collateral can come in different forms. Some of the most common types include mortgage collaterals, invoice financing and margin trading collaterals.

  • Collateralization Ratio: to borrow tokens from a DeFi protocol, one must put up more collateral than the loan is worth. Each protocol can use different collateralization ratios. However, users must maintain the designated ratio to prevent liquidation (i.e. liquidation ratio).

  • Command Line Interface (CLI): it is a text-based user interface. CLIs can provide more core functionality and access to system resources than a graphical user interface (GUI), but at the cost of usability. Because of this, CLIs are generally directed toward developers over the average user. They can be used to demonstrate the functionality underlying a program without expending development time building a more robust user interface.

  • Confirmation: when the blockchain transaction has been validated by the network, a confirmation occurs. This occurs through a process called mining under a Proof of Work (PoW) consensus mechanism and as validation under a Proof of Stake (PoS) consensus mechanism. Theoretically, a transaction that has been successfully confirmed cannot be changed or double spent. The difficulty of a double spend assault increases with the number of confirmations a transaction has.

  • Consensus: the process through which a number of peers, also known as nodes, on a blockchain network come to an agreement over the legitimacy of transactions presented to the network. Proof of Work (PoW) and Proof of Stake (PoS) are the two main consensus procedures.

  • Cryptocurrency: it is a digital currency which has a mathematical foundation that controls the generation of units of currency and using encryption methods to confirm the transfer of payments. Distributed ledger technology is used to keep track of cryptocurrency transactions, which are carried out without the assistance of a central bank.

  • Cryptography: it is the process of encoding data into unintelligible codes to make it secure and hidden. Only the required key may be used to decode and read the data.

  • Crypto Wallet: these wallets store public and private keys allowing users to send, receive, and store tokens. The tokens reside on the blockchain and the wallet accesses them.

  • DAO: a Decentralized Autonomous Organization (DAO) is a collection of individuals that get together without having any decisions made for them by a centralised authority figure or corporation. These are constructed using smart contracts on a blockchain. Members of DAOs frequently pay their way in by buying a governance token designed just for the DAO, which gives them the right to vote on decisions affecting how the money fund is used and handled. People from all around the world may be a part of these organisations, and they frequently interact on Discord channels.

  • Decentralization: it is the term used to describe the transition of power and control to a decentralized system from a centralized structure, government, or party.

  • Decentralized Application (DApp): a computer program that utilizes a blockchain for data storage, runs autonomously, is not controlled or operated from a single entity, is open source and has its use incentivized by the reward of fees or tokens.

  • Decentralized Exchange (DEX): this is a platform which uses smart contracts to exchange cryptocurrencies. Peer-to-peer trading takes place between liquidity pools. This contrasts with centralised exchanges, which are more comparable to cryptocurrency-focused banks or financial firms. The two have significant, ever-changing technological and regulatory variances from one another.

  • Decentralized Finance (DeFi): DeFi stands for decentralized finance. Traditional finance has always relied on a reputable middleman, and because of this, it has always been centralized. For instance, if you need to send money to a friend or family, you depend on your bank to transfer the funds to their account. DeFi, on the other hand, does not need any middlemen. Direct asset transfers are possible between participants. Theoretically, this speeds up and reduces the cost of transactions.

  • Deflationary Token: tokens are deflationary if a percentage is permanently removed from the marketplace over time. Buybacks and burns are a popular way of destroying tokens. This causes scarcity which hopefully makes the price rise.

  • Digital Asset: a scarce, electronically transferred, intangible digital asset having a market price.

  • Digital Identity: a networked or online identity that a person, business, or technological item adopts.

  • Digital Signature: a public key encryption-generated code that is added to an electronic document transmission to validate the document's contents.

  • Distributed Denial of Service (DDoS) Attack: a cyberattack strategy where the attacker repeatedly floods the system with requests in an effort to block the fulfillment of valid requests.

  • DLT (Distributed Ledger Technology): it is a database of data that is duplicated and shared among a network of computers spread out across many locations. It is a flexible way of information recording as opposed to a centralised ledger. Instead of being updated by a centralised authority, the ledger's or record's data is changed by network participants. The information kept on the ledger can be checked and audited, and it can be accessible to some or all users. Value-containing peer-to-peer transfers may be documented on a DLT. The transferred value could be cash, securities, or even private data.

  • Double Spend Attack: it is a malicious attempt to convince two separate parties that one of two conflicting transactions is valid. In such a situation, both transactions appear individually valid, but their combination is not. Thus, only one is included in the blockchain. Due to the nature of blockchain reorganizations, simply showing that a transaction is included in a block is not enough to verify that it is immutable. Transactions are only immutable once they have reached a depth in the chain where a chain reorganization is unlikely to affect them. Double spend attacks can be mitigated by waiting to ensure that a transaction is confirmed by the network and is acceptably immutable before acting on it.

  • ERC-20: it is the standard to which each Ethereum token complies. This standard defines the way each token behaves so that transactions are predictable. Other cryptocurrencies also use the ERC-20 standard, piggybacking on the Ethereum network in the process.

  • ERC-721: this is an additional standard for Ethereum smart contracts that permits the creation of non-fungible tokens, or NFTs. This token standard is used to symbolize a distinct digital asset that cannot be exchanged.

  • Exchange: service for trading cryptocurrency tokens for other tokens or fiat. Exchanges are highly regulated in the European Union, eastern Asia, and the United States of America; thus, many exchanges are located in countries with less oversight.

  • Fiat: according to Investopedia's definition, \"fiat money is a government-issued currency that is not backed by a physical commodity, such as gold or silver, but rather by the government that issued it. It usually requires fiat exchanged at a CEX or through local means such as Bitcoin ATMs to be able to purchase cryptocurrency with fiat currency\".

  • Fork: a fork establishes a different version of a blockchain and is frequently purposefully carried out to update a network. Hard forks result in a new version of the chain that must be embraced in order to continue participating, whereas soft forks result in two chains that are somewhat compatible. A disputed hard fork may result in the creation of two distinct blockchain networks.

  • Fork (Hard): type of fork that is permanently incompatible with the original network. Hard forks typically change transaction data structures, consensus algorithms, or add/remove blocks that would not have otherwise been included. For example, Bitcoin Cash is a hard fork of Bitcoin, and Ethereum Classic is a hard fork of Ethereum.

  • Fork (Soft): type of fork that is compatible with the data on the original chain. Blocks created on the original chain after a soft fork would be valid on the forked chain; however the reverse does not have to be true. For instance, when Ethereum upgraded to the Byzantium version, it was affected through a soft fork.

  • Gas fees: gas fees (i.e. transaction fees) are rewards paid to miners to incentivize them to support the network's transactions which become written to the blockchain. On the Ethereum network this gas fee unit amount is expressed in gwei. Operations to or from CEXs, DEXs Liquidity Pools and wallets require gas fees. The cost users incur due to gas fees will vary depending on the current supply and demand: when demand on Ethereum or an ERC-20 network is at its highest, these gas fees are at their highest.

  • Gas Limit: you can only spend a certain amount of gas each transaction on the Ethereum network, according to the gas limit. It may also be seen as a \"rough estimate\" of the amount of computer power your transaction will require.

  • Gas Price: the gas price is the amount paid to the network for the computational labour completed during a particular transaction. It is paid in ETH units known as Gwei. The price of gas may fluctuate dramatically depending on network congestion.

  • Governance Tokens: governance tokens are cryptocurrencies that represent voting power on a blockchain project. Governance tokens are used to vote on system parameters such as choice of autonomous market makers to back with liquidity from the liquidity pool, borrowing rates from the liquidity pool, usage of exchange fees, etc. Governance tokens are minted at an exponentially decreasing rate to incentivise early liquidity providers in the system. Minted tokens are distributed in proportion to the amount of liquidity supplied to the system at each block. Some fraction of the exchange fees and autonomous market maker spreads is used to buy back these tokens and burn them in order to reduce the quantity of tokens circulating and thus increasing their value.

  • Graphical User Interface (GUI): it is way of displaying information to the user through stylized, on-screen elements, such as windows and taskbars.

  • Hash: a programmatic function that accepts an input and produces the \"hash value\" or \"digital fingerprint,\" which is an alphanumeric string. Each block in the blockchain has its own hash value as well as the hash value used to validate the transaction that came before it. Transactions on the blockchain are verified via hashes.

  • Hard Fork: in accordance to Binance Academy, \"Hard forks are backward-incompatible software updates. Typically, these occur when nodes add new rules in a way that conflicts with the rules of old nodes. New nodes can only communicate with others that operate the new version. As a result, the blockchain splits, creating two separate networks: one with the old rules, and one with the new rules\".

  • Hot Wallet: a wallet that is continuously linked to the internet, such as one that is kept on a major exchange. Compared to hardware wallets or cold storage solutions, hot wallets are thought to offer less security.

  • IBC Transfers: the Inter-Blockchain Communication protocol (IBC) is an inter-module communication protocol that bridges different blockchains to facilitate communication and feature exchanges between networks with different infrastructure designs and consensus algorithms.

  • Initial Coin Offering (ICO): it is like an initial public offering (IPO) of stock, an initial coin offering is a way for a tokenized business to generate investment from the public. ICOs are regulated by the Securities and Exchange Commission (SEC), even if the tokens are not specifically securities because the language used in promoting a sale can serve to classify tokens as a security offering.

  • KYC (Know-Your-Customer): KYC guidelines fit into the broader scope of Anti-Money Laundering (AML) policies in traditional finance. There is no KYC or AML in DeFi.

  • Ledger: a book or collection of accounts in which account transactions are recorded.

  • Liquidation: liquidation is applied to borrowers. They can have their collateral liquidated if they do not maintain the set collateralization ratio.

  • Liquidation Ratio: this is the level at which the collateralization ratio dips that can trigger liquidation.

  • Liquidity Pool: a liquidity pool is a crowd sourced pool of cryptocurrencies or tokens locked in a smart contract that is used to facilitate trades between the assets on a decentralized exchange (DEX). Many decentralised finance (DeFi) platforms use automated market makers (AMMs), which enable digital assets to be traded in an automatic and permissionless manner through the use of liquidity pools, in place of traditional markets of buyers and sellers.

  • Liquidity Provider: a liquidity provider (LP) is a user who funds a liquidity pool with own crypto assets so as to facilitate trading on the platform and earn passive income on the liquidity deposited.

  • Mainnet: it represents the main network on which transactions on a certain distributed ledger actually occur. The Ethereum mainnet, for instance, serves as the public blockchain for network verification and transaction processing.

  • MAS (Multi-Agent System): a Multi-Agent System is a group of agents that interact with each other and the environment to achieve specific goals. In such systems, agents may not have full knowledge of both the environment and the internal state of other agents. Interactions between agents may be cooperative or competitive. In a cooperative interaction, agents work with each other towards a common goal. The aim of this interaction is to enable agents to distribute and share their knowledge and use the intelligence and capabilities of each other to solve problems. In a competitive interaction, agents may compete to obtain individual resources and achieve individual goals.

  • Metamask: Metamask is a popular Web 3.0 wallet used in DeFi. Other wallets you may hear about are the Binance Wallet and Torus.

  • Miner: a miner is an actor in a blockchain network that has the ability to create and submit new blocks to the chain. Which miner is allowed to produce a specific block may be predetermined, or miners may simultaneously compete to add the next block to the chain.

  • Mining: mining is the process through which cryptocurrency transactions are gathered, verified and recorded into a digital ledger known as blockchain. The work done by miners is essential for maintaining the integrity of the network and is also responsible for introducing new coins into the system. It is the method used by a Proof of Work (PoW) consensus mechanism to verify blocks or transactions before adding them to a blockchain. A miner must use a computer to solve a cryptographic puzzle in order to validate a block. The block is regarded as having been \"mined\" or validated after the computer has figured out the issue. The first computer to mine or verify the block receives a reward represented by cryptocurrencies (e.g. BTC, ETH).

  • Multi-Signature: multisig stands for multi-signature, which is a specific type of digital signature that makes it possible for two or more users to sign documents as a group. Therefore, a multi-signature is produced through the combination of multiple unique signatures. Basically speaking, the funds stored on a multi-signature address can only be accessed by using 2 or more signatures. Therefore, the use of a multisig wallet enables users to create an additional layer of security to their funds.

  • Network: it represents a set of actors that are collectively interconnected for a common purpose.

  • Node: a node is any computer that is a part of the blockchain network. A full node is a computer able to completely verify transactions and download all of the data associated with a particular blockchain. A lightweight or light node, in contrast, utilizes a separate validation mechanism and does not download the entire blockchain's data.

  • Nonce: this term has a variety of connotations, and depending on the situation, it is employed in many different ways. On the Ethereum mainnet, it refers to a distinct transaction identification number that rises in value with each subsequent transaction to assure different safety measures. It was originally created from a contraction of a term meaning \"not more than once\" (e.g. preventing a double-spend).

  • NFT: Non-Fungible Tokens are cryptographic assets built on a blockchain with unique identification codes and metadata that distinguish them from each other. These were born as ERC-721 crypto assets representing a unique or rare digital or real world item. They let us tokenize things like art, collectibles, even real estate through a securitisation process that takes place on the blockchain. Unlike cryptocurrencies which are identical to each other and can be used as a medium for commercial transactions, NFTs represent unique and irreplaceable tokens, thereby making it impossible for one non-fungible token to be equal to another (i.e. NFTs are not fungible). This ensures a highly transparent and flexible record of ownership. An NFT can only have one owner at a time. Ownership is managed through the unique ID and metadata that no other token can replicate. NFTs are minted through smart contracts which assign ownership and manage the transferability of the NFT itself. When someone mints an NFT, they execute a code stored in the smart contracts that conform to different standards (i.e. ERC-721). This information is added to the blockchain where the NFT will be managed.

  • Open Source Software: it is a software for which the source code that is available to the public. One of the benefits of open source software is that people from outside the core development team can support it, collaboratively creating new features or fixing bugs. Open source licenses typically include language that prevents anyone from reselling the core code without significant changes.

  • Oracle: within the blockchain context, an oracle is a data source used as a bridge between smart contracts and other external sources. More specifically, an oracle is an agent that not only communicates with external data sources but also verifies and authenticates that the data being provided is accurate. Thus, oracles are responsible for providing vital and reliable information to smart contracts, which in turn perform certain tasks.

  • P2P (Peer-to-Peer): P2P describes interactions that take place between two parties, often two different people. Any number of users can be a part of a P2P network. People may conduct transactions or connect with one another over a blockchain network without depending on a middleman or a single point of failure.

  • Private blockchain: a blockchain or distributed ledger with a closed network whose users are all under the supervision of the same organization. For new members on a private blockchain, there must be a verification procedure. Additionally, a private blockchain may place restrictions on who may take part in the network's consensus.

  • Private Key: this is effectively the encrypted password to someone's cryptocurrency assets, and it is also known as a secret key. It is an impossible-to-guess number that is really lengthy. By signing a transaction with your private key, you approve it. You can access and control your crypto assets using private keys.

  • Proof of Stake (PoS): this is a consensus mechanism in which transactions or blocks are verified by a single validator. Those that validate transactions, stake their cryptocurrencies (e.g. ETH), on those particular transactions. If someone accurately verifies a block (or collection of transactions), they are rewarded. A validator often forfeits the cryptocurrency they staked if they verify an invalid transaction. Comparatively speaking to Proof of Work consensus, PoS takes a tiny amount of computational power.

  • Proof of Work (PoW): this is a consensus mechanism in which a network's nodes or individuals collectively mine each block. Each miner in a PoW network must compete to solve a computational puzzle in order to hash a block. The complexity of successfully hashing each block is increased by the inclusion of solving for a target. The whole hashing procedure will have required some time and computing effort for each block that was hashed. The miner who successfully hashes the block first earns a reward in the form of cryptocurrency. As a result, a hashed block is regarded as Proof of Work. PoW requires a disproportionately higher amount of energy than other consensus processes.

  • Public Blockchain: a network that is completely open to the public, allowing anybody to take part in transactions, run the consensus protocol to help decide which blocks should be added to the chain, and keep track of the shared ledger.

  • Public Key: the wallet address that is visible to the whole public. You must provide your public key in order to receive funds into your account. It is paired with a private key.

  • Python: it is a high-level, all-purpose programming language.

  • Scalability: a change in scale or size to meet the demands of a network This term is used to describe a blockchain project's capacity in its intended use as well as its capability to manage network traffic and future expansion.

  • SDK: a Software Development Kit (SDK) is a set of tools provided by the manufacturer of (usually) a hardware platform, operating system (OS), or programming language. SDKs help software developers create applications for that specific platform, system, or programming language.

  • Slippage: in accordance to Investopedia's definition, \"slippage refers to the difference between the expected price of a trade and the price at which the trade is executed. Slippage can occur at any time but is most prevalent during periods of higher volatility when market orders are used. It can also occur when a large order is executed but there isn't enough volume at the chosen price to maintain the current bid/ask spread. Slippage occurs in all market venues, including equities, bonds, currencies and futures\".

  • Smart Contracts: these are programs having their terms encoded in computer code. These are not legal documents, even if they sometimes include agreements or agreements between parties that resemble a regular legal contract. On the Ethereum Virtual Machine, smart contracts are the most common type of programming. Smart contracts contains a series of automated operations that may be programmed and executed if a series of criteria are satisfied.

  • Soft Fork: a soft fork is an update that is backward-compatible, allowing upgraded nodes to continue connect with not upgraded ones. A soft fork often involves the insertion of a new rule that does not conflict with the existing ones.

  • Snapshot: in the context of cryptocurrencies, the process of capturing the state of a blockchain at a specific block height is frequently referred to as taking a snapshot. In this instance, the snapshot captures the entirety of the blockchain ledger, including all of the current addresses and the information linked with them (i.e. transactions, fees, balance, metadata, and so on). Prior to each round of an airdrop event, snapshots are frequently employed. Tokens are given during an airdrop based on the balance of each blockchain address. In this instance, snapshots are made to capture each token holder's balance at a certain moment in time (i.e. block height). Snapshots are also important during blockchain hard forks, as they mark the block height in which the main chain will be recorded before giving birth to the new chain.

  • Software Agent: a software agent is a computer program that acts on behalf of an entity (e.g. individual, organisation, business).

  • sOEF (Simple Open Economic Framework): the simple-OEF, or sOEF, is a search and discovery service for autonomous economic agents. For additional information: here.

  • Spread: when an order is made on an exchange or market, the disagreement of the difference in price between potential buy and sell offers of an asset is called the spread. A wide spread in price can lead to higher slippage.

  • Stablecoin: a stablecoin is a type of cryptocurrency that is designed to maintain a stable market price. Recently, this type of digital currency has grown in popularity, and we now have numerous stablecoin projects. Although the exact mechanisms vary from one coin to another, stablecoins are supposed to be somewhat resistant to market volatility, so they should not experience significant price changes.

  • Timing Risk: according to Investopedia's definition, \"timing risk is the speculation that an investor enters into when trying to buy or sell an asset based on future price predictions. Timing risk explains the potential for missing out on beneficial movements in price due to an error in timing\".

  • Testnet: a testing network for a new coin, project, product or for potential improvements to an existing product or offering. Testnets (e.g. Kovan Test Network) are used to test the viability and vulnerability of new ideas, concepts, code, and processes prior to moving on to a production network.

  • Token: a unique form of cryptocurrency. It is a method to specifically refer to a cryptocurrency that utilizes a certain blockchain. There are many types (see also ERC-20 and ERC-721).

  • Tokenization: it represents the concept of translating business strategies, goods, or services into discrete, tradeable units that are recorded on a blockchain or other system. Physical goods can be tokenized by associating their unique identifiers with on-chain references.

  • Trustless: in the context of decentralized technology, everyone has a copy of the ledger of every transaction, so there is no need for a third-party repository of truth where confidence is anchored. This means that everyone can independently verify the transactions. Because the system functions uniformly for everyone, the rules and protections incorporated into the blockchain in some ways serve as the foundation for increased trust amongst members.

  • Validator: it is a computer node in charge of validating blocks on a blockchain. It is a person who owns the ability to verify transactions and earn cryptocurrency on a proof-of-stake blockchain as part a reward for validating such blocks.

  • Volatility: volatility describes how quickly and how much the price of an asset changes. It is usually calculated in terms of standard deviations in the annual return of an asset over a set period of time. Because it is a measure of the rapidity and degree of price changes, volatility is often used as an effective measure of the investment risk associated with an asset.

  • Yield Farming: yield farming is the practice of staking or lending crypto assets in order to generate high returns or rewards in the form of additional cryptocurrency. Yield farming protocols incentivize liquidity providers to stake or lock up their crypto assets in a smart contract-based liquidity pool. These incentives can be a percentage of transaction fees or a governance token. These returns are expressed as an annual percentage yield (APY). As more investors add funds to the related liquidity pool, the value of the issued returns rise in value.

  • Volume: a market's volume, also known as trading volume, is the total number of units exchanged during a specific period of time. It is a calculation of the quantity of separate assets that were traded throughout the course of a specific time period. A buyer and a seller are involved in every transaction. The facilitating exchange records the transaction whenever they come to an agreement at a particular price. The trade volume is then calculated using this data. Any trade asset, such as stocks, bonds, fiat money, or cryptocurrencies, may be used to measure trading volume.

  • Wallet: a safe digital place or storage device for cryptographic assets. Wallets can be offline or online (e.g. hot and cold wallets).

  • Web3: it is a notion for a new version of the World Wide Web (WWW) that integrates ideas like decentralization, blockchain technology, and token-based economics is known as Web3 or Web 3.0. It has been likened to Web 2.0 by some engineers and journalists, who claim that data and information are consolidated in a limited number of businesses frequently referred to as \"Big Tech\". Gavin Wood, a co-founder of Ethereum, first used the phrase Web3 in 2014, and in 2021 venture capital firms, major technological companies, and cryptocurrency enthusiasts began to show interest in the concept.

  • 51% Attack: a 51% attack is occurring if one person or one group of individuals control more than half of the network's computing power or mining hash rate. By taking over mining activities, blocking or altering transactions, and double-spending coins, this organisation effectively has complete control over the network and the ability to disrupt a cryptocurrency.

"},{"location":"learn_the_concepts/peer_to_peer_systems/","title":"Peer-to-Peer Applications","text":""},{"location":"learn_the_concepts/peer_to_peer_systems/#what-is-a-peer-to-peer-system","title":"What is a Peer-to-Peer System?","text":"

A Peer-to-Peer (i.e. P2P) system is a network of users who communicate with each other without having to go through a middleman. Peer-to-Peer networks consist of a group of devices that collectively store and share files. Every participant (i.e. node) functions as a distinct peer. Normally, all nodes are equally powerful and carry out the same functions. We can outline three key characteristics featuring a P2P system:

  • No middlemen needed: One of the key characteristics of peer-to-peer systems is that middlemen are not always required, though they are allowed.

    Example

    When booking a hotel room with a travel agent, you are going through a middleman. However, booking a room directly with the hotel itself is direct communication, and an example of peer-to-peer interaction.

  • Direct links: direct communication between both parties can be established only in the presence of direct links between them.

  • Direct interaction: It is not enough just for parties to be able to communicate directly, they have to be able to actually interact. Peer-to-peer systems are characterized by direct interaction.

    Example

    In the hotel room booking example, making a booking with the hotel sending back a booking confirmation actually consists of a direct interaction between the two parties.

    Note

    P2P networks enable the sharing of files stored on linked devices' hard drives. A user can serve as the source of a file after they have downloaded it. In other words, when a node performs the role of a client, it downloads data from other network nodes. However, while they are acting as a server, they serve as the location for file downloads by other nodes. However, both operations can be carried out concurrently. Therefore, each node has a copy of the data and serves as both a client and a server to other nodes. This removes the need of a central administrator or server.

    P2P networks differ from more conventional client-server systems in this way, where client devices receive files from a centralized server. Since every node sends, receives, and saves data, P2P networks often get quicker and more effective as their user bases expand. P2P networks are also particularly resilient to cyberattacks thanks to their distributed architecture, and P2P networks do not have a single point of failure, in contrast to conventional models.

P2P systems can be grouped based on their architecture into the following three primary subtypes:

"},{"location":"learn_the_concepts/peer_to_peer_systems/#unstructured-p2p-networks","title":"Unstructured P2P Networks","text":"

P2P networks that are unstructured lack a defined node organization. Participants communicate with one another in a random fashion. These systems are thought to be resistant to high churn activity (i.e. several nodes frequently joining and leaving the network).

Note

Unstructured P2P networks are simpler to set up, but because search requests are sent to as many peers as possible, they may utilize more CPU and memory. This frequently causes the network to get overrun with requests, especially when only a few nodes are providing the needed material.

"},{"location":"learn_the_concepts/peer_to_peer_systems/#structured-p2p-networks","title":"Structured P2P Networks","text":"

The structured P2P network is organized into an arrangement based on a distributed hash table (i.e. DHT).

Info

DHT is an advanced form of lookup or search system that allows nodes to access data, such as files, through the use of a key instead of having to make a copy of the data on every node.

This contrasts with the idea behind unstructured P2P networks in which whole files may be stored on more than one node.

Note

Searching for material in a structured network is simpler and uses less power and memory than an unstructured network. The routing of requests and information rely on each peer knowing what is available for download and other criteria of the neighboring node, which must be relearned as peers leave or join the network as the neighbors change.

"},{"location":"learn_the_concepts/peer_to_peer_systems/#hybrid-p2p-networks","title":"Hybrid P2P Networks","text":"

In hybrid P2P networks some elements of the P2P architecture are combined with the traditional client-server approach. Hybrid models typically display better overall performance when compared to the other two categories. They typically incorporate some key benefits of each strategy, attaining notable levels of efficiency and decentralization at the same time.

"},{"location":"learn_the_concepts/peer_to_peer_systems/#benefits-and-drawbacks-of-a-p2p-system","title":"Benefits and Drawbacks of a P2P System","text":""},{"location":"learn_the_concepts/peer_to_peer_systems/#benefits","title":"Benefits","text":"
  • No need for a Specific Operating System or Software: Individual peers can be on any OS and in most cases do not need specialized software to share files. This is especially useful in remote P2P networks where users might not have the same hardware.

  • Cost: P2P networks do not need a costly server and can be joined together simply through USB or over the internet. Even more permanent connections (using copper wires in smaller offices, for example) is not as costly as creating a server or buying server software.

  • Egalitarian: Each peer has control over what can be accessed by others on the network by changing the sharing settings. This makes it easier to protect the integrity of the network as an issue with one node will not destroy the rest of the peers.

  • Easy to set up: Configuration is straightforward, with no need for the oversight of an administrator. Each node manages access and sharing themselves.

  • Scalable: P2P networks are easy to scale, with more nodes adding performance and giving more power. Adding more peers makes more storage and processing power available to the network and can improve download and upload speeds.

  • Easy Searching: The idea of a peer-to-peer network is that finding the right resources should, in theory, be easy. Even in an unstructured network, if the content you are searching for is not rare, it should be held by several peers and be available to download from multiple sources.

  • Aligns With Decentralized Systems: Peer-to-peer systems align closely with decentralized systems, as both require trust between parties in order to work effectively, and both do not require central authorities to manage the system. Peer-to-peer communication is the basis of how parties in decentralized systems communicate with each other.

  • More Freedom: By not having to go through a middleman, it gives users more freedom. They are not limited by the information for example provided by the hotel booking site but can find out far more by being in direct contact with the hotel and likewise the hotel can ask questions directly to their guests.

"},{"location":"learn_the_concepts/peer_to_peer_systems/#drawbacks","title":"Drawbacks","text":"
  • Decentralized: this makes it harder to arrange backups and file archiving. The safety and integrity of the content can be at risk if it is not managed, backed up regularly and deleted once it is obsolete. Due to the decentralized nature of such networks, participants may find it hard to provide themselves those useful services normally provided by the middlemen, such as marketing and promoting services, or market balancing services.

  • No Oversight: in most of P2P networks, the decentralized nature makes it hard for a single administrator to monitor contents, and these can be at risk from malware and viruses. Sharing files with an infected node can transmit malware through the network and could cause problems over several affected peers.

  • Slow transmission: simultaneous uploading and downloading of files can lead to a slower rate of transmission. The double function of uploading content while downloading other resources might actually make it slower.

  • Poor Internet Performance: file sharing through P2P networks uses a lot of bandwidth and CPU, which can slow the computer performance for the individual user, especially when it comes to the internet. If multiple files are being shared, there is a risk that productivity in other areas could be reduced.

  • Illegal Content: peer-to-peer networks can be used for downloading pirated music, movies, software and other copyrighted material, even if the sharer is unaware.

"},{"location":"learn_the_concepts/peer_to_peer_systems/#examples-of-peer-to-peer-systems","title":"Examples of Peer-to-Peer Systems","text":"

One of the most popular uses of P2P systems is represented by file sharing networks which allow members to directly share files with each other.

Example

Napster and LimeWire were P2P music sharing networks backed by the idea that peers connected through the internet could find and download any song they wanted, from several other users.

There exist also online gaming platforms which adopt a P2P structure for downloading games between users, such as Blizzard Entertainment which distributed Diablo III, StarCraft II, and World of Warcraft using P2P.

A more successful use for P2P systems was developed in the crypto world thanks to blockchains and how these relate to P2P networks. Bitcoin, Ether and many other cryptocurrencies were developed following the P2P mechanism.

An additional example of P2P architecture is represented by P2P crypto exchanges, on which users can immediately buy or sell cryptos from/to other users directly. The majority of P2P exchanges let you send and receive cryptocurrencies without requesting identity verification, in contrast to centralized exchanges where you must complete KYC in order to fulfill an order. Also, unlike centralized exchanges, P2P-based exchanges do not have a single point of failure. In most cases, a user may sign up for the exchange without having to undergo identification verification. A password and an email address are all that are needed for registration.

Example

P2P-based crypto exchanges include Paxful and Binance P2P.

"},{"location":"learn_the_concepts/blockchains/consensus/","title":"Consensus Mechanisms","text":"

Despite the absence of a central authority to confirm and authenticate the transactions, every Blockchain transaction is regarded as being totally safe and validated. Only the presence of the consensus protocol, a fundamental component of every Blockchain network, makes this feasible.

Info

A consensus algorithm allows every peer in the Blockchain network to agree on the distributed ledger's current state. Consensus algorithms accomplish dependability in the Blockchain network and build confidence amongst unidentified peers in a distributed computing setting in this way.

Hence, the consensus mechanism ensures that every new block added to the Blockchain is the sole version of the truth that has been accepted by every node.

When it comes to confirming the legitimacy of distributed blockchain systems, each consensus technique has a unique set of benefits and drawbacks. While PoW and PoS are the most common, new algorithms are always emerging. The following are the most utilized consensus mechanisms nowadays:

"},{"location":"learn_the_concepts/blockchains/consensus/#proof-of-work-pow","title":"Proof-of-Work (PoW)","text":"

One of the most prominent blockchain technologies is the PoW consensus process, which was initially made popular by Bitcoin. Miners and the power they need to do the computations required to validate transactions are the key elements that distinguish PoW systems. Miners use computer hardware to power network nodes that use processing power to solve algorithmic mathematical computational puzzles, known as proofs of work. The miner who completes the puzzle first validates the blockchain's most recent block of transactions. The successful miner broadcasts the new block to all other nodes, which in turn authenticate its accuracy and add that block to their copies of the blockchain. This verification procedure establishes consensus. A new block cannot be added to the network until this data has been verified. When a miner validates a new block of data this latter one is then added to the PoW blockchain, with the first miner completing the validation process being rewarded with newly created cryptocurrency, known as the block reward.

Note

PoW networks are constrained in terms of their speed and scalability due to how the high energy-intensive process these imply. Massive quantities of processing hash power must be used to solve the computational challenge, which uses a lot of energy. The more intensive the validation process, the more computational power is adopted to solve the puzzle and this leads to a higher competition among validators, which equals harder proofs of work and more energy consuming operations. Technology advancement in the blockchain sector has put a lot of emphasis on addressing the negative environmental effects of crypto mining, and several alternatives have surfaced.

PoW blockchains have typically offered stronger security while preserving significant decentralization, despite their speed and scalability restrictions. Due to the distributed nature of PoW systems, it would be very expensive for a bad actor to seize control of the majority of the network's computing power and take over the blockchain. Typically, it is unable to overcome the high expenses of the hardware, power, and computing.

"},{"location":"learn_the_concepts/blockchains/consensus/#proof-of-stake-pos","title":"Proof-of-Stake (PoS)","text":"

PoS blockchains only have validators for transaction validation, not miners. Similar to PoW systems, validators operate network nodes and validate data, but there is no energy-intensive computing procedure required to earn the privilege to validate.

Info

This consensus mechanism addresses many of the problems of PoW blockchains, including slowness, lack of scalability, wasteful energy use, and high entry barriers. Polkadot, Avalanche, and Cardano are a few instances of contemporary market-dominating PoS blockchains.

Validators stake some native tokens of the blockchain to qualify for selection as a validator node rather than working to solve proofs of work. The potential validator will effectively stake blockchain-native crypto tokens to act as collateral. On a PoS blockchain, when it is time to validate the data contained in a transaction block, the system chooses a validator at random to do so. The quantity of tokens a validator has staked is one such factor that might increase the likelihood that they will be picked as a validator.

The process starts with a new block after the block is confirmed, and the validator is typically compensated with network transaction fees. By asking validators to stake their tokens, PoS blockchains make the network safe and maintain validators' integrity. The entrance barrier to PoS blockchains for validators is arguably lower, when compared to PoW blockchains, since they do not need to invest in expensive hardware or incur significant electricity costs. However, you still need to have enough cryptocurrency to stake if you want to become a validator. PoS blockchains have been accused of being plutocratic since validators' control over the network is frequently proportionally correlated with the quantity of their stake. Thus, even though the PoS validation process uses significantly less energy and is quicker and less expensive, it does have some drawbacks, such as the potential for power concentration in the hands of individuals on the network who have amassed a sizable amount of the platform's governance cryptocurrency.

Note

Through a procedure known as slashing, validators that engage in malevolent or incompetent behaviour lose their stake and network access. By using this incentive system, validators are certain to benefit more from following the law than from breaching it.

There can be particular hardware requirements for various platforms adopting a PoS mechanism. While PoS is not nearly as resource-consuming as PoW, certain PoS blockchains' validator nodes may require strong hardware or software specifications because they may be the need of handling a lot of transactions at once.

"},{"location":"learn_the_concepts/blockchains/consensus/#delegated-proof-of-stake-dpos","title":"Delegated Proof-of-Stake (DPoS)","text":"

It is a well-liked development of the Proof of Stake idea in which network users choose delegates to validate the following block. Delegates may also be referred to as block producers or witnesses. By pooling your tokens into a staking pool and tying them to a specific delegate, you may cast a vote for delegates using DPoS. The argument for DPoS is that it is a more decentralized and equitable method of reaching consensus than just Proof-of-Stake.

"},{"location":"learn_the_concepts/blockchains/consensus/#proof-of-burn-pob","title":"Proof of Burn (PoB)","text":"

With PoB, validators burn coins by sending them to an address from which they are irretrievably lost, as opposed to spending money on expensive hardware equipment. Validators obtain the right to mine on the network based on a random selection procedure by sending the coins to an unreachable address. Burning coins here entails a long-term commitment on the part of validators in return for a temporary loss. Miners may burn either the native money of the Blockchain application or the currency of an alternate chain, such as bitcoin, depending on how the PoB is implemented. The more coins they burn, the better are their chances of being selected to mine the next block.

"},{"location":"learn_the_concepts/blockchains/consensus/#proof-of-capacity-poc","title":"Proof of Capacity (PoC)","text":"

PoC, often referred to as Proof-of-Space (PoSpace) is a mining technique that is based on the amount of hard disk space that a miner has available. Here, miners generate a list of all the possible hashes beforehand in a process called plotting. Such plots are then stored on hard drives. The more storage capacity a miner has, the more possible solutions available. The more solutions, the higher the chances of possessing the correct combination of hashes and thus the higher the possibility of winning the reward.

Info

PoC makes it possible for the typical individual to take part in the network because it does not need expensive or specialized equipment. As a result, this is decentralized and less energy-intensive alternative to some of the more widely used consensus mechanisms. However and there are concerns about its susceptibility to malware attacks.

"},{"location":"learn_the_concepts/blockchains/consensus/#proof-of-activity-poa","title":"Proof of Activity (PoA)","text":"

It is a consensus approach that combines PoW and PoS. Similar to PoW, the mining process starts here with miners vying to use the most powerful processing power to solve a complex mathematical problem. The system then turns to resemble PoS when the block has been mined, with the successfully created block header being sent to the PoA network. The new block is then randomly validated by a number of validators who sign off on the hash. The more crypto a validator possesses, similar to PoS, the better their chances are of being chosen. The block is introduced to the blockchain network and made available to record transactions once each selected validator has signed it. The miner and validators then split the block rewards.

"},{"location":"learn_the_concepts/blockchains/consensus/#proof-of-authority-poa","title":"Proof of Authority (PoA)","text":"

This mechanism chooses its validators based on their track record. Validators in PoA do not stake coins. Instead, in order to have the ability to validate blocks, they must risk their reputations. The majority of blockchain systems, in contrast to this, often do not need participants to disclose their identities. This approach is far less resource-intensive than some of its predecessors, especially PoW, as it requires essentially little computational power.

"},{"location":"learn_the_concepts/blockchains/decentralized_applications/","title":"Decentralized Applications","text":"

Decentralized applications (DApps) are digital applications or programmes that exist and run on a blockchain or peer-to-peer (i.e. P2P) network of computers. DApps, which might resemble ordinary mobile applications on your phone in appearance but have a distinct backend system, as these are not subject to the control of a single authority. DApps function by using smart contracts on a distributed network as opposed to a centralized system. These features make DApps more open, decentralized, and secure as a result. A smart contract functions as a collection of predetermined rules that computer code enforces. The tasks specified in the contract will be carried out by all network nodes when and if specific conditions are satisfied.

DApps share the following features:

  • Open-sourceness: This allows anybody to access, verify, use, copy, and edit their source code. The vast majority of the DApp tokens are not controlled by a single entity. Users are also able to suggest and vote on DApp improvements.

  • Decentralization and Cryptography: To protect data, the DApp stores all of its data on a public, decentralized blockchain that is maintained by a number of users (or nodes).

  • Tokenization: A cryptographic token is required to access DApps. They can use native tokens created using a consensus mechanism like Proof of Work (PoW) or Proof of Stake (PoS), or they can adopt cryptocurrencies like ETH. Additionally, contributors like miners and stakers can be rewarded with this token.

Info

DApps are present on the Ethereum network and on other blockchains, such as BNB Smart Chain (BSC), Solana (SOL), Polygon (MATIC), Avalanche (AVAX), EOS.

To interact with a DApp, you first need a compatible browser extension wallet like MetaMask, or Trust Wallet.

"},{"location":"learn_the_concepts/blockchains/decentralized_applications/#benefits-and-limitations-of-dapps","title":"Benefits and Limitations of DApps","text":"

While DApps and conventional apps may have similar-looking user interfaces, DApps have some advantages over their centralized alternatives. Data is kept on centralized servers by web applications. The entire network of the app might be brought down by a single hacked server, rendering it inoperable for a while or ever. Data leaks or theft may occur in centralized systems as well, endangering businesses and consumers.

DApps, in contrast, are created via decentralized distributed networks. DApps have no single point of failure, making them less susceptible to assaults and making it exceedingly challenging for bad actors to take over the network. The P2P network can also guarantee that the DApp keeps running with little downtime even if a few machines or a section of the network go wrong. Users may exercise more control over the data they disclose because of DApps' decentralized nature. Users do not need to reveal their real-world identities in order to communicate with a DApp because no corporations are in charge of their personal data. Instead, individuals may connect to DApps using a crypto wallet and have complete control over the data they disclose.

Another advantage of DApps is that by utilizing smart contracts, developers may quickly include cryptocurrency into their core functions.

Example

DApps on Ethereum may accept ETH as payment without integrating external payment services.

When it comes to illustrate the limitations of DApps, we first need to highlight the fact that DApps have the potential to play a significant role in a world without censorship. However, decentralized apps are still in their infancy, and the market has yet to find solutions to problems like scalability, code revisions, and a small user base. DApps might overburden the networks they run on since they demand a lot of computational power to function.

Another difficult task is modifying a DApp. A DApp does require continual updates to repair problems, update the user interface, and introduce new functions in order to improve user experience and security.

Info

It is challenging to change the backend code of a DApp once it has been put on the blockchain, though. Any modifications or enhancements would need to be approved by a majority of the network's nodes, which would take a while to put into practise.

It is challenging for one DApp to stand out and draw a lot of users when there are so many others available. A DApp has to have a network effect in order to function properly. This is, the more users a DApp has, the more efficient it becomes at offering services. The DApp may be made more secure and shielded from hackers tampering with the open-source code by having more users.

"},{"location":"learn_the_concepts/blockchains/decentralized_applications/#use-cases","title":"Use Cases","text":""},{"location":"learn_the_concepts/blockchains/decentralized_applications/#gamefi","title":"GameFi","text":"

GameFi DApps are becoming more and more well-liked. The majority of gaming DApps give players complete control over their in-game assets, in contrast to conventional video games. They also provide users the chance to earn money from these things outside the game.

Example

One of the most successful DApp in this sector is the play-to-earn game Axie Infinity on the Ethereum blockchain. Axie Infinity offers NFT-based game avatars, virtual worlds, and gaming-related merchandise. Players can trade with other players on NFT markets, move them to other Ethereum addresses, or keep them in their digital wallets. Players can compete with one another inside the ecosystem to amass ERC-20 tokens that can be exchanged. Generally speaking, players may earn more in-game incentives the longer they play.

"},{"location":"learn_the_concepts/blockchains/decentralized_applications/#dexs-decentralized-exchanges","title":"DEXs - Decentralized Exchanges","text":"

The intermediaries in traditional finance are financial institutions. Everyone may access financial services with DApps without the need for a centralized authority and have total control over their assets. Low-income people can also profit from DeFi since it gives them access to a wide range of financial services at a considerable cost savings.

The most common financial services offered by decentralized apps are borrowing and lending. Instant transaction settlement, little-to-no credit checks, and the usage of digital assets as collateral are all features of DeFi DApps. Users in DApp lending markets have greater freedom. Another essential example of financial DApps is decentralized exchanges (DEXs). These platforms make peer-to-peer trading easier by getting rid of middlemen like centralized cryptocurrency exchanges. Users are not required to give up control of their funds. With the aid of smart contracts, people trade with another user directly rather than putting their assets into an exchange. Orders are carried out directly between the users' wallets and on the blockchain. In comparison to centralized exchanges, DEXs often charge cheaper trading costs since they require less maintenance.

Example

SushiSwap, PancakeSwap, and Uniswap are a few of the well-known DEXs.

"},{"location":"learn_the_concepts/blockchains/decentralized_applications/#entertainment","title":"Entertainment","text":"

DApps enable the transformation of entertainment daily activities into digital experiences that can also produce financial incentives.

Example

For instance, Audius, a decentralized music streaming network built on blockchain, eliminates the middlemen present in the conventional music industry to connect artists and listeners directly. By creating permanent records of their labor on the blockchain, it enables music curators to better monetise their material.

DApps are addressing problems that users of social media platforms encounter. Twitter and Facebook, two centralized social media behemoths, are frequently attacked for editing content and treating user data carelessly.

Example

An example on decentralized social media platform is given by Steemit. In there, the community can interact freely and express their opinions with fewer limitations and censorship while also having more control over their personal data.

"},{"location":"learn_the_concepts/blockchains/decentralized_applications/#governance","title":"Governance","text":"

DApps can enable users to participate more actively in the administration of online organizations by establishing a decision-making process that is more focused on the community. Users with governance tokens of a certain blockchain project can make suggestions for the community to vote on and vote on other people's ideas anonymously with the use of smart contracts.

Note

Decentralized Autonomous Organizations (DAOs) are one of the decentralized governance models. DAOs are completely autonomous DApps that employ smart contracts to decide for themselves without the need for a central authority. There is no hierarchy there. Instead, economic processes bring the organization's interests into line with those of specific DAO members.

"},{"location":"learn_the_concepts/blockchains/oracles/","title":"Oracles","text":"

Blockchains get their most significant positive features by being purposefully segregated from external systems. However, off-chain data cannot be accessed by blockchains or smart contracts directly, and these need such data to perform any contractual agreement between any party on the blockchain itself.

Info

This is known as the core of the oracle dilemma. In fact, blockchains are unable to draw data from or push data out to any external systems as part of their built-in functionality. A blockchain's isolation is precisely what makes it so safe and dependable since the network just needs to come to consensus on a very simple set of questions using information that has already been written in the ledger.

A blockchain needs an additional piece of infrastructure, an oracle, to connect the blockchain to the off-chain services in a secure manner. A blockchain oracle is a safe piece of middleware that makes it easier for blockchains to communicate with any off-chain system, such as data providers, online APIs, business backends, cloud providers, IoT devices, e-signatures, payment systems, other blockchains, and more. This is, a blockchain Oracle is the element that links the blockchain to the external system, making it possible for smart contracts to operate on the basis of inputs and outputs from the real world.

Note

The oracle system must work concurrently on and off the blockchain in order to perform these tasks. The on-chain component is used to connect to the blockchain to listen for requests, broadcast messages, submit proofs, extract blockchain data, and perhaps even carry out computation on the blockchain. The off-chain component is used to execute requests, retrieve and prepare external data, transport blockchain data to other systems, and carry out off-chain computation for improved smart contract scalability, privacy, security, and other features.

Blockchain oracles can help to increase the range of possibilities for smart contracts to work, thus, oracles are essential components of the blockchain ecosystem. Smart contracts would not even be particularly useful without blockchain oracles as they would only have access to data from their own networks since the vast majority of smart contract use cases, including DeFi, depend on knowledge of real world data and off-chain occurrences.

"},{"location":"learn_the_concepts/blockchains/oracles/#types-of-blockchain-oracles","title":"Types of blockchain oracles","text":"

Blockchain oracles may be categorized based on the following characteristics:

  • Data source: does the data come from hardware or software as its source?
  • Information direction: is the flow of data going inward or outward?
  • Trust: are these oracles decentralized or centralized?

Given this, we can identify the following types of oracles:

  • Software oracles: these communicate with internet data sources and provide the data to the blockchain. Any internet data source is a possible source for this information (e.g. online databases, servers, websites, ...). Exchange rates, pricing of digital assets, and real-time flight information are just a few of the types of data that software oracles frequently give.

  • Hardware oracle: these oracles are made to gather data from the real world and provide it to smart contracts. Electronic sensors, barcode scanners, and other information gathering tools may transmit this data. In essence, a hardware oracle converts actual occurrences into digital values that smart contracts can comprehend.

  • Inbound and outbound oracles: outbound oracles transfer information from smart contracts to the outside world, whereas inbound oracles give information from external sources to smart contracts.

  • Centralized oracles: a centralized oracle is the only source of data for the smart contract and is managed by a single organization. The primary issue with centralized oracles is that they have a single point of failure, which reduces the contracts' resistance to weaknesses and intrusions.

  • Decentralized oracles: public blockchains and decentralized oracles goal is to reduce counterparty risk. By not depending on a single source of truth, decentralized oracles improve the accuracy of the data supplied to smart contracts.

  • Specialized oracles: these oracles are created so that a single smart contract can use them. This means that a proportionately large number of contract-specific oracles must be created if one plans to implement numerous smart contracts. These oracles are said to be exceedingly time and money consuming. This strategy can prove to be quite unworkable for businesses that need to pull data from a number of sources. On the other hand, developers have a lot of freedom to customize contract-specific oracles to meet unique needs because they may be created from the ground up to service a particular use case.

  • Human oracles: oracles can occasionally be people who have specific expertise in a certain area. They are able to gather information from numerous sources, investigate its veracity, and convert it into smart contracts. Since human oracles may use cryptography to confirm their identity, it is unlikely that a fraudster will pretend to be them while supplying tainted information.

  • Cross-chain oracles: these are another kind of oracle that can read and write data between several blockchains. Using data from one blockchain to start an activity on another or connecting assets across chains, so they may be utilized outside the native blockchain they were issued on, cross-chain oracles provide interoperability for moving both data and assets between blockchains.

Info

Given the variety of oracle services, selecting between oracle service providers often comes down to reputation. Reputation frameworks offer transparency into each oracle network's and each oracle node operator's precision and dependability. Then, users may decide for themselves which oracles they wish to use to support their smart contracts.

"},{"location":"learn_the_concepts/blockchains/oracles/#blockchain-oracles-use-cases","title":"Blockchain oracles: Use Cases","text":""},{"location":"learn_the_concepts/blockchains/oracles/#decentralized-finance-defi","title":"Decentralized Finance (DeFi)","text":"

Oracles are necessary for a significant section of the decentralized finance (i.e. DeFi) ecosystem to access financial information regarding assets and markets. Decentralized money markets, for instance, employ price oracles to assess users' borrowing capacity and evaluate if their holdings are under collateralized and at risk of liquidation. Similarly, Automated Market Makers (AMMs) employ price oracles to assist concentrate liquidity at the current market price to increase capital efficiency. Synthetic asset platforms use price oracles to link the value of tokens to real-world assets.

"},{"location":"learn_the_concepts/blockchains/oracles/#dynamic-nfts-and-gaming","title":"Dynamic NFTs and Gaming","text":"

Oracles also make it possible for smart contracts to be used in non-financial use cases, such as NFTs and dynamic NFTs. Non-fungible Tokens (i.e. NFTs) that can alter in appearance, value, or distribution based on external factors like the time of day or the weather are examples of dynamic NFTs. Oracles may also produce verified randomness, which projects can utilize to give NFTs randomized characteristics or to choose random winners in high-demand NFT drops. Verifiable randomness is also used in on-chain gaming applications to produce more captivating and surprising gameplay elements, such as the emergence of random treasure boxes or random matchmaking during a tournament.

"},{"location":"learn_the_concepts/blockchains/oracles/#businesses","title":"Businesses","text":"

Businesses may link their backend systems to any blockchain network using the safe blockchain middleware provided by business cross-chain oracles. Enterprise systems may read from and write to any blockchain in this way, and they can also use complicated logic to decide how to deploy assets and data among chains and with counterparties that are connected to the same oracle network. As a consequence, institutions no longer need to invest time and development resources in integrating with each individual blockchain. Instead, they can immediately join blockchains that are highly desired by their counterparties and quickly provide support for smart contract services that their customers require.

"},{"location":"learn_the_concepts/blockchains/oracles/#insurance","title":"Insurance","text":"

Insurance smart contracts provide access to physical sensors, online APIs, satellite images, and legal data by using input oracles to confirm the presence of insurable events throughout claims processing. Using other blockchains or conventional payment networks, output oracles can give insurance smart contracts a mechanism to pay out on claims.

"},{"location":"learn_the_concepts/blockchains/smart_contracts/","title":"Smart Contracts","text":""},{"location":"learn_the_concepts/blockchains/smart_contracts/#what-is-a-smart-contract","title":"What is a Smart Contract?","text":"

A smart contract (i.e. automated self-enforcing contract) is a piece of computer software that initiates a certain activity when the circumstances are satisfied. In other words, smart contracts are essentially lines of code that run when certain criteria are satisfied, performing a certain purpose.

Info

Smart contracts enable trustworthy transactions and agreements to be made between dispersed, anonymous participants, without the need for a centralized authority, a legal system, or an external enforcement mechanism.

A smart contract may describe the transaction's mechanics and serve as the final arbitrator of the terms. Because of this, smart contracts have evolved into the foundation of a complete ecosystem of decentralized applications (dApps) and are now a key area of focus for blockchain development as a whole.

When predefined circumstances have been verified to have been satisfied, a network of computers will carry out the actions. These can entail paying out money to the right people, registering a car, sending out notices, or writing a ticket. When the transaction is finished, the blockchain is then updated.

Info

As a result, the transaction cannot be modified, and only parties to whom permission has been granted can view the outcome. Smart contacts, therefore, allow intermediaries such as banks and central authorities to be bypassed, as the contract is written onto the blockchain, and once written on the blockchain, these are irreversible. This means they are ideal for automating and decentralizing transactions and deals, and indeed are really the key to making dApps work on the blockchain.

Example

Smart contracts are adaptable to be used in almost any sector where money, digital assets, or any type of digital information has to be moved between parties. Their application is also being investigated in the healthcare industry and to enforce intellectual property agreements.

"},{"location":"learn_the_concepts/blockchains/smart_contracts/#benefits","title":"Benefits","text":"
  1. Speed, Efficiency, and Precision: the contract is promptly carried out if a condition is satisfied. Smart contracts are digital and automated, so there is no paperwork to complete or time wasted fixing mistakes that frequently occur when documents are filled out manually.

  2. Transparency and Trust: there is no need to wonder whether information has been changed for one participant's personal gain because there is no third party engaged and participants exchange encrypted records of transactions. Smart contracts are written in a way that does make disputes extremely rare, indeed they are designed to avoid them entirely.

  3. Security: because the blockchain transaction records are encrypted, these are incredibly difficult to hack. Additionally, hackers would need to alter the entire chain in order to change a single record on a distributed ledger since each record is linked to the records that came before and after it.

"},{"location":"learn_the_concepts/blockchains/smart_contracts/#drawbacks","title":"Drawbacks","text":"
  1. Legal status: smart contracts lack any legal status. This may be largely caused because the technology is so new, and most legal jurisdictions have not yet updated their guidance on smart contracts and legislation if required is usually not yet in place.

  2. Difficult to Change: once a record has been written to the blockchain it is impossible to change or delete it. Therefore, if a smart contract has been issued in some way incorrectly, then changing it is almost impossible. You would be able to issue another contract that effectively would reverse the effect of the incorrect contract, but that would not necessarily be easy.

  3. Need for Precision: smart contracts do not work unless they are written in an extremely precise way. In fact, there can not be any ambiguity or vagueness in their terms.

"},{"location":"learn_the_concepts/blockchains/smart_contracts/#use-cases","title":"Use Cases","text":""},{"location":"learn_the_concepts/blockchains/smart_contracts/#finance","title":"Finance","text":"

Thanks to the irreversible, transparent, and trustless properties of blockchain and smart contract technology, DeFi dApps provide whole new sorts of goods and decentralized business models that may be of great use and utility for consumers, in addition to parallel services to the banking and financial services sector like lending, borrowing, trading, and a variety of other financial activities.

Note

DApps have the ability to lower the barriers to entry into the financial services industry for individuals all over the world because of the improved transparency provided by smart contracts (combined with 24/7 functioning, and cheaper prices). The implications of smart-contract-powered dApps on the financial industry are already apparent, despite the DeFi sector's relative youth given the amount of creative dApps that are currently offering users value and usefulness.

"},{"location":"learn_the_concepts/blockchains/smart_contracts/#gaming-and-nfts","title":"Gaming and NFTs","text":"

Blockchain and smart contract technologies in gaming can help gamers more effectively realize the value and usefulness of in-game purchases and asset purchases. Non-fungible tokens (NFTs), which are distinctive digital assets that stand in for in-game content, are frequently the engine behind blockchain technology in the gaming industry. Smart contracts are crucial to NFTs. The blockchain networks that support NFTs provide player ownership, proved scarcity, interoperability, and immutability, while these tokens are unique, uncommon, and indivisible. These features of blockchain in gaming might promote widespread adoption and a more equal value model when taken as a whole.

Info

The worldwide gaming market is a $100 billion ecosystem that is expanding swiftly, yet the way wealth is produced and dispersed within the market may often be unfair. Games are created and released by developers, and users pay to access and participate in such games. As a result, gamers continue to pay money to gain access to in-game resources and gaming options, perpetuating a one-way flow of value.

"},{"location":"learn_the_concepts/blockchains/smart_contracts/#healthcare","title":"Healthcare","text":"

Another sector that has started utilizing blockchain technology for safe, trustless, and transparent data sharing is healthcare. The incorporation of smart contracts and full dApps created to address important healthcare pain points like interoperability, identity, and authentication issues may help strengthen the connection between blockchain technology and healthcare.

"},{"location":"learn_the_concepts/blockchains/smart_contracts/#identity-management","title":"Identity Management","text":"

To increase security and lower the likelihood of data mismanagement or a breach, Digital Identifier (DIDs) smart contracts built on distributed ledger technologies offer people complete ownership over their data and let them share the content of their data as they see fit.

Note

For identity management, smart contracts help to ensure and facilitate: identity protection, the information the user wants to share, and KYC verification.

"},{"location":"learn_the_concepts/blockchains/smart_contracts/#machine-learning-and-artificial-intelligence","title":"Machine Learning and Artificial Intelligence","text":"

The capacity of blockchain technology and related smart contract technology to simplify difficult computing operations like those involved in machine learning and artificial intelligence is one of the most interesting uses of these technologies (AI). There is potential to develop AI-powered smart contracts by fusing the data-intensive processing of AI with the decentralized security and immutability of blockchain technology. Applications for smart contracts will need to develop into increasingly complicated systems as they are used by more and more sectors of the economy.

"},{"location":"learn_the_concepts/blockchains/smart_contracts/#real-estate","title":"Real Estate","text":"

By fusing blockchain and real estate transactions, smart contracts are increasing fractional ownership of assets and decreasing the entry barrier for investing for many. Particularly, there have been a number of successful attempts to tokenize real estate assets. By adding blockchain into real estate deals, smart contract technology may also remodel the paperwork and transaction procedures. When a piece of property is tokenized, much of the required record-keeping can take place via associated smart contracts, which can save the parties time and money.

"},{"location":"learn_the_concepts/blockchains/smart_contracts/#supply-chain-management","title":"Supply Chain Management","text":"

A fantastic use case for blockchain smart contracts is supply chain management. The supply chain may be significantly enhanced by the use of smart contracts.

Note

Smart contracts may be used, for instance, to track things in the supply chain completely transparently. A company may employ supply networks that are enabled by smart contracts to track its inventory more precisely. Additionally, it enhances corporate operations in other areas that are directly related to the supply chain. Additionally, adopting smart contracts leads to less verification work, improved traceability, and fewer frauds and thefts. However, in order for it to be functional, the institutions must upgrade their supply chain with new hardware equipments.

Smart contracts and dApps are poised to continue revolutionizing the world of digital agreements. Numerous businesses will benefit greatly from smart contract technology in the future. Researchers and developers are increasingly keen to take use of smart contract technology to meet the demands of the expanding Internet of Things (IoT). While IoT devices are currently given security and transparency by blockchain technology in general, the benefits of smart contracts might make this integration much more seamless.

"},{"location":"learn_the_concepts/blockchains/transaction_fees/","title":"Transaction Fees","text":"

Any transaction involving the transfer of cryptocurrency will incur fees, whether you are purchasing or withdrawing funds from an exchange or sending or receiving payments in cryptocurrency. Various fee types include:

  • Transaction or miner fees: these vary depending on how many transactions are awaiting their inclusion in the current block and are intended to motivate miners and validators to validate cryptocurrency transactions.

  • Service or network fees: these are applied by third-party providers that enable transactions (e.g. cryptocurrency exchange). These fees are made in addition to any network-generated fee paid to miners.

Transaction fees serve two crucial functions:

  1. Compensate miners or validators assisting and confirming the transactions.

  2. Defend the network against spam assaults: this is because transaction fees lead to a decrease in spam on the network, and large-scale spam assaults become more expensive and more difficult to execute.

Info

Transaction fees serve as a basic but efficient spam filter.

Note

Transaction fees might be modest or substantial. The fees you pay might also be influenced by market forces. It is important to highlight that the amount of fees you pay impacts your transaction's priority for inclusion in the following validated block. In fact, users who wish to execute their transaction more quickly can even increase the transaction cost to enhance the likelihood that their transaction will be added to the following block of transactions on the blockchain.

The confirmation procedure moves along more quickly the more money that is spent.

Miners are in charge of adding transactions to the blockchain and are required for confirming and securing these transactions on each network. These payments make the effort of miners and validators profitable. Despite the fact that each blockchain is distinct, they all have a limited quantity of transactions that may fit into a single block.

Example

The Bitcoin network only permits a total of 2,800 transactions per block.

Depending on how many transactions are awaiting their inclusion, miner fees may change. During periods of high network traffic, miners prioritize the validation of new transactions based on the fee paid by the user. Hence, the user who wants to complete the transaction more quickly can even increase the transaction fee to boost its chances of being included in the next completed block.

"},{"location":"learn_the_concepts/blockchains/transaction_fees/#how-to-reduce-transaction-fees","title":"How to reduce transaction fees","text":"

There are several options available for reducing fees. First of all, you may lower these fees by deciding when you want to complete your transaction. Due to the fact that the majority of cryptocurrency users worldwide are concentrated in the U. S., blockchain networks typically see the highest levels of activity during times when people there are awake. Additionally, traffic is lower during the weekends, particularly on Saturdays. Additionally, the fees you pay depend on how quickly you want your transaction to be validated. If you have a high priority transaction and want it to be confirmed faster, you should expect a higher miner fee. If your transaction is not urgent, then a slower verification time means a lower transaction fee.

Info

On the Ethereum network, transaction fees are referred to as gas. Like other blockchains, the gas costs can go up or down depending on demand. Miners are more likely to give your transaction priority if you pay more for gas. The gas limit specifies the maximum price to be paid for a specific transaction and it should also be taken into account when proceeding with a transaction.

"},{"location":"learn_the_concepts/blockchains/transaction_fees/#transaction-fees-on-the-fetchai-network","title":"Transaction fees on the Fetch.ai network","text":"

On the Fetch.ai network, the gas mechanism is made simple and gives you an option to pick high, medium, or low fees, in addition to allowing you to manually set the amount you wish to pay. Fetch.ai does operate some validators on the network, which further gives it influence over the gas fee levels. Even though some constraints are set by Fetch.ai on gas fees, the decentralized nature of the network guarantees that transaction fees are not set by any central authority, and the actual level of gas is set by each of the validators on the Fetch network. Fetch.ai has the philosophy that having high gas fees does no good for anyone, and because of this, the Fetch team wants to encourage everyone to use the network, and trade and transact using $FET tokens.

Low fees encourages higher network traffic, and this in turn means more transactions and activity and therefore more fees being paid in general. However, $FET that are traded and transferred on other networks are subject to the gas fees that those networks charge and Fetch has no control whatsoever over those fees. The fees gathered on the Fetch network are then distributed among the stakeholders (i.e. validators and community stakeholders). Gas fees on the Fetch network are extremely very low when compared to other networks. As a general comparison, Juno Network, another ecosystem blockchain within the Cosmos ecosystem has fees that on average are $0.05 per transfer, whereas Fetch.ai transfers cost $0.000000000000001.

These gas fees are calculated in an extremely fair way, with the fee being directly proportional to the computing power needed to complete the process.

Example

A simple purchase transaction takes less computing power than activating a smart contract, therefore the associated fee will be lower.

There is full transparency too, as it is always clear what the gas fee will be before making a transaction, and so there are no hidden surprises.

"},{"location":"learn_the_concepts/blockchains/validators/","title":"Validators","text":"

The block validation procedure is one of the essential elements that makes blockchain functioning possible. A network node known as a blockchain validator assists in processing and validating transaction blocks on the platform so that they may be added to the blockchain's permanent ledger.

Info

On PoS blockchains, these nodes are referred to as validators, whereas, on PoW blockchain networks, these are known as miners. In essence, validator nodes take on the responsibility of validating, voting on, and keeping a record of transactions by taking part in the consensus process.

Block validation is a procedure that both of these blockchain variants may use. Staking, the method of block validation utilized on PoS blockchains, would be a more appropriate alternative to mine when referring to blockchain networks.

The process starts with a user sending the transaction on the blockchain, which is then queued on the network for further confirmation. The block is then verified by validator nodes by batching individual transactions into it. The amount of transactions that can be included in a block is governed differently by each blockchain. The block is then processed by validators which add it to the blockchain as a permanent record. On some blockchains, validators have the option to select which transactions to group into a block.

Info

This choice is made depending on the validator's preferences, usually based on the transaction costs involved, and is not necessarily made in chronological sequence. Transactions with extremely little or no fees, however, are more likely to be disregarded by validators and, as a result, can hang around in the unconfirmed state for a very long time. The transaction is often removed from the network if it is not put into a block for validation after a certain time period.

The sender of cryptocurrency assets tacks on the fees to every blockchain transaction as a reward for validators. Senders have the option to set their own rates and even transmit transactions completely free of charge.

"},{"location":"learn_the_concepts/blockchains/what_is_a_blockchain/","title":"What is a Blockchain?","text":"

A blockchain is a series of data records that functions as a distributed, replicated digital ledger of transactions across a network of computer systems.

Info

On blockchains, the records of transactions are compiled into blocks which are linked together to form a chain. A blockchain consists of a stable chain of blocks, and in the context of cryptocurrencies, each one of these blocks stores a list of previously confirmed transactions.

Transactions take place inside a P2P global network, thus, blockchains are considered borderless and immune to censorship. A blockchain network serves as a decentralized ledger since it is maintained by several computers located all over the world. This implies that each participant, namely node, keeps a copy of the blockchain data and interacts with other participants to make sure that everyone is aware of the same information stored in the block.

Note

Each block inside the blockchain contains a hash (i.e. a digital fingerprint or unique identifier), timestamped batches of recent valid transactions, and the hash of the previous block. The previous block hash links the blocks together, preventing existing blocks from being altered or new blocks inserted between existing ones. An additional feature of a blockchain is that all previous records are stored within any current record, so, there is always full history of previous transactions.

"},{"location":"learn_the_concepts/blockchains/what_is_a_blockchain/#blockchains-use-cases","title":"Blockchains: Use Cases","text":"

Currently, the blockchain technology is mostly used to track cryptocurrency transactions. Examples of other uses are:

  1. Monitoring of Supply Chains: businesses may easily identify inefficiencies in their supply chains using blockchain, as well as find products in real time and monitor their quality as they move from producers to retailers.

  2. Data sharing: blockchain could act as an intermediary to securely store and move enterprise data among industries.

  3. Copyright and royalties protection: blockchain technology has the potential to be utilized to build a decentralized database that guarantees the preservation of music rights and rewards musicians with transparent and real-time royalties.

  4. Internet of Things (IoT) network management: blockchain has the potential to regulate IoT networks by identifying connected devices, tracking their behavior, assessing their dependability, and automatically determining the dependability of new devices that are linked to the network, such as smartphones and vehicles.

  5. Healthcare: the healthcare industry may benefit greatly from the use of blockchain. Blockchains are being used by healthcare payers and providers to handle clinical trial data and electronic medical records while upholding regulatory compliance.

"},{"location":"learn_the_concepts/blockchains/what_is_a_blockchain/#who-can-participate-in-a-blockchain-network","title":"Who can participate in a blockchain network?","text":"
  1. Blockchain users: participants joining the blockchain network and conducting transactions with other network participants.

  2. Regulators: users with special permissions to oversee the transactions happening within the network.

  3. Blockchain Network Operators: individuals who have special permissions and authority to define, create, manage, and monitor the blockchain network.

  4. Certificate Authorities: individuals who issue and manage the different types of certificates required to run a permissioned blockchain.

"},{"location":"learn_the_concepts/blockchains/what_is_a_blockchain/#benefits","title":"Benefits","text":"
  1. Security: blockchains are secure, in the sense that the data is cryptographically encrypted, and their structure makes it difficult and immediately noticeable if someone tries to change the data in a blockchain.

  2. Resiliency: the same information is stored in many places on the blockchain. This implies high resiliency as, if any part of the network was to fail then the information stored on the blockchain is still available in full.

  3. Trust: blockchains are decentralized systems and thus benefit from having no central authority controlling them. Due to their structure, and depending on the mechanism the blockchain's participants use to arrive at a consensus on what the state of the chain is, there is no need for the blockchain's users to trust the other parties. Guarantees are provided by the system itself.

"},{"location":"learn_the_concepts/blockchains/what_is_a_blockchain/#drawbacks","title":"Drawbacks","text":"
  1. Complexity: Setting up a blockchain is not a simple task, there are networks of nodes to set up, the participatory framework established, and numerous components to bring together before you can even begin to store any information on the blockchain.

  2. Speed: It takes longer to add a record to a block and add a block to the chain than it does to simply add a record into a traditional database table. This is a challenge for blockchain-based payment systems, as blockchains typically handle fewer transactions per second than conventional payment systems.

  3. Scaling issues: As blockchains grow, they get more complex leading to issues, such as network congestion.

  4. Participation is required: Blockchains are decentralized systems, so they do need the active participation of members, for example they need to vote on governance proposals affecting the chain and be engaged in order for the blockchain to successfully operate.

"},{"location":"learn_the_concepts/blockchains/what_is_a_blockchain/#types-of-blockchain","title":"Types of Blockchain","text":"

The architecture of blockchain systems varies greatly, especially in terms of the consensus protocol employed to validate network data. The security, usability, and sustainability of the underlying blockchain are affected differently by their own design. Different blockchain networks operate quite differently from one another. We can identify the following:

  • Public blockchains are entirely decentralised, permissionless, and open to everybody. All nodes are meant to have equal access and abilities within the blockchain. These are censorship-resistant and offer broad ecosystems for the development of apps and platforms.

    Example

    Examples of public blockchains include Bitcoin and Ethereum. The majority of public blockchain networks use processes known as Proof-of-Work (PoW) or Proof-of-Stake (PoS) to provide consensus.

  • Private blockchains are blockchains within which permissions are managed by a single company. It is the central authority which decides who can be a node. The central authority may not always accord each node an equal right to execute certain responsibilities. This makes private blockchains partially decentralised. Private (or permissioned) blockchains can be structured in various ways to prioritize speed, security, and scalability.

  • Consortium blockchains are permissioned blockchains governed by a group of entities, rather than a single one as private blockchains. These blockchains are more decentralised than private blockchains, which increases their security. But creating consortiums may be a difficult process since it calls for collaboration between several groups, which poses logistical problems and a possible antitrust risk. However, these may offer faster transaction processing times and are easier to modify, but are restrictive with limited usage outside the private consortium.

  • Hybrid blockchains are blockchains which are managed by a single entity but at the same time have some supervision provided by the public blockchain, which is necessary to carry out specific transaction validations.

Note

Varying consensus techniques have different effects on accessibility, security, and sustainability, and not all blockchains are created equally. In fact, the most suitable type of blockchain design needs to be fitting the actual use case it wants to serve.

"},{"location":"ledger-subquery/introduction/","title":"Introduction","text":"

The ledger-subquery is a SubQuery-based indexer for the Fetch ledger. This indexer provides a Graphql API for querying tracked entities. For a list of tracked entities, see the schema.graphql file.

To learn more about how to run or change this SubQuery Project to get your own custom GraphQL API for your app, visit the SubQuery Academy for documentation.

"},{"location":"ledger-subquery/introduction/#endpoints-playground-uis","title":"Endpoints / Playground UIs","text":"

The graphql API endpoints also serve a playground UI to browsers for convenience. This UI is useful for rapid experimentation and iteration of queries as well as just getting some results, features include:

  • real-time query results
  • query editor:
  • auto-complete & validation via schema introspection
  • can store multiple, named queries
  • supports graphql variables
  • local persistence of query editor contents
  • schema reference
  • graphql docs reference
Network API / Playground URL Fetchhub (mainnet) https://subquery.fetch.ai Dorado (testnet) https://subquery-dorado.fetch.ai"},{"location":"ledger-subquery/introduction/#architecture","title":"Architecture","text":""},{"location":"ledger-subquery/introduction/#component-diagram","title":"Component Diagram","text":""},{"location":"ledger-subquery/introduction/#querying","title":"Querying","text":"

The graphql API relies heavily on postgraphile (as a library) to resolve graphql requests.

Postgraphile plugins also play a critical role; in particular, the connection-filter and pg-aggregates plugins.

"},{"location":"ledger-subquery/introduction/#pagination","title":"Pagination","text":"

The graphql API implements the connections specification for pagination (see: GraphQL pagination docs for more).

Tip

It is recommended to prefer using pagination operators by default (e.g. first: <limit>) to avoid unnecessary delays in query responses.

"},{"location":"ledger-subquery/introduction/#filtering","title":"Filtering","text":"

Filtering is facilitated by postgraphile and its plugins. For specifics on supported operators and how to use them, please refer to their documentation:

  • connection-filter plugin
  • operators
  • query examples
"},{"location":"ledger-subquery/introduction/#examples","title":"Examples","text":"

Filtering NativeTransfers for a given sender address:

query nativeTransfersFromAddress {\n  nativeTransfers(first: 5, filter: {\n    fromAddress: {\n      equalTo: \"fetch1t3qet68dr0qkmrjtq89lrx837qa2t05265qy6s\"\n    }\n  }) {\n    nodes {\n      toAddress\n      amounts\n    }\n  }\n}\n

Filtering for Messages from a given sender address:

query messagesFromAddress {\n  messages (first: 5, filter:  {\n    transaction: {\n      signerAddress: {\n        equalTo: \"fetch1t3qet68dr0qkmrjtq89lrx837qa2t05265qy6s\"\n      }\n    }\n  }) {\n    nodes {\n      transaction {\n        signerAddress\n      }\n    }\n  }\n}\n

Filtering on Eventss within a given timeframe and with a given type:

query transferEventsDuring {\n  events(first: 5, filter:  {\n    block: {\n      timestamp: {\n        greaterThanOrEqualTo: \"2022-09-15T01:44:13.719\",\n        lessThanOrEqualTo: \"2022-09-19T02:15:28.632\"\n      }\n    },\n    type: {equalTo: \"transfer\"},\n  }) {\n    nodes {\n      attributes {\n        nodes {\n          key\n          value\n        }\n      }\n    }\n  }\n}\n
"},{"location":"ledger-subquery/introduction/#order-by-sorting","title":"Order by / Sorting","text":"

Each entity, by default, can be sorted by any of its respective fields. Additional support for ordering by certain fields on related entities is facilitated by custom ordering plugins generated from makeAddPgTableOrderByPlugin (see: postgraphile-docs).

"},{"location":"ledger-subquery/introduction/#block-height","title":"Block height","text":"

Any entity which relates to Block can be ordered by a related block's height field:

query contractExecByBlockHeight {\n  contractExecutionMessage (orderBy: EXECUTE_CONTRACT_MESSAGES_BY_BLOCK_HEIGHT_ASC) {\n    nodes {\n      id,\n      ...\n      Block {\n        height\n      }\n    }\n  }\n}\n

"},{"location":"ledger-subquery/introduction/#contract-code-id","title":"Contract Code ID","text":"

The contract entity can be sorted by codeId through the storeMessage and instantiateMessage relations.

query contractsByRelatedCodeID {\n  contracts (orderBy: CONTRACTS_BY_STORE_CONTRACT_MESSAGES_CODE_ID_ASC) {\n    #  or CONTRACTS_BY_INSTANTIATE_CONTRACT_MESSAGES_CODE_ID_ASC\n    nodes {\n      id,\n      ...\n      storeMessage {\n        codeId\n      }\n    }\n  }\n}\n

"},{"location":"ledger-subquery/introduction/#order-direction","title":"Order direction","text":"

Each of these custom orders are implemented in both directions, ascending and descending. These directions are accessed through the ending characters of the order enum, by choosing either _ASC and _DESC.

"},{"location":"ledger-subquery/introduction/#aggregation","title":"Aggregation","text":"

Aggregation is facilitated by the pg-aggregates plugin. Features include:

  • calculating aggregates
  • grouped aggregates
  • applying conditions to grouped aggregates
  • ordering by relational aggregates
  • filtering by the results of aggregates on related connections
"},{"location":"ledger-subquery/introduction/#tests-as-examples","title":"Tests as examples","text":"

Additional examples of queries and use cases can be found in the end-to-end test suite.

"},{"location":"ledger-subquery/introduction/#entities","title":"Entities","text":"

Entities tracked by the indexer exist at varying levels of abstraction. \"Lower-level\" entities include the primitives (i.e. blocks, transactions, messages, and events), upon which \"higher-level\" entities are constructed (e.g. LegacyBridgeSwaps).

Some entities are derived from objects which do not correspond to any network state change (e.g. failed transactions and their messages). In the case of failed transactions, it is desirable to index the associated data for end-user reference. This notion may also apply to other objects but should be considered carefully to avoid storing invalid or useless data.

"},{"location":"ledger-subquery/introduction/#primitive-entities","title":"Primitive entities","text":"

(see: schema.graphql)

  • blocks
  • transactions
  • messages
  • events
  • event attributes
"},{"location":"ledger-subquery/introduction/#entity-relationship-diagrams","title":"Entity relationship diagrams","text":""},{"location":"ledger-subquery/introduction/#versioning","title":"Versioning","text":"

The versions of both the GraphQL API and the Indexer itself can be retrieved simply using the following query on the GraphQL playground.

"},{"location":"ledger-subquery/introduction/#example","title":"Example:","text":"
query ReleaseVersionTest {\n  _metadata {\n    queryNodeVersion\n    indexerNodeVersion\n  }\n}\n

Each of these version numbers are stored as the value to the key \"version\" within their relevant module package.json file. These files can be found in the docker/node-cosmos/ and subql/packages/query/ directories for the Indexer and GraphQL versions, respectively.

// The Indexer version number, taken from \"docker/node-cosmos/package.json\"\n{ \"name\": \"@subql/node-cosmos\",\n\"version\": \"1.0.0\",\n...\n}\n

"},{"location":"ledger-subquery/introduction/#_metadata-entity","title":"\"_metadata\" Entity","text":"

The _metadata entity has further utility beyond the scope of the example query given prior. Using any of the relevant fields from the type definition below, internal states and config information can be retrieved with ease.

type _Metadata {\n        lastProcessedHeight: Int\n        lastProcessedTimestamp: Date\n        targetHeight: Int\n        chain: String\n        specName: String\n        genesisHash: String\n        indexerHealthy: Boolean\n        indexerNodeVersion: String\n        queryNodeVersion: String\n        rowCountEstimate: [TableEstimate]\n        dynamicDatasources: String\n      }\n

"},{"location":"ledger-subquery/introduction/#example_1","title":"Example:","text":"

If a developer was curious about the chain-id or whether the Indexer has passed any health checks, using indexerHealthy, these values can be returned within the playground or otherwise connected projects.

query ReleaseVersionTest {\n  _metadata {\n    chain\n    indexerHealthy\n  }\n}\n

"},{"location":"ledger_v2/","title":"Mainnet is here","text":"

The fetchhub mainnet forms the core of the Fetch.ai ecosystem. In these pages, you will find all information to setup your client and connect on the network and cli-introduction pages.

This documentation covers some of the things you need to know in order to prepare for and develop on this network.

"},{"location":"ledger_v2/#test-networks","title":"Test Networks","text":"

The starting point for most will be our test network, since it can provide you with test tokens with no value, that you can safely experiment with.

Head over the Command line client section for guidance on how to install and configure the fetchd client.

"},{"location":"ledger_v2/archived-networks/","title":"Mainnet Archive","text":""},{"location":"ledger_v2/archived-networks/#mainnet-archives","title":"Mainnet Archives","text":"

Archived data from previous versions of the fetchhub mainnet.

"},{"location":"ledger_v2/archived-networks/#fetchhub-3-archive","title":"Fetchhub-3 archive","text":"Parameter Value Chain ID fetchhub-3 Block range 4,504,601 --> 5,300,200 Date range 08/02/2022 --> 05/04/2022 Denomination afet Decimals 18 (1fet = 1000000000000000000afet) Version v0.9.1 RPC Endpoint https://rpc-fetchhub3-archive.fetch.ai:443 GRPC Endpoint https://grpc-fetchhub3-archive.fetch.ai:443 REST Endpoint https://rest-fetchhub3-archive.fetch.ai:443 Block Explorer https://explore-fetchhub3-archive.fetch.ai Token Faucet N/A Seed Node(s) N/A Snapshots https://storage.googleapis.com/fetch-ai-mainnet-snapshots/fetchhub-3-archive.tgz"},{"location":"ledger_v2/archived-networks/#fetchhub-2-archive","title":"Fetchhub-2 archive","text":"Parameter Value Chain ID fetchhub-2 Block range 2,436,701 --> 4,504,600 Date range 15/09/2021 --> 08/02/2022 Denomination afet Decimals 18 (1fet = 1000000000000000000afet) Version v0.8.7 RPC Endpoint https://rpc-fetchhub2-archive.fetch.ai:443 GRPC Endpoint https://grpc-fetchhub2-archive.fetch.ai:443 REST Endpoint https://rest-fetchhub2-archive.fetch.ai:443 Block Explorer https://explore-fetchhub2-archive.fetch.ai Token Faucet N/A Seed Node(s) N/A Snapshots https://storage.googleapis.com/fetch-ai-mainnet-snapshots/fetchhub-2-archive.tgz"},{"location":"ledger_v2/archived-networks/#fetchhub-1-archive","title":"Fetchhub-1 archive","text":"Parameter Value Chain ID fetchhub-1 Block range 1 --> 2,436,700 Date range 31/03/2021 --> 15/09/2021 Denomination afet Decimals 18 (1fet = 1000000000000000000afet) Version v0.7.4 RPC Endpoint https://rpc-fetchhub1-archive.fetch.ai:443 GRPC Endpoint N/A REST Endpoint https://rest-fetchhub1-archive.fetch.ai:443 Block Explorer https://explore-fetchhub1-archive.fetch.ai Token Faucet N/A Seed Node(s) N/A Snapshots https://storage.googleapis.com/fetch-ai-mainnet-snapshots/fetchhub-1-archive.tgz"},{"location":"ledger_v2/block-explorer/","title":"Block Explorer","text":"

Each of the networks has a dedicated block explorer web site associated with it. This is a useful tool for monitoring network activity.

"},{"location":"ledger_v2/block-explorer/#logging-in-with-the-ledger-nano","title":"Logging in with the Ledger Nano","text":"

Return to the block explorer landing page and click on the key button in the top right corner. You'll then be prompted to \"Sign in With Ledger\". You must accept this request on your ledger nano device. After completing this process, the key button will be replaced by a person icon with a link to your personal address page, which keeps track of the activity that you have performed on the test-net.

"},{"location":"ledger_v2/block-explorer/#getting-testnet-tokens-from-the-faucet","title":"Getting Testnet Tokens from the Faucet","text":"

For networks that support it, you can obtain tokens for your account by copying the address and pasting it into the token faucet. Then, return to the main page, press the \"Get Funds\" button and paste your address in the pop-up. Afterwards you can return to your address page (via the person icon) and should observe that you have been allocated 1 TESTFET.

"},{"location":"ledger_v2/block-explorer/#transferring-tokens-to-another-address","title":"Transferring Tokens to another Address","text":"

After receiving tokens, you can send these to another address using the purple Transfer button on your address page. This will trigger a pop-up that prompts you to specify the destination address and the amount you wish to transfer. After filling in this information, you will be asked to sign the transaction using your ledger nano. The confirmation that the transaction has been broadcast gives two links that can be used to check that the transaction has been executed on the blockchain using either the transaction hash or your account page.

Note:

The transaction format includes a memo field that can be used to check the transaction information on the ledger nano display.

"},{"location":"ledger_v2/block-explorer/#delegating-stake-to-a-validator","title":"Delegating Stake to a Validator","text":"

You can delegate your test-net tokens to a validator who is operating the network by clicking on the Validators tab, and selecting one of the validators that you wish to delegate stake towards. In the Voting Power panel there is an option to DELEGATE tokens. Pressing this button will trigger a pop-up that prompts you to select a delegation amount and then sign the transaction with your Ledger Nano device.

After delegating tokens, buttons labelled with REDELEGATE and UNDELEGATE will appear. The delegation of tokens to a validator provides you with a reward for helping to secure the network. It is also possible to delegate your tokens to a different validator using a REDELEGATE transaction. You can return any bonded tokens to your address by submitting an UNDELEGATE request, which will trigger the tokens to be returned after 21 days have elapsed. The rewards that you receive from delegating tokens to a validator are shown in the account page. These can be sent to your address by sending a WITHDRAW transaction.

"},{"location":"ledger_v2/building/","title":"Building the Ledger","text":""},{"location":"ledger_v2/building/#prerequisites","title":"Prerequisites","text":"
  • Go 1.18+ (installation instructions available here)
  • Packages: make, gcc (on Ubuntu, install them with sudo apt-get update && sudo apt-get install -y make gcc)
"},{"location":"ledger_v2/building/#building-the-code","title":"Building the code","text":"

Download the latest released version from github and build the project using the following commands:

git clone https://github.com/fetchai/fetchd.git && cd fetchd\n

Then build the code with the command:

make build\n

This will generate the ./build/fetchd binary.

For non-developer users we recommend that the user installs the binaries into their system. This can be done with the following command:

make install\n

This will install the binaries in the directory specified by your $GOBIN environment variable (default to ~/go/bin).

which fetchd\n

This should return a path such as ~/go/bin/fetchd (might be different depending on your actual go installation).

If you get no output, or an error such as which: no fetchd in ..., possible cause can either be that make install failed with some errors or that your go binary folder (default: ~/go/bin) is not in your PATH.

To add the ~/go/bin folder to your PATH, add this line at the end of your ~/.bashrc:

export PATH=$PATH:~/go/bin\n

and reload it with:

source ~/.bashrc\n

You can also verify that you are running the correct version

fetchd version\n

This should print a version number that must be compatible with the network you're connecting to (see the network page for the list of supported versions per network).

"},{"location":"ledger_v2/building/#faq","title":"FAQ","text":"
  • Error: failed to parse log level (main:info,state:info,:error): Unknown Level String: 'main:info,state:info,:error', defaulting to NoLevel

This means you had a pre-stargate version of fetchd (<= v0.7.x), and just installed a stargate version (>= v0.8.x), you'll need to remove the previous configuration files with:

rm ~/.fetchd/config/app.toml ~/.fetchd/config/config.toml\n
"},{"location":"ledger_v2/cli-bls/","title":"BLS signatures","text":"

The BLS algorithm can be selected when creating new keys and signing transactions. BLS supported keys are particularly useful for the increasing the efficiency of multi-signature transactions at the cost of simplicity in verification.

It affords the users a shorter and yet still robust grouping of each signature from party members without the sacrifice of security on each multi-signature transaction.

"},{"location":"ledger_v2/cli-bls/#creating-bls-keys","title":"Creating BLS keys","text":"

Creating BLS keys is straightforward in comparison to normal key instantiation with the addition of one extra parameter to the command. This example will show the additional flag required in comparison to a key with the standard algorithm; with 'bls12381' in place of the default 'secp256k1'.

"},{"location":"ledger_v2/cli-bls/#example","title":"Example","text":"
# Create a normal key\nfetchd keys add Ron\n\n# Create a key capable of BLS signed transactions\nfetchd keys add Tom_BLS --algo bls12381\n\n# 'Ron' can be assumed to define implicitly --algo secp256k1 by default\n
"},{"location":"ledger_v2/cli-bls/#bls-transactions-and-signatures","title":"BLS Transactions and signatures","text":"

After creating this BLS key, transactions can be carried out between two keys using the different algorithms.

"},{"location":"ledger_v2/cli-bls/#example_1","title":"Example","text":"

Ensure that the 'Ron' key has some funds before performing this example.

# Perform a normal transfer of funds from Ron to Tom_BLS\nfetchd tx bank send <address_of_Ron> <address_of_Tom_BLS> 1000test\n\n# This should provide a breakdown of the transaction parameters, including the gas fees\n# Keep note of these fees\n\n# Check that these funds have been transferred to Tom_BLS\nfetchd query bank balaces <address_of_Tom_BLS>\n\n# Perform a BLS signed transaction from Tom_BLS to Ron\nfetchd tx bank send <address_of_Tom_BLS> <address_of_Ron> 1000test\n\n# Compare the difference between information printed from each transaction and observe\n# the difference in gas costs (?)\n\n# Now assure funds were successfully transferred back to Ron through a BLS signed transaction \nfetchd query bank balaces <address_of_Tom_BLS>\nfetchd query bank balaces <address_of_Ron>\n
"},{"location":"ledger_v2/cli-governance/","title":"Governance Proposals","text":"

In order to change any attribute of a network, a governance proposal must be submitted. This could be a simple poll, a software update or a governing parameter change.

"},{"location":"ledger_v2/cli-governance/#parameter-change","title":"Parameter change","text":"

This is an example of the process in which network parameters may be changed through the use of a governance proposal.

The values within this code can be changed in order to alter the minimum deposited fund threshold for a proposal to enter the voting phase, and the length of the deposit stage in which the minimum deposit threshold must be met.

# A JSON file containing the following code should be created to instantiate the proposal.\n# The two variables of interest are the \"amount\" which is set from 10000000stake to 1000stake\n# and the \"max_deposit_period\" which is changed from the default value to 7200000000000\n# equal to 2 hours, instead of the standard 2 days (in nanoseconds).\n\n{\n  \"title\": \"Staking Param Change\",\n  \"description\": \"Update max validators\",\n  \"changes\": [\n    {\n      \"subspace\": \"staking\",\n      \"key\": \"MaxValidators\",\n      \"value\": 105\n    }\n  ],\n  \"deposit\": \"1000000000000000000atestfet\"\n}\n
# Create initial proposal by uploading the JSON file\n# this is signed by a key 'proposer' that provides a portion of the current threshold deposit\nfetchd tx gov submit-proposal --proposal ~/json_path/proposal.json --from proposer\n\n# In order to later refer to this proposal, the proposal_id can be determined\nfetchd query gov proposals\n

"},{"location":"ledger_v2/cli-governance/#proposal-deposit-phase","title":"Proposal deposit phase","text":"

The characteristics of the deposit phase are described by a set of network governance parameters, where the deposit period is two days from the initial proposal deposit until expiration, and a minimum threshold of 10000000denom as default. The minimum threshold must be met during this deposit period in order to proceed to the voting phase. The proposer may provide all of this threshold, or just some. In which case, supporters of the proposal may donate additional funding towards the goal of meeting the threshold.

At any point of the deposit stage, the deposit pot can be queried.

# To get the proposal ID, use the txhash obtained when the proposal was submitted and run the following command:\nfetchd query tx <txhash>\n\n# This command returns a text representation of the current total deposit value of a proposal\nfetchd query gov deposits <proposal_id>\n\n# Other users may contribute to funding the proposal using\nfetchd tx gov deposit <proposal_id> <deposit_amount> --from contributer\n

This documentation provides a more detailed explanation of the deposit funding period.

"},{"location":"ledger_v2/cli-governance/#proposal-voting-and-querying","title":"Proposal voting and querying","text":"

After the deposit period has passed, there are two outcomes: either the current minimum threshold is met, or the value is not met and the funds are returned. In the first case this proposal is submitted and to be voted on, returning a tally at the end of the voting period.

In order to submit a vote on a proposal that has passed into the voting phase, all staked users except the proposer may do so using this command.

# Submit a vote from a key 'voter' with the desired outcome of the voter\nfetchd tx gov vote <proposal_id> <yes|no|no_with_veto|abstain> --from voter\n

The current voting turnout and tally can be queried, which displays a list of all voters and their choice.

# The current voting statistics can be printed using\nfetchd query gov votes <proposal_id>\n

"},{"location":"ledger_v2/cli-governance/#example-output","title":"Example output","text":"
votes:\n- option: VOTE_OPTION_YES\n  proposal_id: \"1\"\n  voter: fetch1dmehhhvul8y7slqs3zu2z3fede9kzlnyupd9rr\n- option: VOTE_OPTION_NO\n  proposal_id: \"1\"\n  voter: fetch1064endj5ne5e868asnf0encctwlga4y2jf3h28\n- option: VOTE_OPTION_YES\n  proposal_id: \"1\"\n  voter: fetch1k3ee923osju93jm03fkfmewnal39fjdbakje1x\n
"},{"location":"ledger_v2/cli-governance/#voting-outcome","title":"Voting outcome","text":"

After the voting period has ended, the results are used to determine the next step of the proposal. The potential outcomes include:

  • Majority yes vote
    • The proposal passes through and the users act according to the proposal type - e.g. A Software update proposal passes, and users begin uptake of the new version
  • Majority no vote

    • The funds deposited to pass into the voting stage are returned, and there is no governance change
  • Majority no_with_veto vote

    • This outcome is indicative of a proposal which may undermine the current governance system, e.g. a proposal to set the deposit threshold or voting period to an absurd value
    • All funds deposited in the proposal are to be burned subject to this outcome, and there is no governance change
"},{"location":"ledger_v2/cli-introduction/","title":"CLI - Introduction","text":"

The command line client provides all of the capabilities for interacting with the fetch ledger such as creating addresses, sending transactions and the governance capabilities. Before starting with the command line client you need to follow the installation instructions here

"},{"location":"ledger_v2/cli-introduction/#connecting-to-a-network","title":"Connecting to a network","text":"

While some users will want to connect a node to the network and sync the entire blockchain, for many however, it is quicker and easier to connect directly to existing publically available nodes.

"},{"location":"ledger_v2/cli-introduction/#connecting-to-fetchhub-mainnet","title":"Connecting to fetchhub mainnet","text":"

To connect to the mainnet run the following configuration steps:

fetchd config chain-id fetchhub-4\nfetchd config node https://rpc-fetchhub.fetch.ai:443\n
"},{"location":"ledger_v2/cli-introduction/#connecting-to-dorado-network","title":"Connecting to dorado network","text":"

To connect to the dorado network run the following configuration steps:

fetchd config chain-id dorado-1\nfetchd config node https://rpc-dorado.fetch.ai:443\n

Checkout the Network Information page for more detailed information on the available networks.

"},{"location":"ledger_v2/cli-keys/","title":"CLI - Managing Keys","text":"

Managing your keys is an essential part of working with the Ledger, since all interactions are authenticated with these keys.

"},{"location":"ledger_v2/cli-keys/#adding-keys","title":"Adding keys","text":"

To create a new local key you need run the following command:

fetchd keys add <your_key_name>\n

Note

These keys are stored locally on your system. By default, these keys will be stored in the OS level keychain, however, in general these keys are considered less secure than using a hardware device

After running the command fetchd will print out a summary of the new key. An example of this output is shown below:

- name: test\n  type: local\n  address: fetch142tawq2rj397mctc3jtw9dfzf03ns0ze4swat0\n  pubkey: fetchpub1addwnpepqvtmze0ekffynnjx9n85g6sexzl49ze2vpgc2f52fteyyghjtvvqw682nkx\n  mnemonic: \"\"\n  threshold: 0\n  pubkeys: []\n

This will be followed by a 24-word mnemonic that can be used to re-generate the private key and address for the account (keep this safe, if ever used to control main-net tokens).

"},{"location":"ledger_v2/cli-keys/#looking-up-an-address","title":"Looking up an address","text":"

A common operation that you will want to do is to lookup the address for a specified key. This can be done quickly using the following command:

fetchd keys show -a <name of key>\n

An example of the expected output is shown below:

fetch142tawq2rj397mctc3jtw9dfzf03ns0ze4swat0\n

A less common operation, but still useful, would be to lookup the public key for a specified key. The can be achieved with the following command:

fetchd keys show -p <name of the key>\n

An example of the expected output is shown below:

fetchpub1addwnpepqvtmze0ekffynnjx9n85g6sexzl49ze2vpgc2f52fteyyghjtvvqw682nkx\n
"},{"location":"ledger_v2/cli-keys/#listing-keys","title":"Listing keys","text":"

To lookup more detailed information for all keys on your system use the following command:

fetchd keys list\n

This will output all of your keys information in a yaml format that is similar to the one generated when you first created the key.

- name: test\ntype: local\naddress: fetch142tawq2rj397mctc3jtw9dfzf03ns0ze4swat0\n  pubkey: fetchpub1addwnpepqvtmze0ekffynnjx9n85g6sexzl49ze2vpgc2f52fteyyghjtvvqw682nkx\n  mnemonic: \"\"\nthreshold: 0\npubkeys: []\n
"},{"location":"ledger_v2/cli-keys/#recovering-a-key","title":"Recovering a key","text":"

You can import a key from a 24-word mnemonic by running:

fetchd keys add <name> --recover\n> Enter your bip39 mnemonic\n<type or paste your mnemonic>\n
You'll be prompted to enter the mnemonic phrase, and it will then print the matching address and key details as when creating a new key.

"},{"location":"ledger_v2/cli-keys/#hardware-wallets","title":"Hardware Wallets","text":""},{"location":"ledger_v2/cli-keys/#setup","title":"Setup","text":"

We recommend hardware wallets as a solution for managing private keys. The Fetch ledger is compatible with Ledger Nano hardware wallets. To use your Ledger Nano you will need to complete the following steps:

  1. Set-up your wallet by creating a PIN and passphrase, which must be stored securely to enable recovery if the device is lost or damaged.
  2. Connect your device to your PC and update the firmware to the latest version using the Ledger Live application.
  3. Install the Cosmos application using the software manager (Manager > Cosmos > Install).
"},{"location":"ledger_v2/cli-keys/#adding-a-new-key","title":"Adding a new key","text":"

In order to use the hardware wallet address with the cli, the user must first add it via fetchd. This process only records the public information about the key.

To import the key first plug in the device and enter the device pin. Once you have unlocked the device navigate to the Cosmos app on the device and open it.

To add the key use the following command:

fetchd keys add <name for the key> --ledger --index 0\n

Note

The --ledger flag tells the command line tool to talk to the ledger device and the --index flag selects which HD index should be used.

When running this command, the Ledger device will prompt you to verify the generated address. Once you have done this you will get an output in the following form:

- name: hw1\n  type: ledger\n  address: fetch1xqqftqp8ranv2taxsx8h594xprfw3qxl7j3ra2\n  pubkey: fetchpub1addwnpepq2dulyd9mly3xqnvfgdsjkqlqzsxldpdhd6cnpm67sx90zhfw2ragk9my5h\n  mnemonic: \"\"\nthreshold: 0\npubkeys: []\n
"},{"location":"ledger_v2/cli-multisig/","title":"Multisig keys","text":"

This feature of fetchd allows users to securely control keys in a number of configurations. Using a threshold number K of maximum N keys, a user or group of users can set the minimum number of keys required to sign a transaction. Some examples of these configurations allow some useful features such as the choice of a spare key, where only one key is required to sign (K=1) but there are two keys available to do so. Another more complex example configuration is set out below.

"},{"location":"ledger_v2/cli-multisig/#creating-a-multisig-key","title":"Creating a multisig key","text":"

The following represents the syntax and argument layout of the fetchd command to create a multisig key.

# Create a simple multisig key with a threshold of 1 as default\nfetchd keys add <multisig_key_name> --multisig <list_of_key_names>\n\n# Creating a multisig key with a higher threshold, K\nfetchd keys add <multisig_key_name> --multisig <list_of_key_names> --multisig-threshold <threshold integer K>\n
"},{"location":"ledger_v2/cli-multisig/#example-instantiation-of-a-multisig-key","title":"Example instantiation of a multisig key","text":"

This example represents a shared multisig key that could be used within a business amongst three account holders - where at least two of three (K=2) must sign off on each transaction.

# Create the three keys owned by the separate account holders\nfetchd keys add fred\nfetchd keys add ted\nfetchd keys add ned\n\n# Create the multisig key from keys above\nfetchd keys add business_key --multisig fred,ted,ned --multisig-threshold 2\n

You will need the address of the business_key later in the example. Here just a reminder how to get it:

fetchd keys show -a business_key\n
"},{"location":"ledger_v2/cli-multisig/#signing-and-broadcasting-multisig-transactions","title":"Signing and broadcasting multisig transactions","text":"

Transactions must be signed and broadcast before they are carried out.

In order to sign a multisig transaction, the transaction itself must not be immediately broadcast; but instead, the keyholders must each sign until a minimum threshold K signatures are present.

For this example we will be performing the transaction on the Dorado network and therefore will be using atestfet as the denomination, and a gas price of 1000000000atestfet (this should be changed depending on the actual currency and network used).

"},{"location":"ledger_v2/cli-multisig/#multisig-transaction-example","title":"Multisig transaction example","text":"
# Create a key to represent a vendor that the business must pay\nfetchd keys add vendor\n\n# Generate a transaction as an output file to be signed by\n# the keyholders, 'ted' and 'fred' in this example\nfetchd tx bank send <business_key address> <vendor address> 1000atestfet --gas 90000 --gas-prices 1000000000atestfet --generate-only > transfer.json\n\n# you'll get \"account <address of business_key> not found\" error for missing funds\n# add funds to <address of business_key> using block explorer or by eg\ncurl -X POST -H 'Content-Type: application/json' -d '{\"address\":\"<address of business_key>\"}' https://faucet-dorado.fetch.ai/api/v3/claims\n\n# This transaction file (transfer.json) is then made available for\n# the first keyholder to sign, 'fred'\nfetchd tx sign transfer.json --chain-id dorado-1 --from fred --multisig <address of business_key> > transfer_fredsigned.json\n\n# This is repeated for 'ted'\nfetchd tx sign transfer.json --chain-id dorado-1 --from ted --multisig <address of business_key> > transfer_tedsigned.json\n\n# These two files are then collated together and used as inputs to the\n# multisign command to create a fully signed transaction\nfetchd tx multisign transfer.json business_key transfer_fredsigned.json transfer_tedsigned.json > signed_transfer.json\n\n# Now that the transaction is fully signed, it may be broadcast\nfetchd tx broadcast signed_transfer.json\n\n# Now display the result of the transaction and confirm that the vendor has\n# received payment\nfetchd query bank balances <address of vendor>\n

It is important to note that this method of signing transactions can apply to all types of transaction.

"},{"location":"ledger_v2/cli-multisig/#other-multisig-transaction-examples","title":"Other multisig transaction examples","text":"
# In order to create a staking transaction using a multisig key\n# the same process as above can be used with the output file of this command\nfetchd tx staking delegate <fetchvaloper address> 10000atestfet --from <address of business_key> --gas 200000 --gas-prices 1000000000atestfet --generate-only > stake.json\n\n# The following command can also be used to create a withdrawal transaction for the\n# rewards from staking when using a multisig key - this too must be signed as before\nfetchd tx distribution withdraw-all-rewards --from <address of business_key> --gas 150000 --gas-prices 1000000000atestfet --generate-only > withdrawal.json\n
"},{"location":"ledger_v2/cli-tokens/","title":"CLI - Managing Tokens","text":""},{"location":"ledger_v2/cli-tokens/#querying-your-balance","title":"Querying your balance","text":"

Once fetchd is configured for the desired network. The user can query their balance using the following command:

fetchd query bank balances fetch1akvyhle79nts4rwn075t85xrwmp5ysuqynxcn4\n

If the address exists on the network then the user will expect to see an output in the following form:

balances:\n- amount: \"8000000000000000000\"\n  denom: atestfet\npagination:\n  next_key: null\n  total: \"0\"\n
"},{"location":"ledger_v2/cli-tokens/#sending-funds","title":"Sending funds","text":"

Before sending funds, make sure the sender address has tokens available by querying your balance as shown above. Checkout the Token Faucet page for more information on how to add test tokens to your address.

To send funds from one address to another address then you would use the tx send subcommand. As shown below:

fetchd tx bank send <from address or key name> <target address> <amount>\n

In a more concrete example if the user wanted to send 100atestfet from main key/address to fetch106vm9q6ezu9va7v7e0cvq0nedc54egjm692fcp then the following command would be used.

fetchd tx bank send main fetch106vm9q6ezu9va7v7e0cvq0nedc54egjm692fcp 100atestfet\n

When you run the command you will get a similar output and prompt. The user can check the details of the transfer and then press 'y' to confirm the transfer.

{\"body\":{\"messages\":[{\"@type\":\"/cosmos.bank.v1beta1.MsgSend\",\"from_address\":\"fetch12cjntwl32dry7fxck8qlgxq6na3fk5juwjdyy3\",\"to_address\":\"fetch1hph8kd54gl6qk0hy5rl08qw9gcr4vltmk3w02v\",\"amount\":[{\"denom\":\"atestfet\",\"amount\":\"100\"}]}],\"memo\":\"\",\"timeout_height\":\"0\",\"extension_options\":[],\"non_critical_extension_options\":[]},\"auth_info\":{\"signer_infos\":[],\"fee\":{\"amount\":[],\"gas_limit\":\"200000\",\"payer\":\"\",\"granter\":\"\"}},\"signatures\":[]}\n\nconfirm transaction before signing and broadcasting [y/N]: y\n

Once the transfer has been made a summary is presented to the user. An example is shown below:

code: 0\ncodespace: \"\"\ndata: \"\"\ngas_used: \"0\"\ngas_wanted: \"0\"\nheight: \"0\"\ninfo: \"\"\nlogs: []\nraw_log: '[]'\ntimestamp: \"\"\ntx: null\ntxhash: 77C7382A0B1B9FE39257A6C16C7E3169A875CB3A87F2CE9D947D7C1335B53E76\n

On failure, the response will have a non zero code, as well as some logs under the raw_log key:

code: 4\ncodespace: sdk\ndata: \"\"\ngas_used: \"0\"\ngas_wanted: \"0\"\nheight: \"0\"\ninfo: \"\"\nlogs: []\nraw_log: 'signature verification failed; please verify account number (5815) and chain-id\n  (dorado-1): unauthorized'\ntimestamp: \"\"\ntx: null\ntxhash: 23701B052B423D63EB4AC94773B5B8227B03A576692A57999E92F2554F2372D4\n
"},{"location":"ledger_v2/delegator-guide-cli/","title":"CLI - Delegator guide","text":""},{"location":"ledger_v2/delegator-guide-cli/#querying-the-state","title":"Querying the state","text":""},{"location":"ledger_v2/delegator-guide-cli/#querying-the-current-staking-holdings-of-the-validators","title":"Querying the current staking holdings of the validators","text":"

The following command can be used to retrieve the current staking holdings of all validators:

fetchd query staking validators\n

On dorado network, this will produce an output similar to the following, describing the status of all the existing validators:

- |\n  operatoraddress: fetchvaloper1z72rph6l5j6ex83n4urputykawcqg6t98xul2w\n  conspubkey: fetchvalconspub1zcjduepq3urw6c6u0zvqmde4vr4gmy56nnq57shdhg56jynpu8n3s74hrm0q0mzqrx\n  jailed: false\n  status: 2\n  tokens: \"1000000000000000000000\"\n  delegatorshares: \"1000000000000000000000.000000000000000000\"\n  description:\n    moniker: validator5\n    identity: \"\"\n    website: \"\"\n    security_contact: \"\"\n    details: \"\"\n  unbondingheight: 0\n  unbondingcompletiontime: 1970-01-01T00:00:00Z\n  commission:\n    commission_rates:\n      rate: \"0.050000000000000000\"\n      max_rate: \"0.100000000000000000\"\n      max_change_rate: \"0.010000000000000000\"\n    update_time: 2021-02-12T12:41:25.579730119Z\n  minselfdelegation: \"1000000000000000000000\"\n  producingblocks: true\n- |\n  operatoraddress: fetchvaloper1ysc8n5uspv4698nyk8u75lx98uu92zt7m3udw8\n  conspubkey: fetchvalconspub1zcjduepqmxr8gmcs6pwuxpsma264ax59wxtxd3vchrcv2c06deq9986kwt3s0wsk6n\n  jailed: false\n  status: 2\n  tokens: \"1000000000000000000000\"\n  delegatorshares: \"1000000000000000000000.000000000000000000\"\n  description:\n    moniker: validator2\n    identity: \"\"\n    website: \"\"\n    security_contact: \"\"\n    details: \"\"\n  unbondingheight: 0\n  unbondingcompletiontime: 1970-01-01T00:00:00Z\n  commission:\n    commission_rates:\n      rate: \"0.050000000000000000\"\n      max_rate: \"0.100000000000000000\"\n      max_change_rate: \"0.010000000000000000\"\n    update_time: 2021-02-03T13:00:00Z\n  minselfdelegation: \"1000000000000000000000\"\n  producingblocks: true\n...\n

To obtain the same information for a single validator, use the following command, providing the operatoraddress of the validator.

fetchd query staking validator fetchvaloper1z72rph6l5j6ex83n4urputykawcqg6t98xul2w\n

A delegator will be particularly interested in the following keys:

  • commission/commission_rates/rate: The commission rate on revenue charged to any delegator by the validator.
  • commission/commission_rates/max_change_rate: The maximum daily increase of the validator's commission. This parameter cannot be changed by the validator operator.
  • commission/commission_rates/max_rate: The maximum commission rate this validator can charge. This parameter cannot be changed by the validator operator.
  • minselfdelegation: Minimum amount of atestfet the validator need to have bonded at all time. If the validator's self-bonded stake falls below this limit, their entire staking pool (i.e. all its delegators) will unbond. This parameter exists as a safeguard for delegators. Indeed, when a validator misbehaves, part of their total stake gets slashed. This includes the validator's self-delegateds stake as well as their delegators' stake. Thus, a validator with a high amount of self-delegated atestfet has more skin-in-the-game than a validator with a low amount. The minimum self-bond amount parameter guarantees to delegators that a validator will never fall below a certain amount of self-bonded stake, thereby ensuring a minimum level of skin-in-the-game. This parameter can only be increased by the validator operator.
"},{"location":"ledger_v2/delegator-guide-cli/#query-the-delegations-made-to-a-validator","title":"Query the delegations made to a validator","text":"

From a validator address, we can retrieve the list of delegations it received:

fetchd query staking delegations-to fetchvaloper1z72rph6l5j6ex83n4urputykawcqg6t98xul2w\n

Here is a sample of delegations validator2 received on dorado:

- delegation:\n    delegator_address: fetch1z72rph6l5j6ex83n4urputykawcqg6t9zzruef\n    validator_address: fetchvaloper1z72rph6l5j6ex83n4urputykawcqg6t98xul2w\n    shares: \"1000000000000000000000.000000000000000000\"\n  balance:\n    denom: atestfet\n    amount: \"1000000000000000000000\"\n- delegation:\n    delegator_address: fetch15fn3meky8ktfry3qm73xkpjckzw4dazxpfx34m\n    validator_address: fetchvaloper1z72rph6l5j6ex83n4urputykawcqg6t98xul2w\n    shares: \"100000.000000000000000000\"\n  balance:\n    denom: atestfet\n    amount: \"100000\"\n
"},{"location":"ledger_v2/delegator-guide-cli/#query-the-redelegations","title":"Query the redelegations","text":"

Delegators can choose to redelegate the tokens they already delegated from one validator to another. Redelegation takes effect immediately, without any waiting period. However, the tokens can't be redelegated until the initial redelegation transaction has completed its 21 day completion time (the unlocking time is indicated by the redelegationentry/completion_time field in the outputs below).

To obtain the list of redelegations made from a validator, use the following command:

fetchd query staking redelegations-from fetchvaloper1z72rph6l5j6ex83n4urputykawcqg6t98xul2w\n

This produces an output similar to the following, where delegator fetch15fn3meky8ktfry3qm73xkpjckzw4dazxpfx34m issued 2 redelegations from fetchvaloper1z72rph6l5j6ex83n4urputykawcqg6t98xul2w to fetchvaloper122veneudkzyalay6gusvrhhpp0560mparpanvu:

fetchd query staking redelegations-from fetchvaloper1z72rph6l5j6ex83n4urputykawcqg6t98xul2w\n- redelegation:\n    delegator_address: fetch15fn3meky8ktfry3qm73xkpjckzw4dazxpfx34m\n    validator_src_address: fetchvaloper1z72rph6l5j6ex83n4urputykawcqg6t98xul2w\n    validator_dst_address: fetchvaloper122veneudkzyalay6gusvrhhpp0560mparpanvu\n    entries: []\n  entries:\n  - redelegationentry:\n      creation_height: 291037\n      completion_time: 2021-03-24T14:24:38.973444629Z\n      initial_balance: \"50000\"\n      shares_dst: \"50000.000000000000000000\"\n    balance: \"50000\"\n  - redelegationentry:\n      creation_height: 291133\n      completion_time: 2021-03-24T14:33:43.425472866Z\n      initial_balance: \"10000\"\n      shares_dst: \"10000.000000000000000000\"\n    balance: \"10000\"\n

Similarly, the list of redelegations issued by a delegator can be obtained with the following:

fetchd query staking redelegations fetch15fn3meky8ktfry3qm73xkpjckzw4dazxpfx34m\n
"},{"location":"ledger_v2/delegator-guide-cli/#query-the-user-rewards","title":"Query the user rewards","text":"

After having delegated some tokens to a validator, the user is eligible to a share of the rewards the validator collects.

To retrieve all the outstanding rewards for an address, issue the following command:

fetchd query distribution rewards fetch15fn3meky8ktfry3qm73xkpjckzw4dazxpfx34m\n

This address having delegated tokens to 2 validators on dorado, produces the following output:

rewards:\n- validator_address: fetchvaloper1z72rph6l5j6ex83n4urputykawcqg6t98xul2w\n  reward:\n  - denom: atestfet\n    amount: \"0.000000000000200000\"\n- validator_address: fetchvaloper1ysc8n5uspv4698nyk8u75lx98uu92zt7m3udw8\n  reward:\n  - denom: atestfet\n    amount: \"0.000000000001000000\"\ntotal:\n- denom: atestfet\n  amount: \"0.000000000001200000\"\n

Rewards can also be filtered for a given validator, like validator5 here:

fetchd query distribution rewards fetch15fn3meky8ktfry3qm73xkpjckzw4dazxpfx34m fetchvaloper1z72rph6l5j6ex83n4urputykawcqg6t98xul2w\n

We now get only the reward from this validator:

- denom: atestfet\n  amount: \"0.000000000000200000\"\n
"},{"location":"ledger_v2/delegator-guide-cli/#delegator-operations","title":"Delegator operations","text":""},{"location":"ledger_v2/delegator-guide-cli/#delegating-tokens","title":"Delegating tokens","text":"

To delegate 1000000 atestfet tokens to the fetchvaloper1z72rph6l5j6ex83n4urputykawcqg6t98xul2w validator from the account myKey, the following command can be used:

fetchd tx staking delegate fetchvaloper1z72rph6l5j6ex83n4urputykawcqg6t98xul2w 1000000atestfet --from myKey\n

This will prompt for confirmation before issuing a transaction. After the transaction gets processed, it should appear under the delegations of the fetchvaloper1z72rph6l5j6ex83n4urputykawcqg6t98xul2w validator.

Note

Once delegated, tokens can only be redelegated to another validator, or unbond in order to be returned to their original account. It's important to note that those two operations take 21 days to complete, period in which the involved tokens will be unavailable.

"},{"location":"ledger_v2/delegator-guide-cli/#redelegating-tokens","title":"Redelegating tokens","text":"

Redelegating tokens allows to transfer already delegated tokens from one validator to another.

From the above example where we delegated 1000000 atestfet to fetchvaloper1z72rph6l5j6ex83n4urputykawcqg6t98xul2w, we can now redelegate parts or all of those tokens to another validator. For example, we redelegate 400000 atestfet from fetchvaloper1z72rph6l5j6ex83n4urputykawcqg6t98xul2w to fetchvaloper122veneudkzyalay6gusvrhhpp0560mparpanvu with the following command:

fetchd tx staking redelegate fetchvaloper1z72rph6l5j6ex83n4urputykawcqg6t98xul2w fetchvaloper122veneudkzyalay6gusvrhhpp0560mparpanvu 400000atestfet --from myKey\n

This will prompt for confirmation and issue a new transaction once accepted. From here, inspecting the delegations from our account, we'll see that our delegated tokens are now:

  • 600000atestfet to validator fetchvaloper1z72rph6l5j6ex83n4urputykawcqg6t98xul2w (our initial 1000000 minus the 400000 redelegated)
  • 400000atestfet to validator fetchvaloper122veneudkzyalay6gusvrhhpp0560mparpanvu

Now those 400000 atestfet we redelegated can't be redelegated anymore for 21 days (the exact date can be found by querying the redelegation transaction, under the completion_time key). Note that it's still possible to unbond those tokens if needed.

"},{"location":"ledger_v2/delegator-guide-cli/#unbonding-tokens","title":"Unbonding tokens","text":"

At any time, we can transfer parts or all of our delegated tokens back to our account:

fetchd tx staking unbond fetchvaloper1z72rph6l5j6ex83n4urputykawcqg6t98xul2w 300000atestfet --from myKey\n

Once again, this will prompt for confirmation and issue a transaction, initiating the transfer of 300000 atestfet from our stake on fetchvaloper1z72rph6l5j6ex83n4urputykawcqg6t98xul2w validator back to our account. Those tokens will then be available after a 21 day period (the exact date can be found by querying the redelegation transaction, under the completion_time key).

"},{"location":"ledger_v2/delegator-guide-cli/#withdrawing-rewards","title":"Withdrawing rewards","text":"

In order to transfer rewards to the wallet, the following command can be used:

fetchd tx distribution withdraw-rewards fetchvaloper1z72rph6l5j6ex83n4urputykawcqg6t98xul2w --from myKey\n

It requires the validator address from where the reward is withdrawn, and the name of the account private key having delegated tokens to the validator.

When having delegated tokens to multiple validators, all rewards can be claimed in a single command:

fetchd tx distribution withdraw-all-rewards --from myKey\n

The rewards then appears on the account as soon as the transaction is processed.

"},{"location":"ledger_v2/faucet/","title":"Token Faucet","text":"

For our test networks, we have a simple token faucet implemented to allow users of the network to get started quickly. You can send it an account address, and it will transfer some test token on it.

Token Faucets are network specific, depending on the network type they may or may not be deployed. Please check the networks page for specific details.

The Token Faucet itself is available from the network block explorer (GET FUNDS button on the homepage).

Enter your fetch... address in the popup and click Add funds button. Wait a few blocks for the transaction to be processed, and you should see it appear along with some funds on your account.

"},{"location":"ledger_v2/faucet/#add-funds-to-wallet-using-faucet-apis","title":"Add funds to Wallet using faucet APIs:","text":"

You can also request and get testnet tokens in your wallet using the APIs.

"},{"location":"ledger_v2/faucet/#get-some-atestfet","title":"Get some atestfet","text":"
curl -X POST -H 'Content-Type: application/json' -d '{\"address\":\"<address>\"}' https://faucet-dorado.fetch.ai/api/v3/claims\n
"},{"location":"ledger_v2/faucet/#get-some-nanomobx","title":"Get some nanomobx","text":"
curl -X POST -H 'Content-Type: application/json' -d '{\"address\":\"<address>\"}' https://faucet-mobx-dorado.fetch.ai/api/v3/claims\n
"},{"location":"ledger_v2/faucet/#get-some-ulrn","title":"Get some ulrn","text":"
curl -X POST -H 'Content-Type: application/json' -d '{\"address\":\"<address>\"}' https://faucet-lrn-dorado.fetch.ai/api/v3/claims\n
"},{"location":"ledger_v2/faucet/#sample-response-for-fund-request-to-faucet","title":"Sample response for fund request to faucet","text":"
{\"status\":\"ok\",\"uuid\":\"<uuid>\",\"target\":\"<address>\"}\n
"},{"location":"ledger_v2/faucet/#check-the-wallet-balance","title":"Check the wallet balance","text":"
fetchd query bank balances <address>\n
balances:\n- amount: \"<balance>\"\n  denom: atestfet\npagination:\n  next_key: null\n  total: \"0\"\n
"},{"location":"ledger_v2/governance/","title":"Governance","text":"

In order to be able to take part in the governance you either need to be running a full validator node or you need to have have delegated stake to an existing validator

"},{"location":"ledger_v2/governance/#stake-delegation","title":"Stake Delegation","text":"

In order to delegate stake to a validator the following command should be used:

fetchd tx staking delegate <VALOPER_ADDRESS> <AMOUNT> --from <KEY_NAME>\n

Where the <VALOPER_ADDRESS> begins with the prefix fetchvaloper1... and the <AMOUNT> field contains the currency denomination. For example:

fetchd tx staking delegate fetchvaloper1cct4fhhksplu9m9wjljuthjqhjj93z0s97p3g7 1000atestfet --from agent\n
"},{"location":"ledger_v2/governance/#proposals-overview","title":"Proposals Overview","text":"

There are three types of proposal:

  • Text Proposals: These are the most basic type of proposal. They can be used to get the opinion from participants of the network on a given topic.
  • Parameter Proposals: These proposals are used to update the value of an existing software parameter of the network.
  • Software Upgrade Proposals: These are used to propose an upgrade of the fetchd software, particularly in cases where the software changes might not necessary be backwards compatible or in some way present a major update to the network.
"},{"location":"ledger_v2/governance/#the-proposal-process","title":"The Proposal Process","text":"

Any FET holder can submit a proposal. In order for the proposal to be open for voting, it needs to come with a deposit that is greater than a parameter called minDeposit. The deposit need not be provided in its entirety by the submitter. If the initial proposer's deposit is not sufficient, the proposal enters the deposit period status. Then, any FET holder can increase the deposit by sending a depositTx transaction to the network.

Once the deposit reaches minDeposit, the proposal enters the voting period, which lasts 2 weeks. Any bonded FET holder can then cast a vote on this proposal. The user has the following options for voting:

  • Yes
  • No
  • NoWithVeto
  • Abstain

At the end of the voting period, the proposal is accepted if there are more than 50% Yes votes (excluding Abstain votes) and less than 33.33% of NoWithVeto votes (excluding Abstain votes).

"},{"location":"ledger_v2/governance/#generating-proposals","title":"Generating Proposals","text":"

When creating a proposal, the user will create a proposal JSON file with all the relevant information. An example of a text proposal is shown below:

{\n\"title\": \"Switch to semantic commit messages for fetchd\",\n\"description\": \"This proposal is advocating a switch to sematic commit messages\\nYou can find the full discussion here: https://github.com/fetchai/fetchd/issues/231\",\n\"type\": \"Text\",\n\"deposit\": \"10000000000000000000atestfet\"\n}\n

It is always recommended that the description of a text proposal has a link to a Github issue with the full proposal text along with the discussions about it.

Once the user has created the JSON file, to generate the text propsal on chain run the following command:

fetchd tx gov submit-proposal --proposal proposal.json --from <name of signing key>

"},{"location":"ledger_v2/governance/#increasing-the-deposit-for-a-proposal","title":"Increasing the deposit for a proposal","text":"

If a user wants to increase the deposit of a proposal they would run the following command:

fetchd tx gov deposit <proposalID> 100atestfet --from <key name>

For example:

fetchd tx gov deposit 2 100atestfet --from validator

To get the proposalID, use the txhash obtained when the proposal was submitted and run the following command:

fetchd query tx <txhash>

"},{"location":"ledger_v2/governance/#listing-current-proposals","title":"Listing current proposals","text":"

Current proposals are visible from the block explorer and using the CLI.

To get the list of current proposals and their corresponding proposal-ids the run the following command:

fetchd query gov proposals

"},{"location":"ledger_v2/governance/#voting-on-a-proposal","title":"Voting on a proposal","text":"

To vote for a proposal run the following command

fetchd tx gov vote <proposalID> <option> --from <delegatorKeyName>

For example:

fetchd tx gov vote 5 yes --from validator

Note

When using CLI commands make sure that your CLI is pointing at the correct network. See the CLI introduction documentation for more details

"},{"location":"ledger_v2/joining-a-testnet/","title":"Joining a testnet","text":"

In order to join the test network you will need to have the correct version of the fetchd ledger available on your system.

"},{"location":"ledger_v2/joining-a-testnet/#using-a-local-version","title":"Using a local version","text":"

Assuming that you have followed the installation guide. You should now have fetchd successfully installed in your path. You can check this with the following command:

fetchd version\n

This should print a version number that must be compatible with the network you're connecting to (see the network page for the list of supported versions per network).

"},{"location":"ledger_v2/joining-a-testnet/#configuring-the-client-fetchd","title":"Configuring the client fetchd","text":"

In general to configure the CLI to point at a given network it needs as a minimum the following configuration values

fetchd config chain-id <chain-id>\nfetchd config node <rpc url>\n
"},{"location":"ledger_v2/joining-a-testnet/#dorado-example","title":"Dorado example","text":"

In the case of the Dorado network this would be as follows:

fetchd config chain-id dorado-1\nfetchd config node https://rpc-dorado.fetch.ai:443\n
"},{"location":"ledger_v2/joining-a-testnet/#configuring-the-server-fetchd","title":"Configuring the server fetchd","text":"

Initialize fetchd by running command. This setups a default / empty genesis configuration.

fetchd init <moniker-name> --chain-id <chain id>\n

This will initialize default configuration files under the FETCHD_HOME folder, which default to ~/.fetchd/.

Execute the following command to download the latest genesis file:

curl <rpc url>/genesis | jq '.result.genesis' > ~/.fetchd/config/genesis.json\n

Finally connect fetchd to the network by getting it to connect to a seed node for the given network.

fetchd start --p2p.seeds=<network seed peers>\n

Dorado Example

Less abstractly then, if you wants to connect to the Dorado test net for example, you would need to run the following steps:

# init\nfetchd init my-first-fetch-node --chain-id dorado-1\n\n# genesis\ncurl https://rpc-dorado.fetch.ai:443 | jq '.result.genesis' > ~/.fetchd/config/genesis.json\n# ...or, if that's too large to download from the rpc interface as a single file...\ncurl https://storage.googleapis.com/fetch-ai-testnet-genesis/genesis-dorado-827201.json --output ~/.fetchd/config/genesis.json\n\n# start\nfetchd start --p2p.seeds=eb9b9717975b49a57e62ea93aa4480e091ae0660@connect-dorado.fetch.ai:36556,46d2f86a255ece3daf244e2ca11d5be0f16cb633@connect-dorado.fetch.ai:36557,066fc564979b1f3173615f101b62448ac7e00eb1@connect-dorado.fetch.ai:36558\n

Your local node will then start to synchronise itself with the network, replaying all blocks and transactions up to the current block. Depending on the age of the network and your hard disk speed, this could take a while. Consider using chain snapshots to speed up this process.

To know when your node as finished syncing, you can query it's status from its RPC API:

curl -s 127.0.0.1:26657/status |  jq '.result.sync_info.catching_up'\ntrue # this will print \"false\" once your node is up to date\n
"},{"location":"ledger_v2/live-networks/","title":"Networks","text":""},{"location":"ledger_v2/live-networks/#mainnet","title":"Mainnet","text":"

The chain identifier of our production network is fetchhub-4.

Parameter Value Chain ID fetchhub-4 Block range 5,300,201 --> Date range 05/04/2022 --> Denomination afet Decimals 18 (1fet = 1000000000000000000afet) Version v0.10.3 up to block 6295500 v0.10.4 up to block 7305500 v0.10.5 for blocks > 7305500 RPC Endpoint https://rpc-fetchhub.fetch.ai:443 GRPC Endpoint https://grpc-fetchhub.fetch.ai:443 REST Endpoint https://rest-fetchhub.fetch.ai:443 Block Explorer https://explore-fetchhub.fetch.ai Token Faucet N/A Genesis curl https://raw.githubusercontent.com/fetchai/genesis-fetchhub/fetchhub-4/fetchhub-4/data/genesis_migrated_5300200.json --output ~/.fetchd/config/genesis.json Seed Node(s) 17693da418c15c95d629994a320e2c4f51a8069b@connect-fetchhub.fetch.ai:36456,a575c681c2861fe945f77cb3aba0357da294f1f2@connect-fetchhub.fetch.ai:36457,d7cda986c9f59ab9e05058a803c3d0300d15d8da@connect-fetchhub.fetch.ai:36458 Snapshots https://storage.googleapis.com/fetch-ai-mainnet-snapshots/fetchhub-4-pruned.tgz https://storage.googleapis.com/fetch-ai-mainnet-snapshots/fetchhub-4-full.tgz https://storage.googleapis.com/fetch-ai-mainnet-snapshots/fetchhub-4-archive.tgz"},{"location":"ledger_v2/live-networks/#test-nets","title":"Test Nets","text":""},{"location":"ledger_v2/live-networks/#dorado","title":"Dorado","text":"

This network is running the same major version of fetchd as our mainnet (fetchhub-4), possibly at a more recent minor version.

It is stable for deploying smart contracts and testing IBC.

Parameter Value Chain ID dorado-1 Denomination atestfet Decimals 18 (1testfet = 1000000000000000000atestfet) Min Gas Prices 1000000000atestfet Version v0.10.3 up to block 947800 v0.10.4 for blocks > 947800 and < 2198000 v0.10.5 for blocks > 2198000 RPC Endpoint https://rpc-dorado.fetch.ai:443 GRPC Endpoint https://grpc-dorado.fetch.ai:443 REST Endpoint https://rest-dorado.fetch.ai:443 Block Explorer https://explore-dorado.fetch.ai/ Ledger Explorer https://browse-dorado.fetch.ai/ Token Faucet Use block explorer Genesis curl https://storage.googleapis.com/fetch-ai-testnet-genesis/genesis-dorado-827201.json --output ~/.fetchd/config/genesis.json Seed Node(s) eb9b9717975b49a57e62ea93aa4480e091ae0660@connect-dorado.fetch.ai:36556,46d2f86a255ece3daf244e2ca11d5be0f16cb633@connect-dorado.fetch.ai:36557,066fc564979b1f3173615f101b62448ac7e00eb1@connect-dorado.fetch.ai:36558 Snapshots https://storage.googleapis.com/fetch-ai-testnet-snapshots/dorado-pruned.tgz https://storage.googleapis.com/fetch-ai-testnet-snapshots/dorado-full.tgz https://storage.googleapis.com/fetch-ai-testnet-snapshots/dorado-archive.tgz"},{"location":"ledger_v2/single-node-network/","title":"Running a Single Node Network","text":"

Especially for things like contract development, it can be very useful to be able to run a single node network for testing. This document will outline the steps that are required in order to configure a fetchd network of 1 node.

"},{"location":"ledger_v2/single-node-network/#network-setup","title":"Network Setup","text":"

These steps only need to be done once in order to setup the local network.

Step 1 - Build the ledger from source

Follow the build instructions in order to compile the latest version of the ledger.

Step 2 - Remove any existing networks

Since we are starting a new network we need to remove any local files that we have in our system from a previous network

rm -Rf ~/.fetchd

Step 3 - Create an initial genesis

Create the initial genesis file (~/.fetchd/config/genesis.json) with the following command:

fetchd init --chain-id localnet-1 my-local-node-name

  • localnet-1 is the chain id
  • my-local-node-name is the moniker for the node

If you want to make any updates to the genesis, it is a good opportunity to make these updates now.

Step 4 - Create your validator key

In the following steps we will need to create the public/private keypair for our node.

To create a new key called \"validator\" use the following command.

fetchd keys add validator

  • validator is the name of the key in the keyring

For more information checkout the complete documentation on keys.

Step 5 - Adding the validator to the network

To set the initial state for the network use the following command. This allocates 100000000000000000000 stake tokens to the validator which can be bonded.

fetchd add-genesis-account validator 100000000000000000000stake

stake is the default test token denomination in the cosmos ecosystem, but you could use afet, BTC etc.

Step 6 - Generating a validator transaction

To get your validator to sign the genesis block (and to agree that this is the correct genesis starting point) use the following command.

fetchd gentx validator 100000000000000000000stake --chain-id localnet-1

  • validator here is the name that you have given to the key

Step 7 - Building the complete genesis

To build final genesis configuration for the network run the following command

fetchd collect-gentxs

After running this command the network is successfully configured and you have computed the final genesis configuration for the network.

"},{"location":"ledger_v2/single-node-network/#running-the-local-node","title":"Running the local node","text":"

To run the network use the following command.

fetchd start

"},{"location":"ledger_v2/single-node-network/#resetting-the-network","title":"Resetting the network","text":"

Often you will want to clear out all the data from the network and start again. To do that in a local network simply run the following command:

fetchd tendermint unsafe-reset-all

This resets the chain back to genesis, you DO NOT need to perform the network setup steps again. After running this command you can simply run the fetchd start command again.

"},{"location":"ledger_v2/snapshots/","title":"Chain State Snapshots","text":"

As blockchains get longer, the process of syncing from the genesis block begins to take many hours, or even days to complete. In circumstances where a faster sync is required, various snapshots of the fetchd chain state data are available for download, to more quickly bootstrap a node.

Snapshots are available for both mainnet and the most recent testnet. The URLs can be obtained from the network page. We aim to update snapshots on a daily basis.

The example below uses the pruned mainnet snapshot, but can be adapted as required for full or archive nodes.

"},{"location":"ledger_v2/snapshots/#using-a-snapshot","title":"Using a snapshot","text":""},{"location":"ledger_v2/snapshots/#stop-your-node","title":"Stop your node","text":"

If you are already running fetchd, it is important that you stop it before proceeding. Instructions for this are highly installation dependent and beyond the scope of this document, but could be as simple as a Ctrl-C. If you have not already initialised your node, follow the instructions for joining a testnet (modifying for mainnet as appropriate), then return to this page before starting fetchd.

"},{"location":"ledger_v2/snapshots/#reset-your-node","title":"Reset your node","text":"

WARNING: This will irreversibly erase your node's state database. Ensure you take whatever backups you deem appropriate before proceeding.

If using fetchd <= 0.10.3 fetchd unsafe-reset-all

If using fetchd >= 0.10.4 fetchd tendermint unsafe-reset-all

"},{"location":"ledger_v2/snapshots/#download-and-install-the-snapshot","title":"Download and install the snapshot","text":"

Many options here! The example below assumes a bash-like environment, uses a single connection for downloading, confirms the md5sum of the downloaded data against that of the original, and does not land the original compressed data to disk. This is a good starting point, but depending on your local environment you may wish to make adaptations that eg sacrifice disk space and extra md5sum complexity for the benefit of parallel downloads with aria2. Entirely up to you... let us know how you get on!

# (optional) show the timestamp of the latest available snapshot\necho \"Latest available snapshot timestamp : $(curl -s -I  https://storage.googleapis.com/fetch-ai-mainnet-snapshots/fetchhub-4-pruned.tgz | grep last-modified | cut -f3- -d' ')\"\n# download, decompress and extract state database\ncurl -v https://storage.googleapis.com/fetch-ai-mainnet-snapshots/fetchhub-4-pruned.tgz -o- 2>headers.out | tee >(md5sum > md5sum.out) | gunzip -c | tar -xvf - --directory=~/.fetchd\n\n# (optional, but recommended) compare source md5 checksum provided in the headers by google, with the one calculated locally\n[[ $(grep 'x-goog-hash: md5' headers.out | sed -z 's/^.*md5=\\(.*\\)/\\1/g' | tr -d '\\r' | base64 -d | od -An -vtx1 | tr -d ' \\n') == $(awk '{ print $1 }' md5sum.out) ]] && echo \"OK - md5sum match\" || echo \"ERROR - md5sum MISMATCH\"\n# (optional) show the creation date of the downloaded snapshot\necho \"Downloaded snapshot timestamp: $(grep last-modified headers.out | cut -f3- -d' ')\"\n
"},{"location":"ledger_v2/snapshots/#restart-your-node","title":"Restart your node","text":"

Again, this entirely depends on your local installation, but a simple example for mainnet might be...

fetchd start --p2p.seeds 17693da418c15c95d629994a320e2c4f51a8069b@connect-fetchhub.fetch.ai:36456,a575c681c2861fe945f77cb3aba0357da294f1f2@connect-fetchhub.fetch.ai:36457,d7cda986c9f59ab9e05058a803c3d0300d15d8da@connect-fetchhub.fetch.ai:36458`.\n
"},{"location":"ledger_v2/state-sync/","title":"State-sync","text":"

State sync is a feature which allows you to quickly bootstrap a new node by allowing it to pull a state snapshot taken by other nodes.

The state sync feature is only available from fetchd v0.10.6 and later. Prior versions needed to either sync from scratch or restore a chain snapshot, which both could take hours before having the node fully synced.

With state sync, it now takes only a few minutes before having an operational node.

"},{"location":"ledger_v2/state-sync/#configuring-the-new-node","title":"Configuring the new node","text":"

In order to instruct the node to sync itself using a state sync snapshot, it need some configuration in the ~/.fetchd/config/config.toml file. Open this file in an editor and lookup for the statesync section. By default, it should looks like this:

#######################################################\n###         State Sync Configuration Options        ###\n#######################################################\n[statesync]\n# State sync rapidly bootstraps a new node by discovering, fetching, and restoring a state machine\n# snapshot from peers instead of fetching and replaying historical blocks. Requires some peers in\n# the network to take and serve state machine snapshots. State sync is not attempted if the node\n# has any local state (LastBlockHeight > 0). The node will have a truncated block history,\n# starting from the height of the snapshot.\nenable = false\n# RPC servers (comma-separated) for light client verification of the synced state machine and\n# retrieval of state data for node bootstrapping. Also needs a trusted height and corresponding\n# header hash obtained from a trusted source, and a period during which validators can be trusted.\n#\n# For Cosmos SDK-based chains, trust_period should usually be about 2/3 of the unbonding time (~2\n# weeks) during which they can be financially punished (slashed) for misbehavior.\nrpc_servers = \"\"\ntrust_height = 0\ntrust_hash = \"\"\ntrust_period = \"168h0m0s\"\n# Time to spend discovering snapshots before initiating a restore.\ndiscovery_time = \"15s\"\n# Temporary directory for state sync snapshot chunks, defaults to the OS tempdir (typically /tmp).\n# Will create a new, randomly named directory within, and remove it when done.\ntemp_dir = \"\"\n# The timeout duration before re-requesting a chunk, possibly from a different\n# peer (default: 1 minute).\nchunk_request_timeout = \"10s\"\n# The number of concurrent chunk fetchers to run (default: 1).\nchunk_fetchers = \"4\"\n

A few changes are needed:

  • First, set enable = true to activate the state sync engine.
  • Then, at least 2 rpc servers must be provided. A good place to find some is the cosmos chain registry. Servers must be comma separated without space (ie: rpc_servers = \"https://rpc-fetchhub.fetch.ai:443,https://fetch-rpc.polkachu.com:443\"). These servers will be used to verify the snapshots, so make sure you trust them enough for this.
  • a recent trust_height and trust_hash are needed. Recent means it must be contained in the trust_period (168 hours, or ~1 week old by default). These can be obtained from a RPC server you trust to provide you correct data (and the 2nd RPC server from rpc_servers will be charged of confirming that the data are correct).
  • And last, set chunk_request_timeout to 60s (the 10s default value seems too short and can lead to \"context deadline exceeded\" timeout errors when verifying the hashes)

To retrieve the correct value for a fetch.ai RPC server, and the current network height, use:

curl https://rpc-fetchhub.fetch.ai:443/block | jq -r '{\"trusted_hash\": .result.block_id.hash, \"trusted_height\": .result.block.header.height}'\n{\n\"trusted_hash\": \"...some hash...\",\n  \"trusted_height\": \"...some height...\"\n}\n

and set the trusted_hash and trusted_height values in the config file.

Once this is set, make sure you have the correct genesis by downloading it from the RPC node:

curl https://raw.githubusercontent.com/fetchai/genesis-fetchhub/fetchhub-4/fetchhub-4/data/genesis_migrated_5300200.json --output ~/.fetchd/config/genesis.json\n

and start the node using the seeds from the chain-registry:

fetchd start --p2p.seeds=\"17693da418c15c95d629994a320e2c4f51a8069b@connect-fetchhub.fetch.ai:36456,a575c681c2861fe945f77cb3aba0357da294f1f2@connect-fetchhub.fetch.ai:36457,d7cda986c9f59ab9e05058a803c3d0300d15d8da@connect-fetchhub.fetch.ai:36458\"\n

After the node initialized, it will start searching for available snapshots, and it should print log messages similar to:

8:22AM INF Discovered new snapshot format=1 hash=\"\ufffd \u076b/\ufffd\ufffd\\r\ufffdF#C(pD\ufffd<\ufffd\ufffd\\x066\ufffd\ufffd\\x1f\ufffd\ufffd\\x1f<i\ufffd\u075d\" height=2000 module=statesync\n8:22AM INF Discovered new snapshot format=1 hash=\"F\ufffd=\\x05\ufffdGh\ufffd{\ufffd|\ufffd\ufffd\ufffd\ufffd\ufffd,\ufffdQ'\ufffd=]\\x1a\ufffd$\ufffdb\ufffd\u05bfQ\" height=1900 module=statesync\n

The node will select the one with the height value the closest to the tip of the chain, and it will then start restoring the state, and finish syncing the few blocks remaining.

If it fails to verify any blocks or hash when restoring, it will attempt to restore the next available snapshot, and, if no more are available, will fallback in discovery mode until an usable snapshot is available.

"},{"location":"ledger_v2/state-sync/#configure-an-existing-node-to-provide-snapshots","title":"Configure an existing node to provide snapshots","text":"

In order to provide new nodes snapshots they can start from, existing nodes need to be configured to create these snapshots. This requires changes in the ~/.fetchd/config/app.toml file, in the state-sync section.

###############################################################################\n###                        State Sync Configuration                         ###\n###############################################################################\n\n# State sync snapshots allow other nodes to rapidly join the network without replaying historical\n# blocks, instead downloading and applying a snapshot of the application state at a given height.\n[state-sync]\n\n# snapshot-interval specifies the block interval at which local state sync snapshots are\n# taken (0 to disable). Must be a multiple of pruning-keep-every.\nsnapshot-interval = 0\n\n# snapshot-keep-recent specifies the number of recent snapshots to keep and serve (0 to keep all).\nsnapshot-keep-recent = 2\n

Here snapshot-interval must be set to a number of blocks between each snapshot creation and it must be a multiple of your node prunning settings (default is 100, so valid values are 100, 1000, 700...). The number of snapshots to keep can be set with snapshot-keep-recent.

"},{"location":"ledger_v2/versions/","title":"Versions","text":"

There are multiple versions of the fetchd software with differing levels of features and maturity. The following table outlines the rough overview of these versions

Version Maturity Description v0.2.x Deprecated This is a stable version of the network to support agent development v0.3.x Deprecated Builds upon our stable release and adds support for the random beacon consensus module v0.4.x Deprecated Builds upon the random beacon consensus and adds support for aggregated signatures v0.5.x Deprecated Extension of v0.4.x v0.6.x Deprecated Extension of v0.5.x v0.7.x Deprecated Pre stargate fetchhub mainnet version v0.8.x Deprecated Mainline version of the network used for Stargate fetchhub mainnet v0.9.x Deprecated Mainline version of the network used for Carpicorn fetchhub mainnet v0.10.x Stable Mainline version of the network used for Dorado fetchhub mainnet"},{"location":"ledger_v2/versions/#upgrade-history","title":"Upgrade history","text":"

For node operators, the full upgrade history, documentations and procedures are available at: https://github.com/fetchai/genesis-fetchhub

"},{"location":"ledger_v2/validators/overview/","title":"Validators Overview","text":""},{"location":"ledger_v2/validators/overview/#introduction","title":"Introduction","text":"

The Fetch.ai Ledger relies on a set of validators that are responsible for committing new blocks in the blockchain. These validators participate in the consensus protocol by broadcasting votes which contain cryptographic signatures signed by each validator's private key.

Validator candidates can bond their own FET and have FET delegated, or staked, to them by token holders. The validators are determined by who has the most stake delegated to them. The top N validator candidates with the most stake will become the active validators.

Validators and their delegators will earn FET as block provisions and tokens as transaction fees through execution of the consensus protocol. Transaction fees will be paid in FET.

If validators double sign, are frequently offline or do not participate in governance, their staked FET (including FET of users that delegated to them) can be slashed. The penalty depends on the severity of the violation.

"},{"location":"ledger_v2/validators/overview/#hardware","title":"Hardware","text":"

The hardware resources for running a validator node largely depend on the network load. As a recommended configuration we suggest the following requirements

  • 2 x CPU, either Intel or AMD, with the SSE4.1, SSE4.2 and AVX flags (use lscpu to verify)
  • 8 GB RAM
  • 500 GB SSD
  • 100 Mbit/s always-on internet connection
  • Linux OS (Ubuntu 18.04 or 20.04 recommended) / MacOS

Uptime in incredibly important for being a validator. It is expected that validators will have appriopriate redundancies for compute, power, connectivity etc. While the blockchain itself it highly replicated it is also expected that validators will perform local storage backups in order to minimise validator down time.

"},{"location":"ledger_v2/validators/overview/#set-up-a-website","title":"Set Up a Website","text":"

Set up a dedicated validator's website and signal your intention to become a validator on our Discord server. This is important since delegators will want to have information about the entity they are delegating their FET to.

Strictly speaking this is not necessary, however, it is recommended. As a validator on the network you will want to get other community users to delegate stake to your validator. The more combined stake that a validate has then the great share of the block rewards they will take.

"},{"location":"ledger_v2/validators/overview/#seek-legal-advice","title":"Seek Legal Advice","text":"

Seek legal advice if you intend to run a Validator.

"},{"location":"ledger_v2/validators/overview/#community","title":"Community","text":"

We highly recommdend to check out the validator community on the discord channel for more information and to see that latest announcements about becoming a validator.

  • Discord
"},{"location":"ledger_v2/validators/security/","title":"Security","text":""},{"location":"ledger_v2/validators/security/#validator-security","title":"Validator Security","text":"

Each validator candidate is encouraged to run its operations independently, as diverse setups increase the resilience of the network. Validator candidates should commence their setup phase now in order to be on time for launch.

"},{"location":"ledger_v2/validators/security/#key-management-hsm","title":"Key Management - HSM","text":"

It is mission critical that an attacker cannot steal a validator's key. If this is possible, it puts the entire stake delegated to the compromised validator at risk. Hardware security modules are an important strategy for mitigating this risk. HSM modules must support ed25519 signatures.

"},{"location":"ledger_v2/validators/security/#sentry-nodes-ddos-protection","title":"Sentry Nodes (DDOS Protection)","text":"

Validators are responsible for ensuring that the network can sustain denial of service attacks.

One recommended way to mitigate these risks is for validators is to carefully structure their network topology in a so-called sentry node architecture.

Validator nodes should only connect to full-nodes they trust because they operate them themselves or are run by other validators they know socially. This architecture shifts the burden of denial-of-service from the validator's node directly to its sentry nodes, and may require new sentry nodes be spun up or activated to mitigate attacks on existing ones.

Sentry nodes can be quickly spun up or change their IP addresses. Because the links to the sentry nodes are in private IP space, an internet based attacked cannot disturb them directly. This will ensure validator block proposals and votes always make it to the rest of the network.

To setup your sentry node architecture you can follow the instructions below:

Validators nodes should edit their config.toml:

# Comma separated list of nodes to keep persistent connections to\n# Do not add private peers to this list if you don't want them advertised\npersistent_peers =[list of sentry nodes]\n# Set true to enable the peer-exchange reactor\npex = false\n

Sentry Nodes should edit their config.toml:

# Comma separated list of peer IDs to keep private (will not be gossiped to other peers)\n# Example ID: 3e16af0cead27979e1fc3dac57d03df3c7a77acc@3.87.179.235:26656\nprivate_peer_ids = \"node_ids_of_private_peers\"\n
"},{"location":"ledger_v2/validators/setup/","title":"Setting up a Validator Node","text":"

This guide assumes that you have successfuly installed, configured and connected your validator to the desired network.

"},{"location":"ledger_v2/validators/setup/#creating-a-validator","title":"Creating a validator","text":"

To create a validator on the network you will need to send a transaction to the network bonding / staking your FET tokens. This process registers you as a validator and if you are one of the chosen validators you will start to produce blocks.

fetchd tx staking create-validator \\\n--amount=<the amount to bond> \\\n--pubkey=$(fetchd tendermint show-validator) \\\n--moniker=\"choose a moniker\" \\\n--chain-id=<chain_id> \\\n--commission-rate=\"0.10\" \\\n--commission-max-rate=\"0.20\" \\\n--commission-max-change-rate=\"0.01\" \\\n--min-self-delegation=\"<the min self delegation>\" \\\n--gas auto --gas-adjustment 1.5 --gas-prices \"<network gas prices>\" \\\n--from=<key_name>\n

Dorado Example

Before trying to create a validator you should verify that you have some tokens available beforehand. The easiest way to do this is via the CLI.

Here is an sample of a typical command line command that will register the node as running the validator.

fetchd tx staking create-validator \\\n--amount=1000000000000000000atestfet \\\n--pubkey=$(fetchd tendermint show-validator) \\\n--moniker=\"my-test-validator\" \\\n--chain-id=dorado-1 \\\n--commission-rate=\"0.10\" \\\n--commission-max-rate=\"0.20\" \\\n--commission-max-change-rate=\"0.01\" \\\n--min-self-delegation=\"1000000000000000000\" \\\n--gas auto --gas-adjustment 1.5 --gas-prices 1000000000atestfet \\\n--from=test-key\n
"},{"location":"ledger_v2/validators/setup/#editing-a-validator","title":"Editing a validator","text":"

Over time it is possible that validators will want to adjust various settings about their nodes. This can be simple things like the associated website for a validator or more consequential actions like altering the commission rate.

In either case, should a validator choose to make this update they would send an \"edit-validator\" transaction to the network. These can be created in a similar way to the \"create-validator\" transactions as shown below:

fetchd tx staking edit-validator\n  --moniker=\"choose a moniker\" \\\n--website=\"https://fetch.ai\" \\\n--details=\"To infinity and beyond!\" \\\n--chain-id=<chain_id> \\\n--commission-rate=\"0.10\" \\\n--from=<key_name>\n
"},{"location":"ledger_v2/validators/setup/#unbonding-a-validator","title":"Unbonding a validator","text":"

When / if a validator wants to stop being a validator for any reason, they can unbond some or all of their staked tokens. This is done with the following command.

fetchd tx staking unbond \\\n<validator operator address> \\\n<amount to remove> \\\n--from <key name>\n

An example of the command is given in the following example:

fetchd tx staking unbond \\\nfetchvaloper1jqqwdch3jmzlmj4tjfn67s3sqm9elkd3wrpspf \\\n1000000000000000000000atestfet \\\n--gas auto --gas-adjustment 1.5 --gas-prices 1000000000atestfet \\\n--from test-key\n
"},{"location":"native_and_erc20/how_to_convert_fet/","title":"How to convert","text":"

On this page, you can find instructions on how to convert ERC-20 and native FETs to each other.

"},{"location":"native_and_erc20/how_to_convert_fet/#native-to-erc-20-fet","title":"Native to ERC-20 FET","text":"
  1. Ensure you have the Fetch wallet installed and that the native FETs you want to convert are in this wallet.

  2. Ensure you have the Metamask wallet installed and that your Ethereum account is set up on it. This is where the ERC-20 FETs will go to.

  3. Go to the Token Bridge.

  4. Unlock the Metamask wallet if instructed to do so.

  5. Ensure the Native to ERC-20 tab is selected.

  6. Hit Connect Browser Wallet and allow the token bridge to connect to your Fetch wallet.

  7. The fields will be automatically populated with the addresses on the two wallets. Ensure these are correct.

  8. Under Amount, enter the amount of tokens you wish to convert.

  9. Hit Transfer.

"},{"location":"native_and_erc20/how_to_convert_fet/#erc-20-to-native-fet","title":"ERC-20 to Native FET","text":"
  1. Ensure you have the Metamask wallet installed and that the ERC-20 FETs you want to convert are in this wallet.

  2. Ensure you have the Fetch wallet installed and that your Fetch.ai account is set up on it. This is where the native FETs will go to.

  3. Go to the Token Bridge.

  4. Unlock the Metamask wallet if instructed to do so.

  5. Ensure the ERC-20 to Native tab is selected.

  6. The Ethereum address should be automatically filled in with the address of the active account on your Metamask wallet. Ensure that it is correct.

  7. Insert your Fetch wallet address in the Native Address section.

  8. Under Amount, enter the amount of tokens you wish to convert.

  9. Hit Transfer and allow the Token Bridge to connect with your wallet.

"},{"location":"native_and_erc20/native_and_erc20_fet/","title":"Native and ERC-20 FET","text":"

The Fetch.ai token (FET) is a utility token and the key medium of exchange on the Fetch.ai network. FET can be used to pay for services in the Fetch ecosystem and network transaction fees. Users can also choose to stake FET to participate in securing the network via its Proof-of-Stake (PoS) consensus mechanism and earn rewards in return for contributing to validator nodes.

The Fetch.ai team initially developed the FET utility token on an ERC-20 contract on the Ethereum network while finishing the work on the Fetch.ai native main network (main-net). This helped developers across the world get their hands on the FET tokens earlier and kickstart the process of developing innovative solutions within the Fetch.ai ecosystem. FET, as an ERC-20 token on the Ethereum contract has never been Fetch.ai's end game because the Ethereum chain does not offer the degree of scalability needed by the kinds of applications the fetch.ai ecosystem aims for.

With the launch of the Fetch.ai Main-net, the native FET became available. This meant that users operating on the Fetch.ai network no longer needed to hold any token (e.g. ETH or BTC) associated with any other network. It is only the native FET tokens that fuel the Fetch.ai ecosystem and its applications.

ERC-20 FET tokens are still in circulation and currently co-exist with native FET tokens and can be transferred from one to the other easily on the Fetch.ai network. All of the ERC-20 FET tokens will ultimately become native FET tokens, but in the foreseeable future both will live side by side. FET tokens can be purchased from different centralized or decentralized exchanges. However, certain exchanges may buy or sell one type or the other.

"},{"location":"native_and_erc20/reconciliation/","title":"Reconciliation service","text":"

During the stake migration from ETH to the Fetch Main-net on September 14th, 2021, the staked funds were all migrated from the Ethereum staking contract to the Fetch.ai Main-net. However, some users decided to wait for reconciliation to access their funds after the auto-migration was complete.

The reconciliation service has been built to restore access of the migrated funds for these users.

Warning

The reconciliation service is specifically for those users who opted to not use the guides to gain access to their staked funds during the stake migration. If you are unable to access your funds for any other reason, for example you were hacked, then the reconciliation service is not applicable to you.

"},{"location":"native_and_erc20/reconciliation/#instructions","title":"Instructions","text":"

First, make sure the following conditions are satisfied:

  • Your Fetch wallet is set up and you have access to it.

    Info

    The address of the account on your Fetch wallet must have recorded at least one transaction on the network. If you have never made a transaction using this address before, the reconciliation tool and the network have no way of knowing your wallet exists and will not allow you to complete the final submission.

  • You have at least 1 Native FET token in your wallet to pay the transaction fee associated with the reconciliation tool. You can buy FET from centralized or decentralized exchanges.

  • You have the Metamask wallet set up and configured with the address you used in the original staking platform.

If the above conditions are satisfied, head over to the reconciliation service and follow the instructions.

Info

The reconciliation service is activated at certain times so users can register with it. For economic safety, the fund transfers only happen during network upgrades.

If the above conditions are satisfied, head over to the reconciliation service and follow the instructions.

Once your registered transaction is successful and your reconciliation request is deemed valid, the funds will be automatically released to the main-net address on your Fetch wallet.

"},{"location":"soef/simple-oef-usage/","title":"SOEF Connection","text":"

You can use the SOEF in the agent framework by using the SOEF connection as a package in your agent project.

Note

Please consult the relevant guide for details.

"},{"location":"soef/simple-oef/","title":"Simple-OEF: Agent Search and Discovery","text":"

This documentation has been produced for the Simple-OEF version 0.3.4.

"},{"location":"soef/simple-oef/#concepts","title":"Concepts","text":"

The Simple-OEF, or soef, is a search and discovery mechanism for autonomous economic agents. Agents register with the soef and are then able to conduct searches around them to find other agents that may be able to help. It is a relatively simple implementation focussing on functionality, performance and ease-of-use. As it develops, it will evolve into a full-scale decentralised, multi-dimensional digital world.

The work-flow is:

  • Find relevant agents on the soef,
  • Communicate using the Agent Framework's peer-to-peer network,
  • Negotiate and then transact on the ledger in order to exchange value for tokens

When an agent registers with the soef, it is issued with a unique reference which is quoted in all subsequent transactions. This way, the soef knows who its talking to. The soef is transaction based, so it does not need a permanent connection to be maintained in order to work with it. If it does not hear from an agent for a period of time, that agent will be timed out and automatically unregistered. This period of time is typically about one hour, but you can see the soef's configuration at:

https://s-oef.fetch.ai:443\" target=\"_blank\">https://s-oef.fetch.ai:443

Agents identify themselves in a number of ways. These include their address, their given name, their classification and their genus. They can also describe how they \"look\" in other ways, and specify the services that they provide.

In order to register, agents must provide a valid address and a given name. The address can be for the Fetch.ai native ledger, the Fetch.ai Cosmos ledger or the Ethereum ledger. It is this that uniquely identifies them, and addresses cannot be duplicated or shared. The given name can be anything and it is not used for search filtering. Typically, it can be thought of as a debugging aid or a context. Names could be Alice, Bob or Jim, as well as they could be a flight number, train identity or reference code. They appear in find results, but are not used to find by.

"},{"location":"soef/simple-oef/#describing-an-agent","title":"Describing an Agent","text":"

Agents describe themselves in three ways:

  1. Identity: their address and ledger type along with their given name
  2. Personality Pieces: how they look
  3. Service Keys: what they do, sell or want.

We cover all of these in this next section. It's important to understand the difference between personality pieces and service keys, as agents only have one appearance, but they can provide many services. Search results can be filtered by a number of both, and wildcards are permitted where relevant.

"},{"location":"soef/simple-oef/#personality-pieces","title":"Personality Pieces","text":"

Agents can have a number of personality pieces. These describe how an agent appears, where it is, and other properties such as heading, supported protocols and types of transactions. All personality pieces are optional, but the more you set, the easier it is for searchers to narrow you down accurately.

Piece Description genus Coarse type of agent, includes things such as vehicle, building, iot. See the genus table below. classification An agent's classification, typically in the form mobility.railway.train. See note below on classifications. No fixed classifications are specified. Classifications can contain alphanumeric characters, the period, underscore and colon (_.:). architecture Agent's architecture. See the architecture table below. Introduced in version 0.1.20. The vast majority of agents should set this to agentframework. dynamics.moving Boolean, indicates if the agent is moving or not. dynamics.heading Indicates the heading of the agent, in radians, with 0.0 pointing due north. dynamics.altitude Altitude of the agent in metres from MSL dynamics.position Indicates the GPS co-ordinates of the agent as latitude and longitude. action.buyer Boolean, indicates whether the agent wishes to buy information, i.e., is an agent that requires value from another agent. action.seller Boolean, indicates whether the agent sells information, i.e., provides value. Value provided can be zero-cost."},{"location":"soef/simple-oef/#genus-list","title":"Genus List","text":"

A genus is a coarse agent class. It is the roughest description of what an agent is, and an easy way of filtering large groups of agents out of searches. The supported genus list is:

Name Description test Agent is a test agent, and should be generally ignored. vehicle Moving objects such as trains, planes and automobiles avatar An agent that represents a human being service An agent that provides a service iot An agent that represents an Internet of Things device data An agent that represents data furniture Small fixed location items such as signs, mobile masts building Large fixed location item such as house, railway station, school buyer Indicates the agent is a buyer only and does not have value to deliver viewer The agent is a view in the world, acting as a \"camera\" to view content financial Financial agent: service, exchange, autonomous market maker, etc.

The best way to use genus is to pick the best fit choice. If there isn't one for you, then do not specify it. If you feel that a high-level genus is missing, please make the suggestion in our Developer Discord (see here).

"},{"location":"soef/simple-oef/#architectures","title":"Architectures","text":"

An architecture is a clue to other agents to describe how the agent is built. The vast majority of agents will be built using the Fetch Agent Framework, but in some cases, such as light-weight IoT devices or test/debugging, agents are built otherwise. Architecture offers a way of describing or filtering, as agents with a similar architecture are more likely to be able to communicate with each other in a meaningful way.

Architecture Description custom Custom agent architecture agentframework Built using the Fetch Agent Framework"},{"location":"soef/simple-oef/#a-note-on-classifications","title":"A Note on Classifications","text":"

There is currently no fixed set of guidelines as to how classifications are used. It is expected that agent builders will converge on a set of standards, and as those become clearer, they will be documented as \"by convention\" classification uses. Here are some examples of classifications in use:

mobility.railway.station\nmobility.railway.train\nmobility.road.taxi\ninfrastructure.road.sign\n

When filtering by classifications, the * wildcard can be used to, for example, capture all mobility related agents with a wildcard of mobility.*.

"},{"location":"soef/simple-oef/#service-keys","title":"Service Keys","text":"

Agents can have a number of service keys. Service keys are simple key/value pairs that describe the list of services that the agent provides. Whilst personality pieces can be thought of as how an agent looks, service keys are what an agent has or does. Service keys are user defined and as with personality pieces, currently have no convention for formatting. They are at the agent builder's discretion. As this changes, the documentation will be updated. However, for buyer agents, three suggested keys are:

buying_genus\nbuying_architecture\nbuying_classifications\ndata_type\nsi_unit\n

This allows searches to look for potential buyers of classifications, genus or with a compatible architecture.

"},{"location":"soef/simple-oef/#finding-agents","title":"Finding Agents","text":"

The soef is designed for geographic searches where agents are able to find other agents near to them that are able to provide them with the value that they want, or who might wish to have the value they provide. However, it also allows for positionless searches on a single node. Future versions of the soef will support searches across nodes, and dimensional reduction-based fuzzy searches.

Geographic searches are performed using the find_around_me operation. This allows searches that:

  • Are within a certain range in KM
  • Optionally must be positioned within an angle of a heading
  • That have a specified set of personality pieces (with wildcards where applicable)
  • That have a specified set of service keys (with wildcards)
  • Where chain identifiers match

Positionless searches are performed using the find_on_this_node operation. This allows searches that:

  • That have a specified set of personality pieces (with wildcards where applicable)
  • That have a specified set of service keys (with wildcards)
  • Where chain identifiers match

At least one filter must be supplied in positionless searches. Positionless searches are not boundless, they are capped at a specific number. The tighter the filters, the less likely that you will be capped.

Some limits apply to the maximum number of filters, range and returned results. This may vary from soef instance to soef instance. You can see (and parse if required) these by getting the soef status at:

https://s-oef.fetch.ai:443\" target=\"_blank\">https://s-oef.fetch.ai:443

The soef returns XML that includes information about all found agents. An example of that, unparsed, looks like this:

<response>\n<success>1</success>\n<total>1</total>\n<capped>0</capped>\n<results>\n<agent name=\"TrainNumber1234\" genus=\"vehicle\" classification=\"mobility.railway.train\" user_context=\"18:00 to Berlin\">\n<identities>\n<identity chain_identifier=\"fetchai\">2h6fi8oCkMz9GCpL7EUYMHjzgdRFGmDP5V4Ls97jZpzjg523yY</identity>\n</identities>\n<range_in_km>55.7363</range_in_km>\n<location accuracy=\"3\">\n<latitude>52.5</latitude>\n<longitude>0.2</longitude>\n</location>\n</agent>\n</results>\n</response>\n

The <location> block is only returned if the agent has set itself to disclose its position in a find. Likewise, the user_context=\"\" is only returned if enabled. Normally, the default is not to, and agents will then only return the <range_in_km> item. This is because agents may deliver their precise location as part of the value that they deliver, and therefore it would need to be negotiated and potentially paid for. However, sometimes, it is desirable for agents to always deliver their position when found but specify the accuracy. Because of this, the soef supports four levels of accuracy:

Level Accuracy none Default do not disclose position, range only. low Rounded to nearest 11km medium Rounded to nearest 1.1km high Rounded to nearest 110 metres maximum No rounding: supplied in maximum available detail"},{"location":"soef/simple-oef/#technical-details","title":"Technical Details","text":"

For the majority of use cases, the soef will be used from the Agent Framework. As a result, talking to it directly will not be needed. There are some occasions where interacting with the soef directly may be required, and this section documents the API functionality.

Until version 1.0 and main-net version 2 (expected in early 2021), some of the security and paid-for-services are not implemented and where they are, they generally not enforced. Digital signatures for the sign-on process and unique identity recovery will be implemented, as will encryption on sensitive data transport, for example. Thus the API is likely to change substantially in the coming months, particularly the initial registration process. It is not recommended that you invest in substantial code that talks to the soef directly until after 1.0, and it is always preferred to go through the Agent Framework.

"},{"location":"soef/simple-oef/#registration","title":"Registration","text":"

Agents register at the /register page on the soef. They are expected to provide four pieces of information:

  1. An API key
  2. A chain identifier, which can be either fetchai_v1 for the Fetch native network (testnet or mainnet), fetchai_v2_* for the Fetch version 2 network or ethereum for the Ethereum network. See the \"Chain identifiers\" table below for a complete list of supported chain identifiers.
  3. An address, which must be a valid address for the specified chain identifier
  4. A \"given name\" (see \"Concepts\", above), which can be anything from Alice to Bob, or a flight number, or any other user-given context. It must not exceed 128 characters.

If registration is successful, the soef will return a result like this:

<response>\n<encrypted>0</encrypted>\n<token>0A709D1ED170A3E96C4AC9D014BCAE30</token>\n<page_address>\noef_AEC97453A80FFFF5F11E612594585F611D1728FFCD74BBF4FE915BBBB052\n  </page_address>\n</response>\n

This indicates success and that the agent is now in the Lobby. The lobby is a temporary holding pen where newly registered agents wait until the negotiation is complete. If an agent does not respond and complete its registration within 60 seconds, it is removed from the lobby and registration is cancelled.

The <page_address> is the unique URL for the new agent. This must be quoted in all subsequent interactions and is how the soef identifies that specific agent. To complete registration, use the unique URL and specify the parameters:

  • token= with the token that was returned above and
  • command=acknowledge

If this works, you will receive a success response:

<response>\n<success>1</success>\n</response>\n

At this point, your agent is now fully registered and can then communicate with the soef.

Agents that do not contact the soef at least once over a specified interval will be automatically unregistered. The typical setting for this is 60 minutes.

"},{"location":"soef/simple-oef/#chain-identifiers","title":"Chain Identifiers","text":"

The soef supports a selection of chain identifiers designed to allow agents to distinguish networks in searches, but also to identify the type of address used for verification purposes.

Chain identifier Network fetchai_v1 Version 1 Fetch.ai network (testnet or mainnet). Versions prior to 0.2 of the soef used fetchai for this, which is retained for compatibility. fetchai_v2_testnet_stable Version 2 Fetch.ai stable testnet, also known as \"Agentland\". Versions prior to 0.2 of the soef used fetchai_cosmos which is retained for compatibility, but deprecated. fetchai_v2_testnet_incentivised Current incentivised testnet. Fetch.ai are running a high-reward sequence of testnets in Q4 2020 and Q1 2021 leading to V2 mainnet. fetchai_v2_misc Miscellaneous v2 network. These are temporary or transient testnets where there is a desire to separate the chain ID from other v2 networks. fetchai_v2_mainnet Fetch.ai v2 mainnet. Not yet active."},{"location":"soef/simple-oef/#commands","title":"Commands","text":"

The soef has a number of commands that can be used to set or update personality pieces, manage service keys, unregister, find other agents and other operations. These commands are specified using the agent's unique URL and a command= parameter. There may then be other required and optional parameters for that particular command.

Command Details unregister Unregisters the agent from the soef. The unique URL is invalidated and the agent will no longer appear in searches. No parameters. ping Say hello. This is for agents that have been idle for a long period of time and wish to maintain their connection. No parameters. set_personality_piece Sets or updates a personality piece. Specify the piece (see personality piece table above) and the value. For personality pieces with multiple values, such as dynamics.position, separate them with the pipe character |. set_service_key Sets or updates a service key. Specify the key and the value to assign to it. remove_service_key Removes an existing service key. Specify the key. set_find_position_disclosure_accuracy Sets the find disclosure accuracy. See the table in \"Finding Agents\", above, for the accepted values for the parameter accuracy. find_around_me Geographic finding of agents around me. This allows various filters, such as personality pieces and service keys, to be specified. See below, as this is more complex. find_on_this_node Positionless finding of agents on this node. Various filters such as personality pieces and service keys can narrow the search. See below for more information. set_position This is a direct internal mapping to set_personality_piece with a piece of dynamics.position. It existed in the earliest versions of the soef and remains as a short-cut. It expects longitude and latitude as parameters. set_declared_name This allows an agent's declared name to be changed after registration. It takes one parameter, name, to specify the replacement name. Names cannot exceed 128 characters and must not contain illegal characters. set_user_context Sets an optional user-context for an agent to what is specified in the value parameter. This can be optionally disclosed in find_around_me if enabled. See set_disclose_user_context, below. The user context must not contain illegal characters and is limited to 160 maximum. set_disclose_user_context If the disclose parameter is set to true, the optional user context is disclosed if it has been set. Default is false."},{"location":"soef/simple-oef/#find-commands-in-detail","title":"Find Commands in Detail","text":"

find_around_me and find_on_this_node are the big commands. Ultimately, they will cost a small amount of tokens to use, depending on the size of the request, as it involves the most computing time. This provides an incentive for soef operators to maintain soef nodes that correspond to subject areas, geographic areas or both. The command has a number of parameters specifying the filtering required. For find_around_me, the range_in_km is required, whereas narrowing down agents to be within a certain angle of a direction is optional. This cannot exceed a certain range, typically between 50 and 75km. This, and other configuration items, are available on the soef's configuration page. There are other parameters that are optional, although for find_on_this_node at least one ppfilter or skfilter must be specified. The parameters are:

Parameter Use range_in_km Range in kilometres to include agents in results. of_heading Optional: if a pizza-slice type search is required, this is the direction, in degrees, with 0.0 being north. within Optional: if a pizza-slice search, this is the angle in degrees from the of_heading that is allowed. If either of_heading or within are specified, both must be specified. Example: of_heading set to 90.0 and within set to 30 would exclude any agents that are not within 30 degrees of direct east of the me agent. chains_must_match Boolean. Must be true or false. Default is false. If specified, this ensures that any agents returned in the search will have the same chain identifier as you. ppfilter Specify a personality piece filter. Multiple ppfilters can be specified. Example use is: ppfilter=dynamics.moving,true. Wildcards can be used where relevant, e.g.: ppfilter=classification,mobility* will match all classifications that start with mobility, whereas ppfilter=classification,*mobility* will match all classifications with mobility anywhere in it. skfilter Specify a service key filter. Multiple skfilters can be specified. Example use is: skfilter=fruit,peach which will require any returned results to have a service key of fruit and a value of peach. Wildcards can be specified, so skfilter=fruit,pea* will match any agent with a service key of fruit that starts pea, so pear and peach would match."},{"location":"soef/simple-oef/#sk-filters-filter-modes","title":"SK Filters: filter modes","text":"

The skfilter parameter for find_around_me also supports a mode. Four modes are supported:

Mode string Description PS Key must be present, and success is required PF Key must be present, and failure is required OS Only match if present, and success is required OF Only match if present, and failure is required

For example:

command=find_around_me&range_in_km=50&skfilter=type,fruit,PS&skfilter=size,large,OF\n

In this example, the key type must be present, and it must match to fruit. If the size key is present, and it is set to large, then do not match. I.e., return everything that's a fruit within 50km except where the size is large.

"},{"location":"soef/simple-oef/#further-information","title":"Further Information","text":"

You can find further information, or talk to us, in the #agents channel on our official developer Discord server, which you can access here.

We welcome your feedback and strive to deliver the best decentralised search and discovery service for agents that is possible. There are many upcoming features, including the operation incentive mechanisms, additional security and encryption, active searches (where results happen without find_around_me being issued), non-geographic searches across one and many soef nodes and dimensional-reduction based approximate searches.

[Docs: issue 15, 0.3.4, 28-Dec-2020, TWS]

"},{"location":"uAgents/","title":"Introduction","text":"

The \u03bcAgents (micro-Agents) project is a fast and lightweight framework that makes it easy to build agents for all kinds of decentralised use cases.

"},{"location":"uAgents/#why-use-agents","title":"Why use \u03bcAgents?","text":"

Here are a few of the reasons to build with \u03bcAgents. They are:

  • easy to learn: follow our quick start guides to install the Python package and create an agent in just a few minutes.
  • customizable: create any type of agent you can think of and put into code.
  • connected: on startup, each agent automatically joins the fast growing network of \u03bcAgents by registering on the Almanac, a smart contract deployed on the Fetch.ai blockchain.
  • secure: \u03bcAgent messages and wallets are cryptographically secured, so their identities and assets are protected.
  • platform and language independent: though initially launched as a Python library, the exchange protocol is defined in terms of standard data types, and since it is a lightweight framework, expect packages to appear in other languages very soon.
"},{"location":"uAgents/addresses/","title":"Agent addresses","text":"

You can print your agent's addresses in the following way:

from uagents import Agent\nalice = Agent(name=\"alice\")\nprint(\"uAgent address: \", alice.address)\nprint(\"Fetch network address: \", alice.wallet.address())\n

Your agent will have two types of addresses:

  • uAgent address: represents the main \u03bcAgent identifier. Other \u03bcAgents can use this to query the agent's information in the Almanac contract.

  • Fetch address: provides the agent with the capabilities for interacting with the Fetch ledger such as registering in the Almanac contract.

"},{"location":"uAgents/agent-protocols/","title":"Agent protocols","text":"

The \u03bcAgents framework supports capturing related message types and handlers in protocols. Agents who include the same protocol will be able to communicate with each other.

A protocol is built similar to an agent, but it has no identity and cannot be run. It contains only the message types and handlers that define some component of agent functionality.

To show how this works, we will use a simple restaurant table booking request as an example. We first need to define the type of messages that the handler will receive and send. Here we define BookTableRequest which will contain the requested table number and BookTableResponse which will inform the user if that table is available.

from uagents import Context, Model, Protocol\nclass BookTableRequest(Model):\ntable_number: int\nclass BookTableResponse(Model):\nsuccess: bool\n

Now we define the booking protocol as book_proto and we define the desired logic to determine if the BookTableResponse will be successful or not.

book_proto = Protocol()\n@book_proto.on_message(model=BookTableRequest, replies={BookTableResponse})\nasync def handle_book_request(ctx: Context, sender: str, msg: BookTableRequest):\nif ctx.storage.has(str(msg.table_number)):\nsuccess = False\nelse:\nsuccess = True\nctx.storage.set(str(msg.table_number), sender)\n# send the response\nawait ctx.send(sender, BookTableResponse(success=success))\n

We will create a folder named protocols and save this file in it as book.py. We can then import it from the agent script:

from protocols.book import book_proto\n

Then, if your agent is called restaurant you can include the protocol in this way:

restaurant.include(book_proto)\n
"},{"location":"uAgents/almanac-endpoint/","title":"Endpoint weighting","text":"

When an agent registers in the almanac contract, it must specify the service endpoints that they provide along with a weight parameter for each endpoint. Then, when any agent tries to communicate with your agent, the service endpoint will be chosen using a weighted random selection.

You will have two format options when defining your agent's endpoints:

"},{"location":"uAgents/almanac-endpoint/#list-format","title":"List format","text":"

Define your agent's endpoints as a list of strings, the weights will be automatically assigned a value of 1.

agent = Agent(\nname=\"alice\",\nport=8000,\nseed=\"agent secret phrase\",\nendpoint=[\"http://127.0.0.1:8000/submit\",\"http://127.0.0.1:8001/submit\"]\n)\n
"},{"location":"uAgents/almanac-endpoint/#dict-format","title":"Dict format","text":"

Define your agent's endpoints in a Dict format specifying the weight for each endpoint, if the weight parameter is not specified, it will be assigned a value of 1.

agent = Agent(\nname=\"alice\",\nport=8000,\nseed=\"agent recovery seed phrase\",\nendpoint={\n\"http://127.0.0.1:8000/submit\": {\"weight\": 2},\n\"http://127.0.0.1:8001/submit\": {}, # weight value = 1\n},\n)\n
"},{"location":"uAgents/almanac-overview/","title":"Almanac Contract","text":"

Agents of the system will be registered in the Almanac contract. Users can query a particular agent's information directly from the contract to communicate.

A central part of the system is that registrations are strictly time (in blocks) limited. This primarily helps with the liveness problem when dealing with a large ecosystem of agents. In order to keep the registration information up to date, the agent will need to intermittently re-register their information with the Almanac contract.

When an agent\u2019s information times out, queries for that agent will no longer return the registered information.

With each registration, the agent will need to prove ownership of the agent address by signing a sequence number with their \u03bcAgent private key and submitting the signature for verification on the contract. This sequence number should increment with each successful registration and should also be queryable. This will be performed automatically.

"},{"location":"uAgents/almanac-registration/","title":"Registration","text":"

Agent registration in the almanac-contract is a key part for remote agents communication.

To be found by other \u03bcAgents, each \u03bcAgent needs to register (paying a small fee) in the almanac contract using their agent address. Therefore, your agents need to have funds available in their Fetch address. When using the testnet, you can use the function fund_agent_if_low to fund your agent:

from uagents.setup import fund_agent_if_low\nfrom uagents import Agent\nagent = Agent(name=\"alice\", seed=\"agent1 secret phrase\")\nfund_agent_if_low(agent.wallet.address())\n
This function will check if you have enough tokens to register in the almanac-contract. If not it will add tokens to your Fetch address. Make sure to add a seed to your agent so you don't have to fund different addresses each time you run your agent.

\u03bcAgents can communicate by querying the almanac-contract and retrieving an HTTP endpoint from the recipient \u03bcAgent. Therefore, we need to specify the service endpoints when defining an agent:

agent = Agent(\nname=\"alice\",\nport=8000,\nseed=\"agent1 secret phrase\",\nendpoint=[\"http://127.0.0.1:8000/submit\"],\n)\n

Here we defined a local http address, but you could also define a remote address to allow agent communication over different machines through the internet.

"},{"location":"uAgents/booking-demo/","title":"Restaurant Booking Demo","text":"

To showcase how easy it is to create \u03bcAgents for a particular application, here is an example of how to create a custom message type using the Model class.

from uagents import Model\nclass TableStatus(Model):\nseats: int\ntime_start: int\ntime_end: int\n

For this example, we will create a restaurant booking service with two agents: a restaurant with tables available and a user requesting table availability.

"},{"location":"uAgents/booking-demo/#restaurant-setup","title":"Restaurant Setup","text":"

We can create a restaurant agent with its corresponding http endpoint. We will also make sure that the agent is funded so it is able to register in the Almanac contract.

from uagents import Agent\nfrom uagents.setup import fund_agent_if_low\nrestaurant = Agent(\nname=\"restaurant\",\nport=8001,\nseed=\"restaurant secret phrase\",\nendpoint=[\"http://127.0.0.1:8001/submit\"],\n)\nfund_agent_if_low(restaurant.wallet.address())\n
The protocols query_proto and book_proto are built from message handlers in the same way as agents. See query protocol and book protocol for the details and logic behind these protocols, but for now we will simply import them. You will need to add these files inside a protocols folder in the same directory you are running your agent. See agent protocols for more information. Next we build the restaurant agent from these protocols and set the table availability information.

from protocols.book import book_proto\nfrom protocols.query import query_proto, TableStatus\n# build the restaurant agent from stock protocols\nrestaurant.include(query_proto)\nrestaurant.include(book_proto)\nTABLES = {\n1: TableStatus(seats=2, time_start=16, time_end=22),\n2: TableStatus(seats=4, time_start=19, time_end=21),\n3: TableStatus(seats=4, time_start=17, time_end=19),\n}\n
"},{"location":"uAgents/booking-demo/#storage","title":"Storage","text":"

We will now store the TABLES information in the restaurant agent and run it.

# set the table availability information in the restaurant protocols\nfor (number, status) in TABLES.items():\nrestaurant._storage.set(number, status.dict())\nif __name__ == \"__main__\":\nrestaurant.run()\n
The restaurant agent is now online and listing for messages.

"},{"location":"uAgents/booking-demo/#user-setup","title":"User Setup","text":"

We will first import the needed objects and protocols. We will also need the restaurant agent's address to be able to communicate with it.

from protocols.book import BookTableRequest, BookTableResponse\nfrom protocols.query import (\nQueryTableRequest,\nQueryTableResponse,\n)\nfrom uagents import Agent, Context\nfrom uagents.setup import fund_agent_if_low\nRESTAURANT_ADDRESS = \"agent1qw50wcs4nd723ya9j8mwxglnhs2kzzhh0et0yl34vr75hualsyqvqdzl990\"\nuser = Agent(\nname=\"user\",\nport=8000,\nseed=\"user secret phrase\",\nendpoint=[\"http://127.0.0.1:8000/submit\"],\n)\nfund_agent_if_low(user.wallet.address())\n

Now we create the table query to generate the QueryTableRequest using the restaurant address. If the request has not been completed before, we send the request to the restaurant agent.

table_query = QueryTableRequest(\nguests=3,\ntime_start=19,\nduration=2,\n)\n# This on_interval agent function performs a request on a defined period\n@user.on_interval(period=3.0, messages=QueryTableRequest)\nasync def interval(ctx: Context):\ncompleted = ctx.storage.get(\"completed\")\nif not completed:\nawait ctx.send(RESTAURANT_ADDRESS, table_query)\n

The function below activates when a message is received back from the restaurant agent. handle_query_response will evaluate if there is a table available, and if so, respond with a BookTableRequest to complete the reservation.

@user.on_message(QueryTableResponse, replies={BookTableRequest})\nasync def handle_query_response(ctx: Context, sender: str, msg: QueryTableResponse):\nif len(msg.tables) > 0:\nctx.logger.info(\"There is a free table, attempting to book one now\")\ntable_number = msg.tables[0]\nrequest = BookTableRequest(\ntable_number=table_number,\ntime_start=table_query.time_start,\nduration=table_query.duration,\n)\nawait ctx.send(sender, request)\nelse:\nctx.logger.info(\"No free tables - nothing more to do\")\nctx.storage.set(\"completed\", True)\n

Then, handle_book_response will handle messages from the restaurant agent on whether the reservation was successful or unsuccessful.

@user.on_message(BookTableResponse, replies=set())\nasync def handle_book_response(ctx: Context, _sender: str, msg: BookTableResponse):\nif msg.success:\nctx.logger.info(\"Table reservation was successful\")\nelse:\nctx.logger.info(\"Table reservation was UNSUCCESSFUL\")\nctx.storage.set(\"completed\", True)\nif __name__ == \"__main__\":\nuser.run()\n

Finally, run the restaurant agent and then the user agent from different terminals.

Run restaurant agent from one terminal

python restaurant.py\n

Run user agent from a second terminal

python user.py\n

You should see this printed on the user terminal:

INFO:root:Adding funds to agent...complete INFO:root:Registering Agent user... INFO:root:Registering Agent user...complete. Wallet address: fetchnfu3hd87323mw484ma3v3nz2v0q6uhds7d There is a free table, attempting to book one now Table reservation was successful

See the full example scripts at restaurant and user.

"},{"location":"uAgents/installation/","title":"Installation","text":""},{"location":"uAgents/installation/#system-requirements","title":"System requirements","text":"

The system requirements for the Python \u03bcAgents package are as follows, but libraries for more platforms and languages will be released soon.

System requirements

The Python \u03bcAgents pacakge runs on Ubuntu/Debian, MacOS, and Windows.

You need Python 3.8, 3.9 or 3.10 on your system.

"},{"location":"uAgents/installation/#install-from-pypi","title":"Install from PyPI","text":"

We recommend first creating a clean Python virtual environment, for example using poetry or pipenv.

poetrypipenv

Create and enter a new poetry virtual environment:

poetry init -n && poetry shell\n

Create and enter a new pipenv environment:

pipenv --python 3.10 && pipenv shell\n

Now install \u03bcAgents from the PyPI package registry:

pip install uagents\n

Alternatively, install from source code:

Download the latest released version from Github and navigate to the uAgents directory

git clone https://github.com/fetchai/uAgents.git\ncd uAgents\n

Install the required dependencies

poetry install\n

Open the virtual environment

poetry shell\n
"},{"location":"uAgents/interval-tasks/","title":"Interval tasks","text":"

\u03bcAgents can use interval tasks to periodically perform actions with some time interval.

We can use the on_interval decorator to repeat a task in a specified period. We also need to import Context to have access to the information that the agent needs to function. In this case, we will just define a say_hello function that will print out the agent name every 2 seconds.

from uagents import Agent, Context\nalice = Agent(name=\"alice\")\n@alice.on_interval(period=2.0)\nasync def say_hello(ctx: Context):\nctx.logger.info(f'hello, my name is {ctx.name}')\nif __name__ == \"__main__\":\nalice.run()\n

Run your agent

python agent.py\n

You should see the message printed out in the terminal every 2 seconds.

Hello my name is alice. Hello my name is alice. Hello my name is alice. Hello my name is alice.

For another interval task example see agent communication.

"},{"location":"uAgents/protocol/","title":"Exchange protocol","text":""},{"location":"uAgents/protocol/#overview","title":"Overview","text":"

The \u03bcAgents Exchange Protocol defines a simple standard by which the agents communicate.

In this protocol, agents can send messages enclosed in envelopes, which are then encoded and sent via HTTP to the endpoints of other agents.

We break down each of these concepts in more detail below.

"},{"location":"uAgents/protocol/#messages","title":"Messages","text":"

Messages consist of key-value pairs following the standard JSON format.

Here are a few examples:

{\"message\": \"hello\"}\n
{\"name\": \"alice\", \"age\": 26, \"languages\": [\"English\", \"Japanese\", \"Arabic\"]}\n
{\"item\": \"pretzel\", \"bid\": {\"amount\": 120, \"denomination\": \"GBP\"}}\n

Once created, messages are then enclosed in envelopes containing some important metadata.

"},{"location":"uAgents/protocol/#envelopes","title":"Envelopes","text":"

Envelopes have the following form and are quite similar to blockchain transactions:

@dataclass\nclass Envelope:\nsender: str:    # bech32-encoded public address\ntarget: str:    # bech32-encoded public address\nsession: str    # UUID\nprotocol: str   # protocol digest\npayload: bytes  # JSON type: base64 str\nexpires: int    # Unix timestamp in seconds\nsignature: str  # bech32-encoded signature\n
"},{"location":"uAgents/protocol/#semantics","title":"Semantics","text":"

The sender field exposes the address of the sender of the message.

The target field exposes the address of the recipient of the message.

The protocol contains the unique schema digest string for the message.

The payload field exposes the payload of the protocol. Its JSON representation should be a base64 encoded string.

The expires field contains the Unix timestamp in seconds at which the message is no longer valid.

The signature field contains the signature that is used to authenticate that the message has been sent from the sender agent.

Envelopes are then JSON encoded and sent to endpoints of other agents or services.

"},{"location":"uAgents/protocol/#endpoints","title":"Endpoints","text":"

The protocol supports only one standardised endpoint:

HTTP 1.1 POST /submit

and expects data which is broadly JSON compatible. The protocol currently supports MIME content type application/json.

"},{"location":"uAgents/remote-agents/","title":"Remote agents","text":"

\u03bcAgents can also interact remotely from different locations across the internet. All you need to know is the recipient agent's address to query it's information in the Almanac contract. See Addresses for more information about \u03bcAgent addresses.

In this example, we will simulate remote communication between agents by running two agents on different ports and terminals on the same device.

"},{"location":"uAgents/remote-agents/#alice","title":"Alice","text":"

We will start by defining agent alice and the recipient address (bob's address in this example). Then we will include a send function and a handler as we have learned in agent interactions:

from uagents.setup import fund_agent_if_low\nfrom uagents import Agent, Context, Model\nclass Message(Model):\nmessage: str\nRECIPIENT_ADDRESS = \"agent1q2kxet3vh0scsf0sm7y2erzz33cve6tv5uk63x64upw5g68kr0chkv7hw50\"\nalice = Agent(\nname=\"alice\",\nport=8000,\nseed=\"alice secret phrase\",\nendpoint=[\"http://127.0.0.1:8000/submit\"],\n)\nfund_agent_if_low(alice.wallet.address())\n@alice.on_interval(period=2.0)\nasync def send_message(ctx: Context):\nawait ctx.send(RECIPIENT_ADDRESS, Message(message=\"hello there bob\"))\n@alice.on_message(model=Message)\nasync def message_handler(ctx: Context, sender: str, msg: Message):\nctx.logger.info(f\"Received message from {sender}: {msg.message}\")\nif __name__ == \"__main__\":\nalice.run()\n
"},{"location":"uAgents/remote-agents/#bob","title":"Bob","text":"

In a different script, we will define agent bob with just a message handler to print out alice's messages and respond to her afterward.

from uagents.setup import fund_agent_if_low\nfrom uagents import Agent, Context, Model\nclass Message(Model):\nmessage: str\nbob = Agent(\nname=\"bob\",\nport=8001,\nseed=\"bob secret phrase\",\nendpoint=[\"http://127.0.0.1:8001/submit\"],\n)\nfund_agent_if_low(bob.wallet.address())\n@bob.on_message(model=Message)\nasync def message_handler(ctx: Context, sender: str, msg: Message):\nctx.logger.info(f\"Received message from {sender}: {msg.message}\")\n# send the response\nawait ctx.send(sender, Message(message=\"hello there alice\"))\nif __name__ == \"__main__\":\nbob.run()\n

Now, we first run bob and then alice from different terminals. They will register automatically in the Almanac contract using their funds. The received messages will print out in each terminal.

Run Bob and Alice

python bob.py\npython alice.py\n

In bob's terminal:

INFO:root:Adding funds to agent...complete INFO:root:Registering Agent bob... INFO:root:Registering Agent bob...complete. Wallet address: fetch1cr9ghmxrf943dmw484ma3v3nz2v0q6u9pynqdk [bob] From: agent1qdp9j2ev86k3h5acaayjm8tpx36zv4mjxn05pa2kwesspstzj697xy5vk2a hello there bob [bob] From: agent1qdp9j2ev86k3h5acaayjm8tpx36zv4mjxn05pa2kwesspstzj697xy5vk2a hello there bob [bob] From: agent1qdp9j2ev86k3h5acaayjm8tpx36zv4mjxn05pa2kwesspstzj697xy5vk2a hello there bob

In alice's terminal:

INFO:root:Adding funds to agent...complete INFO:root:Registering Agent alice... INFO:root:Registering Agent alice...complete. Wallet address: fetchnfu3hd87323mw484ma3v3nz2v0q6uhds7d [alice] From: agent5kdyfj2ev86k3h5acaa93kdnch8shv4mjxn05pa2kwesspstzj023jdus93j hello there alice [alice] From: agent5kdyfj2ev86k3h5acaa93kdnch8shv4mjxn05pa2kwesspstzj023jdus93j hello there alice [alice] From: agent5kdyfj2ev86k3h5acaa93kdnch8shv4mjxn05pa2kwesspstzj023jdus93j hello there alice

For a more complex example visit restaurant booking demo.

"},{"location":"uAgents/remote-agents/#the-agentverse-explorer","title":"The Agentverse Explorer","text":"

\u03bcAgents can also interact remotely using a mailbox server. For example, you can use The Agentverse Explorer to find other agents and register your own.

To register agents in the Agentverse mailbox, you need to sign in at The Agentverse Explorer. Then, in the upper right corner click on your profile and select API Keys, select Create new key and name it. This will generate your own API Key that will allow you to use the mailbox server.

Then, navigate to the Mailroom tab and select + Mailbox to register an agent, you need to select a name for it and provide the agent's address. Finally, you need to define the \u03bcAgent specifying the mailbox server and the API Key

# First generate a secure seed phrase (e.g. https://pypi.org/project/mnemonic/)\nSEED_PHRASE = \"put_your_seed_phrase_here\"\n# Copy the address shown below\nprint(f\"Your agent's address is: {Agent(seed=SEED_PHRASE).address}\")\n# Then sign up at https://agentverse.ai to get an API key and register your agent\nAPI_KEY = \"put_your_API_key_here\"\n# Now your agent is ready to join the agentverse!\nagent = Agent(\nname=\"alice\",\nseed=SEED_PHRASE,\nmailbox=f\"{API_KEY}@wss://agentverse.ai\",\n)\n

Now, you can recreate the example we showed at the begining of this section by also registering agent bob in The Agentverse Explorer.

"},{"location":"uAgents/run-agent/","title":"Running an agent","text":""},{"location":"uAgents/run-agent/#create-the-agent","title":"Create the agent","text":"

You can create your first \u03bcAgent by building a Python script with the following steps:

from uagents import Agent, Context\nalice = Agent(name=\"alice\", seed=\"alice recovery phrase\")\n

It is optional but useful to include a seed parameter when creating an agent to set fixed addresses. Otherwise, random addresses will be generated every time you run the agent.

"},{"location":"uAgents/run-agent/#give-the-agent-something-to-do","title":"Give the agent something to do","text":"

Let's start with a simple task of saying hello every 2 seconds:

@alice.on_interval(period=2.0)\nasync def say_hello(ctx: Context):\nctx.logger.info(f'hello, my name is {ctx.name}')\n
The Context object is a collection of data and functions related to the agent. In this case, we just use the agent's name.

"},{"location":"uAgents/run-agent/#run-the-agent","title":"Run the agent","text":"

You can now run your first \u03bcAgent!

from uagents import Agent, Context\nalice = Agent(name=\"alice\", seed=\"alice recovery phrase\")\n@alice.on_interval(period=2.0)\nasync def say_hello(ctx: Context):\nctx.logger.info(f'hello, my name is {ctx.name}')\nif __name__ == \"__main__\":\nalice.run()\n

Run your agent

python agent.py\n

After a few lines in the agent's logs, you should see the following text printed on your terminal:

hello, my name is alice. hello, my name is alice. hello, my name is alice. ..."},{"location":"uAgents/simple-interaction/","title":"Agent interactions","text":""},{"location":"uAgents/simple-interaction/#add-a-second-agent","title":"Add a second agent","text":"

To show \u03bcAgents interacting, we'll need to create a second agent. Importing the Bureau class will allow us to create a collection of agents and run them together in the same script. Then we can simply add agents alice and bob to the Bureau and run it.

from uagents import Agent, Context, Bureau\nalice = Agent(name=\"alice\", seed=\"alice recovery phrase\")\nbob = Agent(name=\"bob\", seed=\"bob recovery phrase\")\n@alice.on_interval(period=2.0)\nasync def say_hello(ctx: Context):\nctx.logger.info(f'Hello, my name is {ctx.name}')\n@bob.on_interval(period=2.0)\nasync def say_hello(ctx: Context):\nctx.logger.info(f'Hello, my name is {ctx.name}')\nbureau = Bureau()\nbureau.add(alice)\nbureau.add(bob)\nif __name__ == \"__main__\":\nbureau.run()\n

Yoy should observe alice and bob printing out their name in the terminal.

Run your agents

python simple-agents.py\n

You will see the message printed out every 2 seconds. You might see a message indicating insufficient funds to register, check out agent registration for more information.

Hello, my name is alice Hello, my name is bob Hello, my name is alice Hello, my name is bob"},{"location":"uAgents/simple-interaction/#agent-communication","title":"Agent communication","text":"

To allow our agents to communicate with each other we will need a message structure, and for that, we need to import Model to define a generic message.

from uagents import Model\nclass Message(Model):\ntext: str\n

We can use the send function from the Context class to send a message from alice to bob on an interval.

@alice.on_interval(period=2.0)\nasync def send_message(ctx: Context):\nmsg = f'hello there {bob.name} my name is {alice.name}'\nawait ctx.send(bob.address, Message(text=msg))\n

We also need to introduce a message handler for bob. We will do this inside the on_message decorator that will activate the message_handler once bob receives the message.

@bob.on_message(Message)\nasync def message_handler(ctx: Context, sender: str, msg: Message):\nctx.logger.info(f\"Received message from {sender}: {msg.text}\")\nctx.logger.info(msg)\n

Finally, we need to add both agents to the Bureau in order to run them from the same script.

from uagents import Agent, Context, Bureau, Model\nclass Message(Model):\ntext: str\nalice = Agent(name=\"alice\", seed=\"alice recovery phrase\")\nbob = Agent(name=\"bob\", seed=\"bob recovery phrase\")\n@alice.on_interval(period=2.0)\nasync def send_message(ctx: Context):\nmsg = f'hello there {bob.name} my name is {alice.name}'\nawait ctx.send(bob.address, Message(text=msg))\n@bob.on_message(model=Message)\nasync def message_handler(ctx: Context, sender: str, msg: Message):\nctx.logger.info(f\"Received message from {sender}: {msg.text}\")\nbureau = Bureau()\nbureau.add(alice)\nbureau.add(bob)\nif __name__ == \"__main__\":\nbureau.run()\n

When running the script above, you should see alice's message printed on the terminal:

Run your agents

python agent-communication.py\n
[bob]: message received from agent1qww3ju3h6kfcuqf54gkghvt2pqe8qp97a7nzm2vp8plfxflc0epzcjsv79t: hello there bob my name is alice [bob]: message received from agent1qww3ju3h6kfcuqf54gkghvt2pqe8qp97a7nzm2vp8plfxflc0epzcjsv79t: hello there bob my name is alice

You could also try to add a response from bob to alice, for that you would need to add a send message from bob after alice's message is received and a new message handler for alice to be able to manage and print out bob's message. For a slightly more complex example check out the next section remote agents.

"},{"location":"uAgents/storage/","title":"Storage","text":"

You can store information using the agent's local storage by simply running:

ctx.storage.set(\"key\", \"value\")\n
within a handler, where ctx is the agent's Context object.

This will save the information in a JSON file, you can retreive it a any time using:

 ctx.storage.get(\"key\")\n

See the restaurant booking demo for an example that makes use of the agent's storage to store table information.

"}]} \ No newline at end of file +{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Welcome to Fetch.ai Ecosystem","text":""},{"location":"#what-is-fetchai","title":"What is Fetch.ai?","text":"

Our mission is to build the infrastructure required for developing modern, decentralized and peer-to-peer (P2P) applications that are free from centralized rent-seeking.

We achieve this by providing open-source software tools which you can use together with our interoperable decentralized network, to harness the power of AI and automation, and carry out complex tasks in the digital economy.

"},{"location":"#get-started","title":"Get Started","text":"

The fetch ecosystem is made up of various tools and frameworks that help you build and jump-start your very own decentralized applications. Select one below to dive right in!

User facing tools

AEA Registry AEA Manager Fetch Wallet Explorer

Tools that use the chain

AEAFramework ACN \u03bcAgents

Tools to directly interact with the chain

CosmPy Jenesis

The chain

Ledger Cosmwasm

You can also jump straight into our GitHub repos:

  • Fetch Wallet
  • AEA Framework (& ACN)
  • \u03bcAgents
  • CosmPy
  • Jenesis
  • Ledger (fetchd)
"},{"location":"#bug-reports-feature-requests","title":"Bug Reports & Feature Requests","text":"

If you want to report a bug or request a feature:

  • AEA Framework
  • \u03bcAgents
  • Cosmpy
  • Jenesis
  • Ledger (fetchd)
  • Documentation

Want to report a security vulnerability? Visit our Bug Bounty.

"},{"location":"#questions-general-discussions","title":"Questions & General Discussions","text":"

Visit fetch.ai developer forums:

  • Fetch Wallet
  • AEA Framework
  • \u03bcAgents
  • CosmPy
  • Jenesis
  • Ledger (fetchd)

Chat with Fetch.ai developers on discord.

"},{"location":"bug_bounty/","title":"Bug Bounty Program","text":"

The Fetch bug bounty program provides incentives for developers and security experts to report vulnerabilities in the Fetch ecosystem.

The Fetch team has undertaken risk mitigation measures to limit the potential impacts of bugs or intentional misuse of the software and to ensure that all software has been internally audited and thoroughly tested.

In addition to the implemented safety measures, we are offering a bug bounty for the core components of the Fetch ecosystem as outlined under the Scope section below. The bug bounty program ensures that the software lives up to the highest standards possible and that the risk of users losing funds is at a minimum.

"},{"location":"bug_bounty/#scope","title":"Scope","text":"

The scope of the bounty program extends to:

  • FetchD: https://github.com/fetchai/fetchd
  • Fetch Wallet: https://github.com/fetchai/fetch-wallet
  • The Block Explorer: https://github.com/fetchai/cosmos-explorer
  • Documentation:: https://github.com/fetchai/docs
  • CosmPy: https://github.com/fetchai/cosmpy
  • AEA Framework, including the ACN and packages authored by Fetchai: https://github.com/fetchai/agents-aea and https://pypi.org/project/aea
  • AEA Manager Website: https://github.com/fetchai/agents-manager-app-site
"},{"location":"bug_bounty/#classification","title":"Classification","text":""},{"location":"bug_bounty/#critical-bugs-awards-up-to-20000-fet-tokens","title":"Critical Bugs (awards up to 20,000 FET tokens)","text":"

Critical bugs are those that result in loss of funds or lead to a lack of availability of the network. This may be as a result of vulnerabilities found in the deployed and supported versions of the blockchain client, smart contracts or any of the other software outlined within the Scope section.

"},{"location":"bug_bounty/#non-critical-bugs-awards-up-to-10000-fet-tokens","title":"Non-critical Bugs (awards up to 10,000 FET tokens)","text":"

Non-critical bugs are those that cannot cause loss of funds or any other type of economic loss. These types of bugs affect the experience of developers or users of the network and have a perceived or Fetch suggested workaround.

Awards are issued subject to reclassification and verification by the Fetch team.

"},{"location":"bug_bounty/#how-to-report","title":"How to Report","text":"

Please follow the steps listed below to report your bug:

  • In an email, describe the issue clearly with reference to the underlying source code and indicate whether the bug is Critical or Non-critical.
  • Attach all relevant information that is required to reproduce the bug in a test environment.
  • Include the relevant version information associated with the faulty software of the components along with any other relevant system information such as OS versions.
  • Include suggested solutions and/or mitigations (if known).
  • Send this email to bounty@fetch.ai and start the subject with your classification Critical or Non-critical followed by a short title of the bug.

The Fetch team will review your information and your classification of the bug. After reviewing, one of the Fetch developers will set out to reply within 2 working days to confirm whether the bug meets the requirements of the bug bounty program or to request more time to complete this assessment. The Fetch team will also post updates on the #bugs channel on our Discord server: https://discord.gg/M9XmgyWzup.

For non-critical bugs, the Fetch team will create an issue or a pull request allowing you to follow the progress on the bug fix.

For critical bugs that can result in loss of funds, it is important that the Fetch team has an opportunity to deploy a patched version before the exploit is acknowledged publicly. Hence, critical bugs and their fixes will be shared after the code is patched to prevent the targeting of such exploits.

"},{"location":"bug_bounty/#terms-and-conditions","title":"Terms and Conditions","text":"

These include, but are not limited to:

  • Bounty awards are made at the sole discretion of Fetch.ai and are subject to change and verification.
  • We will make every attempt to respond to all submissions promptly and to provide rewards in a timely manner but do not make any guarantees as to how long the processing of claims will take.
  • All users warrant that they are legally able to receive bounties. More specifically: they are of the appropriate age, the work they are submitting is their own and that they are resident in a territory that allows payment of such rewards.
  • Submitters must be willing to undergo any Know Your Customer (KYC) or Anti-Money Laundering (AML) checks as required.
  • This program is not open to Fetch.ai employees or contractors, past or present.
  • Fetch.ai reserves the right to alter or discontinue the Bug Bounty Program without notice.
"},{"location":"decentralisation/","title":"Decentralization","text":""},{"location":"decentralisation/#what-are-decentralized-systems","title":"What are Decentralized Systems?","text":"

A decentralized system is an interconnected system if no single entity (individual, organization, or group) is the sole authority. In a decentralized system, control and decision-making is in the hands of those participating in the system, and they have (more or less) equal standing in terms of influence. This is in contrast to a centralized system where a single authority is above all others in terms of rights and privileges.

Example

A good example of a decentralized system is an ant colony, where control is distributed among the colony's members. Without any form of central control, an ant colony successfully achieves its goals, performs all its required tasks, and effectively responds to the ever changing conditions on the colony's environment through the collective contribution of the individual ants.

Example

Another great example is when you fly internationally from one place to another. There are numerous participants working together and communicating efficiently to make that a seamless experience without there being a central unit that manages and plans it all.

There are two key characteristics of decentralized systems:

  • Equality of participants: all participants contribute towards the system's decision-making and control, and each of them takes an active role in managing the system.

  • Lack of central authorities: It is not that decentralized systems lack the authority to make changes to the management of the network, rather this authority is distributed among the participants. The system is run by the participants for the benefit of the participants.

"},{"location":"decentralisation/#decentralized-vs-distributed","title":"Decentralized vs Distributed","text":"

These are very easy to get confused, and sometimes it seems they are used interchangeably but they are actually quite different.

A decentralized system is one where there is a lack of central authorities.

A distributed system is a system in which the physical location of the participants, the servers, the nodes, and whatever other components involved are in different places. So instead of the system being physically all in one place, different parts of the system are located on physically different places and the processing of tasks are shared between them.

Example

The most obvious example of a distributed system is cloud computing, where tasks are shared among numerous computers or a database hosted on more than one computer.

What determines whether a system is decentralized or not is whether there are central authorities. What determines whether a system is distributed is whether the processing is done in one or multiple physical locations.

"},{"location":"decentralisation/#decentralized-vs-centralized","title":"Decentralized vs Centralized","text":"

In a centralized system, there is a central authority that manages the system, makes decisions about how it is run and exercises control over the other components of the system.

Example

Some examples of centralized systems:

  • Expedia: Hotel owners and people looking for hotel rooms connect to each other through Expedia, rather than communicating and transacting directly together.
  • Messaging services such as WhatsApp: People communicate with each other through a centrally controlled and owned messaging service.
  • Amazon: Buyers and sellers interact commercially through Amazon.
  • Client-server model: Clients interact with each other only through the server, a great example of this is email where communication between two users goes through a server.
"},{"location":"decentralisation/#why-are-decentralized-systems-useful","title":"Why are Decentralized Systems Useful?","text":"

Decentralized systems are useful because they are open and democratic, more transparent, and they are run solely in the interests of their participants by their participants.

"},{"location":"decentralisation/#benefits","title":"Benefits","text":"
  • Democratic: Decentralized systems require participation in order to successfully function, and therefore they are naturally democratic.

  • Open: Participants can freely join or leave as they wish. The system is open to new participants and does not stop existing ones leaving.

  • Public: Because decentralized systems need to be managed and run by their participants (democratic) and anyone can in principle join the system or leave (open), these systems are often public and their inner workings are not kept confidential.

  • Transparent: In order for the participants of a decentralized system to contribute to its liveliness, they must be made aware of how the system works, how it is managed, and so on.

  • Trust minimisation: participants do not have to go through central authorities to interact with others and achieve their goals. Hence, there is no need to trust central entities to handle your needs. Any guarantees needed by the system has to be provided by the system itself and the way it is designed, and (due to transparency) this is typically well known.

"},{"location":"decentralisation/#drawbacks","title":"Drawbacks","text":"
  • Speed: Decentralized systems are often slower than centralized systems. Because there is more than one decision maker, decisions have to be made by consensus in the group and arriving at that consensus takes more time than a single entity making the decision.

  • Efficiency: In decentralized systems, decision-making about the system requires participation. This makes it far slower to make changes to the system as it takes time for participants to agree to change. Also, because the system is open, participants can leave/join as they wish, then the system has to be designed to cope with an often changing number of participants. All this added complexity inevitably affects the efficiency of the system's operation.

  • Control: Because a decentralized system is often run collectively by its participants, controlling how the system is managed and the direction of its development is difficult.

  • Simplicity: For any system, it is often more complex to engineer a decentralized version with more than one decisions maker than a centralized one that achieves the same goals via central decision-makers and enforcers.

"},{"location":"fund_form/","title":"Developer Fund","text":"The Developer Fund

Following the announcement of a new development fund for growing the Fetch.ai ecosystem, we are accepting applications from prospective projects, either from Cosmos or EVM, to build upon the Fetch.ai network or scale using it's tools.

Please fill the form below to apply for the developer grant.

Name * Project/Company Name [If available] * Contact Email (if you're applying on behalf of a project or a company, please enter the email of the main point of contact) * Project links [Please provide Github, Website, Twitter if available] * Which category does your project fall under? * dApps On-chain analytics Automation DeFi Infrastructure NFTs Other: Project description [Please outline in detail the project you're seeking to receive a grant for] * Have you received any funding previously? * Yes: No Please share any details on the project tokenomics [if available] * What do you want to achieve through your project? * Project development timelines * Project Milestones * What is the funding amount you are looking for? Less than $50,000 Between $50,000 to $150,000 More than $150,000 Funding request breakdown [Please describe how the funds will be used] * Additional attachments [Please share links - e.g dropbox/google drive] * Submit"},{"location":"CosmPy/","title":"Getting started","text":"

Cosmpy is a Python library for interacting with Cosmos-based blockchains.

  • A simplified command line tool for querying and sending transactions to Cosmos-SDK blockchains.
  • Features an easy interface for deploying and interacting with Cosmwasm smart contracts.
  • Provides access to lower-level ledger APIs for advanced use-cases.
"},{"location":"CosmPy/#to-install","title":"To install","text":"
pip3 install cosmpy\n
"},{"location":"CosmPy/#version","title":"Version","text":"

.

"},{"location":"CosmPy/#repository","title":"Repository","text":"
https://github.com/fetchai/cosmpy\n
"},{"location":"CosmPy/#to-contribute","title":"To contribute","text":"

Clone the repo:

git clone https://github.com/fetchai/cosmpy.git --recursive && cd cosmpy\n

Set up development environment:

make new_env_dev\n

This creates a new pipenv virtual environment and installs the development dependencies.

Enter the virtual environment:

pipenv shell\n
"},{"location":"CosmPy/auto-compounder/","title":"Stake Auto-Compounder","text":"

When an account delegates tokens to a network's validator, it will start generating rewards proportional to the amount of Stake delegated. But since rewards aren't automatically added to your stake and therefore don't contribute to future rewards, we can perform a compounding strategy to generate exponential rewards.

"},{"location":"CosmPy/auto-compounder/#delegate","title":"Delegate","text":"

The first thing we need to do is delegate some tokens to a validator. You can do so by using a Wallet and specifying the validator address and amount.

validators = ledger_client.query_validators()\n# choose any validator\nvalidator = validators[0]\nkey = PrivateKey(\"FX5BZQcr+FNl2usnSIQYpXsGWvBxKLRDkieUNIvMOV7=\")\nwallet = LocalWallet(key)\n# delegate some tokens to this validator\ntx = ledger_client.delegate_tokens(validator.address, 9000000000000000000, wallet)\ntx.wait_to_complete()\n
"},{"location":"CosmPy/auto-compounder/#auto-compounder","title":"Auto Compounder","text":"

Then we can construct a code that claims rewards and delegates the rewarded tokens back to the validator. This way we keep growing our Stake and therefore we generate compounded rewards. We first need to define the time limit and the compounding period.

It is important to note that each time an account performs a claim or a delegate transaction it has to pay certain fees, therefore the compounding period has to be long enough to generate sufficient rewards to exceed the fees that will be paid in each transaction.

# set time limit and compounding period in seconds\ntime_limit = 600\nperiod = 100\n
Finally, we start a timer that claims rewards and delegates them in each time period. Notice that in the code below we constructed a while loop that will be running until the timer exceeds the time limit. Each loop will last the time specified in period. We query the balance before and after claiming rewards to get the value of the reward after any fees. If the true reward value is positive, we delegate those tokens to the validator, if it is negative, it means that the fees from claiming and delegating transactions exceeded the rewards, and therefore we won't delegate.

time_check = 0\nstart_time = time.monotonic()\ntime.sleep(period)\n# query, claim and delegate rewards after time period\nwhile time_check < time_limit:\nbegin = time.monotonic()\nsummary = ledger_client.query_staking_summary(wallet.address())\nprint(f\"Staked: {summary.total_staked}\")\nbalance_before = ledger_client.query_bank_balance(wallet.address())\ntx = ledger_client.claim_rewards(validator.address, wallet)\ntx.wait_to_complete()\nbalance_after = ledger_client.query_bank_balance(wallet.address())\n# reward after any fees\ntrue_reward = balance_after - balance_before\nif true_reward > 0:\nprint(f\"Staking {true_reward} (reward after fees)\")\ntx = ledger_client.delegate_tokens(validator.address, true_reward, wallet)\ntx.wait_to_complete()\nelse:\nprint(\"Fees from claim rewards transaction exceeded reward\")\nend = time.monotonic()\ntime.sleep(period-(end-begin))\ntime_check = time.monotonic() - start_time\n

You can view the full python example at staking auto-compounder

"},{"location":"CosmPy/connect-to-network/","title":"Connect to a network","text":"

To start interacting with a blockchain, you first need to establish a connection to a network node. You can use LedgerClient as a client object which takes a NetworkConfig as an argument.

from cosmpy.aerial.client import LedgerClient, NetworkConfig\nledger_client = LedgerClient(NetworkConfig.fetch_mainnet())\n

For convenience, some networks' configurations are provided automatically. For example, NetworkConfig.fetch_mainnet() is the configuration for the Fetch ledger. If you want to interact with other chains, you can customise NetworkConfig as shown in the example below:

cfg = NetworkConfig(\nchain_id=\"cosmoshub-4\",\nurl=\"grpc+https://grpc-cosmoshub.blockapsis.com:429\",\nfee_minimum_gas_price=1,\nfee_denomination=\"uatom\",\nstaking_denomination=\"uatom\",\n)\nledger_client = LedgerClient(cfg)\n

A full list of chain identifiers, denominations and end-points can be found at the Cosmos chain registry.

"},{"location":"CosmPy/deploy-a-contract/","title":"Deploy a contract","text":"

You can deploy smart contracts in CosmPy using LedgerContract. For this, you will need the path to where the contract is stored (in this case simple.wasm), a LedgerClient and a Wallet:

from cosmpy.aerial.contract import LedgerContract\nPATH = \"contracts/simple/simple.wasm\"\ncontract = LedgerContract(PATH, ledger_client)\ncontract.deploy({}, wallet)\n

You can now start interacting with the contract. To get the address of where the contract is deployed on the network:

print(f\"Contract deployed at: {contract.address}\")\n

You can query the values of the contract's state variables:

result = contract.query({\"get\": {\"owner\": wallet}})\nprint(\"Initial state:\", result)\n

You can also set these values. The following sets the state variable value to foobar:

contract.execute({\"set\": {\"value\": \"foobar\"}}, wallet).wait_to_complete()\n

Let's check if this was set correctly:

result = contract.query({\"get\": {\"owner\": wallet)}})\nprint(\"State after set:\", result)\n

Similarly, you can clear the state variables:

contract.execute({\"clear\": {}}, wallet).wait_to_complete()\nresult = contract.query({\"get\": {\"owner\": wallet}})\nprint(\"State after clear:\", result)\n
"},{"location":"CosmPy/liquidity-pool/","title":"Liquidity Pool","text":""},{"location":"CosmPy/liquidity-pool/#swap-tokens","title":"Swap Tokens","text":"

You can interact with a liquidity pool by swapping atestfet for CW20 tokens or vice versa. First, perform all the necessary imports:

import base64\nfrom cosmpy.aerial.client import LedgerClient, NetworkConfig\nfrom cosmpy.aerial.contract import LedgerContract\nfrom cosmpy.aerial.faucet import FaucetApi\nfrom cosmpy.aerial.wallet import LocalWallet\n
Set the network configuration, define a local wallet and add some tokens to it using the FaucetApi

# Network configuration\nledger = LedgerClient(NetworkConfig.latest_stable_testnet())\n# Define any wallet\nwallet = LocalWallet.generate()\n# Add tokens to wallet\nfaucet_api = FaucetApi(NetworkConfig.latest_stable_testnet())\nfaucet_api.get_wealth(wallet.address())\n
Define the CW20, pair, and liquidity token contracts with the following addresses:

# Define cw20, pair and liquidity token contracts\ntoken_contract_address = (\n\"fetch1qr8ysysnfxmqzu7cu7cq7dsq5g2r0kvkg5e2wl2fnlkqss60hcjsxtljxl\"\n)\npair_contract_address = (\n\"fetch1vgnx2d46uvyxrg9pc5mktkcvkp4uflyp3j86v68pq4jxdc8j4y0s6ulf2a\"\n)\nliq_token_contract_address = (\n\"fetch1alzhf9yhghud3qhucdjs895f3aek2egfq44qm0mfvahkv4jukx4qd0ltxx\"\n)\ntoken_contract = LedgerContract(\npath=None, client=ledger, address=token_contract_address\n)\npair_contract = LedgerContract(\npath=None, client=ledger, address=pair_contract_address\n)\nliq_token_contract = LedgerContract(\npath=None, client=ledger, address=liq_token_contract_address\n)\n

Swap the defined swap_amountof atestfet for CW20 tokens

# Swap atestfet for CW20 tokens\nswap_amount = \"10000\"\nnative_denom = \"atestfet\"\ntx = pair_contract.execute(\n{\n\"swap\": {\n\"offer_asset\": {\n\"info\": {\"native_token\": {\"denom\": native_denom}},\n\"amount\": swap_amount,\n}\n}\n},\nsender=wallet,\nfunds=swap_amount + native_denom,\n)\ntx.wait_to_complete()\n

You can query your CW20 balance using the following code:

token_contract.query({\"balance\": {\"address\": str(wallet.address())}})\n

To trade 10 CW20 tokens for atestfet you can use the following:

tx = token_contract.execute({\n\"send\": {\n\"contract\": pair_contract_address,\n\"amount\": \"10\",\n\"msg\": \"eyJzd2FwIjp7fX0=\"\n}\n},wallet)\ntx.wait_to_complete()\n
"},{"location":"CosmPy/liquidity-pool/#add-and-remove-liquidity","title":"Add and Remove Liquidity","text":"

You need to increase your wallet's allowance to provide CW20 tokens to the liquidity pool. You don't need to increase the allowance to provide atestfet

# Set the amount of CW20 tokens to be added to liquidity pool\ncw20_liquidity_amount = \"100\"\n# Increase allowance\ntx = token_contract.execute(\n{\n\"increase_allowance\": {\n\"spender\": pair_contract_address,\n\"amount\": cw20_liquidity_amount,\n\"expires\": {\"never\": {}},\n}\n},\nwallet,\n)\ntx.wait_to_complete()\n
To set the amount of atestfet to be added to the liquidity pool and not influence the existing token prices, we need to choose an amount that matches the atestfet:CW20 token ratio already existing in the pool. For this reason, we will query the pair_contract pool to observe the atestfet:CW20 token ratio

# Query Liquidity Pool\npair_contract.query({\"pool\": {}})\n

At the moment the code was run, the ratio was close to 247:10 atestfet:CW20, and since we defined above the amount of CW20 tokens to provide to the liquidity pool as 100, we will match the LP pool ratio by setting the atestfet amount as 2470. It will be difficult to exactly match the current ratio of the pool, but when adding liquidity to the pool, there is a slippage_tolerance parameter that allows a certain percentage change in the price.

# Set the amount of atestfet tokens to be added to liquidity pool\nnative_liquidity_amount = \"2470\"\n# Provide Liquidity\n# Liquidity should be added so that the slippage tolerance parameter isn't exceeded\ntx = pair_contract.execute(\n{\n\"provide_liquidity\": {\n\"assets\": [\n{\n\"info\": {\"token\": {\"contract_addr\": token_contract_address}},\n\"amount\": cw20_liquidity_amount,\n},\n{\n\"info\": {\"native_token\": {\"denom\": native_denom}},\n\"amount\": native_liquidity_amount,\n},\n],\n\"slippage_tolerance\":\"0.1\"\n}\n},\nsender=wallet,\nfunds=native_liquidity_amount + native_denom,\n)\ntx.wait_to_complete()\n

When providing liquidity, you are rewarded with newly minted LP tokens. LP tokens represent the liquidity provider's share in the pool. You can burn your LP tokens to withdraw your share from the liquidity pool, for more information visit Terraswap. The following code shows how to withdraw your share from the LP.

# Query your LP token balance to burn it all\nLP_token_balance = liq_token_contract.query({\"balance\": {\"address\": str(wallet.address())}})[\"balance\"]\n# Convert the withdrawal msg to base64\nwithdraw_msg = '{\"withdraw_liquidity\": {}}'\nwithdraw_msg_bytes = withdraw_msg.encode(\"ascii\")\nwithdraw_msg_base64 = base64.b64encode(withdraw_msg_bytes)\nmsg = str(withdraw_msg_base64)[2:-1]\n# Withdraw Liquidity\ntx = liq_token_contract.execute(\n{\n\"send\": {\n\"contract\": pair_contract_address,\n\"amount\": LP_token_balance,\n\"msg\": msg,\n}\n},\nsender=wallet,\n)\ntx.wait_to_complete()\n

You can now query you LP token balance to observe that it has gone down to zero

liq_token_contract.query({\"balance\": {\"address\": str(wallet.address())}})\n

You can also check the full code example at liquidity-pool

"},{"location":"CosmPy/low-level-api/","title":"Low-level API","text":"

The Cosmpy library provides a high-level API which greatly simplifies the most common use cases when interacting with Cosmos-based chains (e.g. sending tokens, staking, deploying and interacting with contracts). There are documentation and example code covering such use cases.

However, cosmpy also provides low-level access to the entire Cosmos-SDK, enabling the full gamut of functionality to be accessed, albeit with a little more boilerplate.

Here, we aim to help developers navigate the low-level, protobuf-based API functionality, provided by Cosmpy.

"},{"location":"CosmPy/low-level-api/#recap-high-level-api-aerial","title":"Recap: High Level API - Aerial","text":"

As a reminder, here is a quick example of using the high level functionality provided by Cosmpy. In this case, we connect to a testnet, create a wallet, stake some tokens with a validator, then claim our rewards:

from cosmpy.aerial.client import LedgerClient, NetworkConfig\nfrom cosmpy.aerial.wallet import LocalWallet\nfrom cosmpy.crypto.keypairs import PrivateKey\nclient = LedgerClient(NetworkConfig.fetchai_dorado_testnet())\nwallet = LocalWallet(PrivateKey(\"rBDA3Q0vK5T+JVQmXSoooqUY/mSO4mmhMHQJI31+h1o=\"))\ntx = client.delegate_tokens(\"fetchvaloper1rsane988vksrgp2mlqzclmt8wucxv0ej4hrn2k\", 20, wallet)\ntx.wait_to_complete()\ntx = client.claim_rewards(\"fetchvaloper1rsane988vksrgp2mlqzclmt8wucxv0ej4hrn2k\", wallet)\ntx.wait_to_complete()\n

The available high-level helper functions provided by cosmpy can be found by browsing for instance the aerial client package.

"},{"location":"CosmPy/low-level-api/#low-level-api","title":"Low Level API","text":""},{"location":"CosmPy/low-level-api/#simple-messages","title":"Simple Messages","text":"

Not all Cosmos-SDK functionality is encapsulated in the high level aerial packages. In which case, it is necessary to locate and use the definition of the relevant protobuf message.

Analogous to the rewards claim example above, what if a validator operator wanted to claim their commission? At the time of writing, there is no high-level API to achieve this, so the low level API must be used.

In the protos directory, there is a MsgWithdrawValidatorCommission message, which is what we need. It takes a single validator_address parameter which is a utf-8 string.

To send a transaction containing such a message:

from cosmpy.aerial.client import LedgerClient, NetworkConfig\nfrom cosmpy.aerial.wallet import LocalWallet\nfrom cosmpy.aerial.tx import Transaction\nfrom cosmpy.aerial.client.utils import prepare_and_broadcast_basic_transaction\nfrom cosmpy.protos.cosmos.distribution.v1beta1.tx_pb2 import MsgWithdrawValidatorCommission\nfrom cosmpy.crypto.keypairs import PrivateKey\nclient = LedgerClient(NetworkConfig.fetchai_dorado_testnet())\nwallet = LocalWallet(PrivateKey(\"<redacted>private key of dorado validator0\"))\ntx = Transaction()\ntx.add_message(\nMsgWithdrawValidatorCommission(\nvalidator_address=\"fetchvaloper1rsane988vksrgp2mlqzclmt8wucxv0ej4hrn2k\"\n)\n)\ntx = prepare_and_broadcast_basic_transaction(client, tx, wallet)\ntx.wait_to_complete()\n
"},{"location":"CosmPy/low-level-api/#nested-messages","title":"Nested messages","text":"

The above example creates and broadcasts a simple MsgWithdrawValidatorCommission message. However, sometimes it is necessary to include one message in another. For example, what if we wanted to use the above message but execute it from a different account using authz (i.e. use an account which holds minimal funds, whose keys need not be treated with the same level of care as those of the validator itself)?

In this case, we'll need to send an authz MsgExec message, which can be found in tx_pb2.py under cosmos/authz area of cosmpy/protos. This message takes two parameters. The grantee is a simple string address similar to the above. But the msgs field needs to support multiple types of messages and not just MsgWithdrawValidatorCommission.

Protobuf is strongly typed, so to facilitate this flexibility, it is necessary to first pack the nested message into a protobuf.Any message.

Therefore, we arrive at the code looking like:

from cosmpy.aerial.client import LedgerClient, NetworkConfig\nfrom cosmpy.aerial.wallet import LocalWallet\nfrom cosmpy.aerial.tx import Transaction\nfrom cosmpy.aerial.client.utils import prepare_and_broadcast_basic_transaction\nfrom cosmpy.crypto.keypairs import PrivateKey\nfrom cosmpy.protos.cosmos.distribution.v1beta1.tx_pb2 import MsgWithdrawValidatorCommission\nfrom cosmpy.protos.cosmos.authz.v1beta1.tx_pb2 import MsgExec\nfrom google.protobuf import any_pb2\nclient = LedgerClient(NetworkConfig.fetchai_dorado_testnet())\nwallet = LocalWallet(PrivateKey(\"rBDA3Q0vK5T+JVQmXSoooqUY/mSO4mmhMHQJI31+h1o=\"))\nmsg = any_pb2.Any()\nmsg.Pack(\nMsgWithdrawValidatorCommission(\nvalidator_address=\"fetchvaloper1rsane988vksrgp2mlqzclmt8wucxv0ej4hrn2k\"\n),\n\"\",\n)\ntx = Transaction()\ntx.add_message(MsgExec(grantee=str(wallet.address()), msgs=[msg]))\ntx = prepare_and_broadcast_basic_transaction(client, tx, wallet)\ntx.wait_to_complete()\n
"},{"location":"CosmPy/low-level-api/#more-protobuf-examples","title":"More protobuf examples","text":"

Before running the above, the necessary authz grant must first be put in place. For Ledger Nano users (other hardware wallets are also available) that might mean an excursion to the command line. For the Fetchai network using FetchD:

fetchd tx authz grant $(fetchd keys show grantee --output json | jq -r .address) generic --msg-type \"/cosmos.distribution.v1beta1.MsgWithdrawValidatorCommission\" --from=$(fetchd keys show grantor --output json | jq -r .address) --gas auto --gas-adjustment 1.5 --gas-prices 5000000000atestfet\n

By default, the above provides one year's worth of authorization to withdraw validator commission using accounts already present in the keyring.

For those with access to their keys in python:

from cosmpy.aerial.client import LedgerClient, NetworkConfig\nfrom cosmpy.aerial.wallet import LocalWallet\nfrom cosmpy.aerial.tx import Transaction\nfrom cosmpy.aerial.client.utils import prepare_and_broadcast_basic_transaction\nfrom cosmpy.crypto.keypairs import PrivateKey\nfrom cosmpy.protos.cosmos.distribution.v1beta1.tx_pb2 import MsgWithdrawValidatorCommission\nfrom cosmpy.protos.cosmos.authz.v1beta1.tx_pb2 import MsgGrant\nfrom cosmpy.protos.cosmos.authz.v1beta1.authz_pb2 import GenericAuthorization, Grant\nfrom google.protobuf import any_pb2, timestamp_pb2\nfrom datetime import datetime, timedelta\nclient = LedgerClient(NetworkConfig.fetchai_dorado_testnet())\nwallet = LocalWallet(PrivateKey(\"rBDA3Q0vK5T+JVQmXSoooqUY/mSO4mmhMHQJI31+h1o=\"))\nvalidator = LocalWallet(PrivateKey(\"<redacted>private key of dorado validator0\"))\nauthz_any = any_pb2.Any()\nauthz_any.Pack(\nGenericAuthorization(\nmsg=\"/cosmos.distribution.v1beta1.MsgWithdrawValidatorCommission\"\n),\n\"\",\n)\nexpiry = timestamp_pb2.Timestamp()\nexpiry.FromDatetime(datetime.now() + timedelta(seconds=60))\ngrant = Grant(authorization=authz_any, expiration=expiry)\nmsg = MsgGrant(\ngranter=str(validator.address()),\ngrantee=str(wallet.address()),\ngrant=grant,\n)\ntx = Transaction()\ntx.add_message(msg)\ntx = prepare_and_broadcast_basic_transaction(client, tx, validator)\ntx.wait_to_complete()\n
"},{"location":"CosmPy/oracles/","title":"Oracles","text":"

Oracles are entities that can update state variables in smart contracts and whose goal is usually to accurately estimate or predict some real world quantity or quantities. These quantities can then be used in the logic of other smart contracts.

This guide shows how to write a CosmPy script that deploys and updates an oracle contract with a coin price, and another script that deploys a contract that queries this coin price.

"},{"location":"CosmPy/oracles/#preliminaries","title":"Preliminaries","text":"

We will need the binaries for both contracts, which can be downloaded as follows:

wget https://raw.githubusercontent.com/fetchai/agents-aea/develop/packages/fetchai/contracts/oracle/build/oracle.wasm\nwget https://raw.githubusercontent.com/fetchai/agents-aea/develop/packages/fetchai/contracts/oracle_client/build/oracle_client.wasm\n

The scripts also require the following imports:

from time import sleep\nimport requests\nfrom cosmpy.aerial.client import LedgerClient, NetworkConfig\nfrom cosmpy.aerial.contract import LedgerContract\nfrom cosmpy.aerial.wallet import LocalWallet\nfrom cosmpy.crypto.address import Address\nfrom cosmpy.crypto.keypairs import PrivateKey\n

"},{"location":"CosmPy/oracles/#oracle-deployer-and-updater","title":"Oracle deployer and updater","text":"

We first choose a data source for the coin price, the update interval, and the decimal precision for the oracle value:

COIN_PRICE_URL = (\n\"https://api.coingecko.com/api/v3/simple/price?ids=fetch-ai&vs_currencies=usd\"\n)\nUPDATE_INTERVAL_SECONDS = 10\nORACLE_VALUE_DECIMALS = 5\n

Next, we create a wallet and ledger interface to interact with the latest stable testnet:

wallet = LocalWallet(PrivateKey(\"T7w1yHq1QIcQiSqV27YSwk+i1i+Y4JMKhkpawCQIh6s=\"))\nledger = LedgerClient(NetworkConfig.fetchai_stable_testnet())\n

Create the LedgerContract object:

contract = LedgerContract(\"oracle.wasm\", ledger)\n

To deploy the oracle contract, add the fee amount to the instantiation message and call the deploy function:

instantiation_message = {\"fee\": \"100\"}\ncontract.deploy(instantiation_message, wallet, funds=\"1atestfet\")\nprint(f\"Oracle contract deployed at: {contract.address}\")\n

Save the oracle contract address to use for the oracle client script below (ORACLE_CONTRACT_ADDRESS).

As the deployer of the contract, we have permission to grant the oracle to a particular address. In this case, we'll grant the oracle role to our own wallet:

grant_role_message = {\"grant_oracle_role\": {\"address\": wallet)}}\ncontract.execute(grant_role_message, wallet).wait_to_complete()\n

Finally, start updating the contract with the coin price retrieved from the COIN_PRICE_URL:

while True:\nresp = requests.get(COIN_PRICE_URL).json()\nprice = resp[\"fetch-ai\"][\"usd\"]\nvalue = int(price * 10**ORACLE_VALUE_DECIMALS)\nupdate_message = {\n\"update_oracle_value\": {\n\"value\": str(value),\n\"decimals\": str(ORACLE_VALUE_DECIMALS),\n}\n}\ncontract.execute(update_message, wallet).wait_to_complete()\nprint(f\"Oracle value updated to: {price} USD\")\nprint(f\"Next update in {UPDATE_INTERVAL_SECONDS} seconds...\")\nsleep(UPDATE_INTERVAL_SECONDS)\n

For the complete example script, see aerial_oracle.py.

"},{"location":"CosmPy/oracles/#oracle-client","title":"Oracle client","text":"

Now we'll write a script that deploys a contract that can request the oracle value in exchange for the required fee.

We again start by creating a wallet and ledger interface in a new terminal session:

wallet = LocalWallet(PrivateKey(\"CI5AZQcr+FNl2usnSIQYpXsGWvBxKLRDkieUNIvMOV8=\"))\nledger = LedgerClient(NetworkConfig.fetchai_stable_testnet())\n

Set ORACLE_CONTRACT_ADDRESS to the address of the contract deployed in the previous script:

ORACLE_CONTRACT_ADDRESS = \"contract_address_goes_here\"\n

Next, we define the contract object, set the oracle contract address in the instantiation message, and deploy the contract:

contract = LedgerContract(\"oracle_client.wasm\", ledger)\ninstantiation_message = {\"oracle_contract_address\": str(ORACLE_CONTRACT_ADDRESS)}\ncontract.deploy(instantiation_message, wallet)\n

Finally, define a request interval and start a loop that executes the function that requests the oracle value:

REQUEST_INTERVAL_SECONDS = 10\nwhile True:\nrequest_message = {\"query_oracle_value\": {}}\ncontract.execute(\nrequest_message, wallet, funds=\"100atestfet\"\n).wait_to_complete()\nresult = contract.query({\"oracle_value\": {}})\nprint(f\"Oracle value successfully retrieved: {result}\")\nsleep(REQUEST_INTERVAL_SECONDS)\n

For the complete example script, see aerial_oracle_client.py.

"},{"location":"CosmPy/query-balance/","title":"Querying balances","text":"

A LedgerClient object can be used to query the balances associated with a particular address:

address: str = 'fetch12q5gw9l9d0yyq2th77x6pjsesczpsly8h5089x'\nbalances = ledger_client.query_bank_all_balances(address)\n

This will return a List of Coin objects that contain amount and denom variables that correspond to all the funds held at the address and their denominations. This list includes all natively defined coins along with any tokens transferred using the inter-blockchain communication (IBC) protocol.

>>> balances\n[Coin(amount='29263221445595384075', denom='afet')]\n

It's also possible to query the funds associated with a particular denomination by calling

balance = ledger_client.query_bank_balance(address, denom='afet')\n

which will return the value of the (integer) funds held by the address with the specified denomination. If the denom argument is omitted the function will return the fee denomination specified in the NetworkConfig object used to initialise the LedgerClient.

"},{"location":"CosmPy/send-tokens/","title":"Send tokens","text":"

Once you have your wallet configured, you can send transactions to the network. The LedgerClient object provides useful utilities to do common operations. The following example shows how to send 10 atestfet to another address:

destination_address = 'fetch1h2l3cnu7e23whmd5yrfeunacez9tv0plv5rxqy'\ntx = ledger_client.send_tokens(destination_address, 10, \"atestfet\", wallet)\n# block until the transaction has been successful or failed\ntx.wait_to_complete()\n
"},{"location":"CosmPy/stake-optimizer/","title":"Stake Optimizer","text":"

When you delegate tokens to a validator for a determined period, you can use the auto-compounder to get increasing rewards. You can maximize your rewards for a given staking period by selecting an optimal compounding period. To do this, you will need to follow these steps:

  • Set and Query Variables: When calculating staking rewards, you need to set and query variables such as staking parameters, transaction fees, and network parameters
  • Calculate Reward Rate: After you select and query all the variables needed, you will calculate the reward rate
  • Calculate Optimal Compounding Period: You will calculate the optimal compounding period that will maximize your rewards

First, you need to define a network to work with.

from cosmpy.aerial.client import LedgerClient\nfrom cosmpy.aerial.config import NetworkConfig\nledger = LedgerClient(NetworkConfig.fetchai_stable_testnet())\n
"},{"location":"CosmPy/stake-optimizer/#set-and-query-variables","title":"Set and Query Variables","text":""},{"location":"CosmPy/stake-optimizer/#staking-variables","title":"Staking Variables","text":"

First, we need to define the desired amount and the total period that we would like to stake in: initial_stake and total_period variables. Here we will stake 50 TESTFET for 60000 minutes. For this guide, we will work with minutes as a time unit.

initial_stake = 50000000000000000000\ntotal_period = 60000\n
"},{"location":"CosmPy/stake-optimizer/#validator-selection-and-variables","title":"Validator Selection and Variables","text":"

We will now select a validator to delegate our tokens. We will do this by analyzing which one has the lowest commission and a reasonable amount of stake delegated compared to the total stake.

from cosmpy.protos.cosmos.staking.v1beta1.query_pb2 import QueryValidatorsRequest\nreq = QueryValidatorsRequest()\nresp = ledger.staking.Validators(req)\n# Calculate the total stake currently in the testnet\n# Status = 3 means that the validator is bonded\nvalidators_stake = [int(validator.tokens) for validator in resp.validators if validator.status == 3]\ntotal_stake = sum(validators_stake)\n# For every bonded validator, we print commission and percentage of total stake\nprint(\"MONIKER      COMISSION   % of TOTAL STAKE\")\nfor validator in resp.validators:\nif validator.status == 3:\nmoniker = validator.description.moniker\ncomission = int(validator.commission.commission_rates.rate)/1e18*100\nprint(moniker[:10],\" \", comission,\"%     \", round(int(validator.tokens)/total_stake*100,3),\"%\")\n

After running the code above, you will observe each validator commission rate and its percentage delegated of the total stake. The most important parameter to observe in each validator is the commission it will take from the rewards. We will always select a validator with the lower commission as long as it has a reasonable stake compared with the total stake. In this case, at the moment the code was run, all validators had the same commission, therefore, we simply selected the validator with the highest stake, which was validator0. Feel free to select the most convenient validator when you run the code above. We will save the variables commission and the fraction of our initial_stake to the total stake to use them later on.

# get all the active validators on the network\nvalidators = ledger.query_validators()\n# Query info of selected validator\nselected_validator = \"validator0\"\nvalidator = [v for v in validators if v.moniker == selected_validator][0]\nquery_validator = [v for v in resp.validators if v.description.moniker == selected_validator][0]\n# Set the comission %\ncommission = int(query_validator.commission.commission_rates.rate)/1e18\n# Set percentage delegated of total stake\npct_delegated = initial_stake/total_stake\n
"},{"location":"CosmPy/stake-optimizer/#estimate-transaction-fees","title":"Estimate Transaction Fees","text":"

We need to know an estimate of the transaction fees it will cost every time we claim rewards and delegate tokens. For that, both claim rewards and delegate tokens transactions were combined into a single multi-msg transaction to simulate the total fees.

from cosmpy.aerial.client.distribution import create_withdraw_delegator_reward\nfrom cosmpy.aerial.client.staking import create_delegate_msg\nfrom cosmpy.aerial.tx import SigningCfg\nfrom cosmpy.aerial.wallet import LocalWallet\nfrom cosmpy.crypto.keypairs import PrivateKey\nfrom cosmpy.crypto.address import Address\nfrom cosmpy.aerial.tx import Transaction\n# Use any address with at least the amount of initial_stake available\nkey = PrivateKey(\"XZ5BZQcr+FNl2usnSIQYpXsGWvBxKLRDkieUNIvMOV7=\")\nalice = LocalWallet(key)\nalice_address = Address(key)._display\ntx = Transaction()\n# Add delegate msg\ntx.add_message(create_delegate_msg(alice_address,validator.address,initial_stake,\"atestfet\"))\n# Add claim reward msg\ntx.add_message(create_withdraw_delegator_reward(alice_address, validator.address))\naccount = ledger.query_account(alice.address())\ntx.seal(SigningCfg.direct(alice.public_key(), account.sequence),fee=\"\",gas_limit=0)\ntx.sign(alice.signer(), ledger.network_config.chain_id, account.number)\ntx.complete()\n# simulate the fee for the transaction\n_, str_tx_fee = ledger.estimate_gas_and_fee_for_tx(tx)\n
Since the output of this function is a string, we will convert it to an int and round it up to get a more conservative estimate for the fee

denom = \"atestfet\"\ntx_fee = str_tx_fee[:-len(denom)]\n# Add a 20% to the fee estimation to get a more conservative estimate\nfee = int(tx_fee) * 1.20\n
"},{"location":"CosmPy/stake-optimizer/#query-network-variables","title":"Query Network Variables","text":"

There are three network variables that we need to query since they will contribute to the staking rewards calculation: total_supply, inflation and community_tax

# Total Supply of tokens\nreq = QueryTotalSupplyRequest()\nresp = ledger.bank.TotalSupply(req)\ntotal_supply = float(json.loads(resp.supply[0].amount))\n# Inflation\nreq = QueryParamsRequest(subspace=\"mint\", key=\"InflationRate\") \nresp = ledger.params.Params(req)\ninflation = float(json.loads(resp.param.value))\n# Community Tax\nreq = QueryParamsRequest(subspace=\"distribution\", key=\"communitytax\") \nresp = ledger.params.Params(req)\ncommunity_tax = float(json.loads(resp.param.value))\n
"},{"location":"CosmPy/stake-optimizer/#calculate-reward-rate","title":"Calculate Reward Rate","text":"

We can now proceed to calculate a theoretical staking rewards rate using the variables gathered above. These are: inflation, total_supply, pct_delegated, community_tax and commission

# Calculate annual reward\nanual_reward = (inflation * total_supply) *pct_delegated* (1- community_tax)*(1- commission)\n# Convert from annual reward to minute reward\nminute_reward = anual_reward/360/24/60\n# Set the rate\nrate = minute_reward/initial_stake\n
"},{"location":"CosmPy/stake-optimizer/#calculate-optimal-compounding-period","title":"Calculate Optimal Compounding Period","text":"

We can calculate the optimal compounding period that maximizes our staking rewards analytically by using the following formula.

Where:

M = Total stake at time D

S= Initial Stake \\ f = Transaction Fee \\ k = Reward Rate

m = Number Of Compounding Transactions \\ n = Compounding Period

D = m x n = Total Staking Time

We will now find the value that maximizes reward by taking the first derivative with respect to n and finding the root in the interval (0,D]

import numpy as np\nfrom sympy.utilities.lambdify import lambdify, implemented_function\nfrom sympy import *\nfrom scipy.optimize import brentq\nf = fee\nS = initial_stake\nk = rate\nD = total_period\n# x will represent n\nx = Symbol(\"x\")\n# Define the function\nM = (S*(1+(k*x))**(D/x))+((1-((1+(k*x))**(D/x)))/(k*x))*f\nMx = lambdify(x,M)\n# Take the first derivative with respect to x\nM_prime = M.diff(x)\nMx_prime = lambdify(x,M_prime)\n# Find the maximum reward value by finding the root of the function\noptimal_period = brentq(Mx_prime, 0.1, D)\nprint(\"optimal_period: \", analytical_optimal_period, \" minutes\")\n
You can make use of the optimal_period value in the staking auto-compounder to maximize your rewards

You can also plot the function along with the optimal period to observe the results

import matplotlib.pyplot as plt\nplot = plt.figure(0,figsize=(6,4), dpi=100)\ny = np.linspace(1,300, 100)\nplt.plot(y,Mx(y),\"k\", label = 'analytical function')\nplt.axvline(x = optimal_period, color = 'g', linewidth = 2, label = f'optimal period: {round(optimal_period)}')\nplt.legend()\nplt.xlabel(\"Compounding periods\")\nplt.ylabel('Total Reward')\nplt.title('Maximizing Rewards')\nplt.grid()\n

Finally, we can compare the compounding staking rewards to a simple non-compounding strategy

# Compounding Strategy\ncomp_rewards = []\nrewards = 0\nperiod = optimal_period\nS = initial_stake\nfor i in range(total_period):\nrewards = rewards + (S*rate)\nif i%period == 0:\nS = S + rewards - fee\nrewards = 0\ncomp_rewards.append(S)\nS = S + rewards - (fee/2)\ncomp_rewards.append(S)\n# Simple Strategy\ns_reward = initial_stake*rate\nsimple_rewards = [initial_stake+(s_reward*i) for i in range(comp_period)]\n# Plots\nplot = plt.figure(0,figsize=(12,4), dpi=100)\nplt.subplot(1,2,1)\nplt.plot(comp_rewards, label = \"Compounded Rewards\")\nplt.plot(simple_rewards, label = \"Simple Rewards\")\nplt.xlabel(\"time in minutes\")\nplt.ylabel('Reward')\nplt.title('Staking Rewards')\nplt.legend()\nplt.subplot(1,2,2)\nplt.plot(total_rewards, label = \"Compounded Rewards\")\nplt.plot(simple_rewards, label = \"Simple Rewards\")\nplt.xlabel(\"time in minutes\")\nplt.ylabel('Reward')\nplt.title('Staking Rewards (log scale)')\nplt.legend()\nplt.yscale('log')\n

You can view an abbreviated version of the code at stake optimizer

"},{"location":"CosmPy/staking/","title":"Staking","text":"

A big part of the cosmos networks is staking. Staking is the process where you delegate your tokens to the network's validators in order to secure the network. There are three main actions you can take when staking:

  • Delegating: This is the process where you send your tokens to a chosen validator. They are applied immediately and you start earning rewards as soon as this transaction completes. The more tokens you stake, the more rewards you will earn.
  • Redelegating: This is the process where you transfer your staked tokens from one validator to another. This can be for many reasons, such as better returns, more trustworthiness, etc.
  • Undelegating: While your tokens are staked, you cannot spend them or send them to other users. To regain access to them, you must undelegate them. When you initiate this process, the funds will be removed from the validator they were delegated to, and must be left to cool down for a period of time (for example 21 days). After this period, the funds are automatically released into the user's wallet.
"},{"location":"CosmPy/staking/#actions","title":"Actions","text":"

LedgerClient provides useful utilities for interacting with the staking component of the network.

Note

For simplicity, the staking methods do not have an option for specifying the denom field. This is because in almost all networks, there is only one staking denomination. Therefore, the denomination used is the one specified in the NetworkConfig supplied to the LedgerClient object.

"},{"location":"CosmPy/staking/#delegate","title":"Delegate","text":"

To stake 20 tokens with the specific validator using a Wallet:

validator_address = 'fetchvaloper1e4ykjwcwhwtasqxq50d4m7xz9hh7a86e9y8h87'\ntx = ledger_client.delegate_tokens(validator_address, 20, wallet)\n# block until the transaction has been successful or failed\ntx.wait_to_complete()\n
"},{"location":"CosmPy/staking/#redelegate","title":"Redelegate","text":"

To redelegate 10 tokens from an existing validator (with the address validator_address) to another (with the address alternate_validator_address):

validator_address = 'fetchvaloper1e4ykjwcwhwtasqxq50d4m7xz9hh7a86e9y8h87'\nalternate_validator_address = 'fetchvaloper1e4ykjwcwhwtasqxq50d4m7xz9hh7a86e9y8h87'\ntx = ledger_client.redelegate_tokens(validator_address, alternate_validator_address, 10, wallet)\n# block until the transaction has been successful or failed\ntx.wait_to_complete()\n
"},{"location":"CosmPy/staking/#undelegate","title":"Undelegate","text":"

To undelegate 5 tokens and start the cool down process:

tx = ledger_client.undelegate_tokens(validator_address, 5, wallet)\n# block until the transaction has been successful or failed\ntx.wait_to_complete()\n

Note

The cool down is tracked for each invocation of undelegate action. So for example if you trigger 3 undelegate actions on 3 consecutive days. The first batch of tokens will become available 3 days before the final batch.

"},{"location":"CosmPy/staking/#claiming-rewards","title":"Claiming Rewards","text":"

While your funds are staked, you are earning rewards on them. Rewards can be collected at any time and unlike delegations, when collected they become immediately available.

To claim rewards from a specific validator:

tx = ledger_client.claim_rewards(validator_address, wallet)\n# block until the transaction has been successful or failed\ntx.wait_to_complete()\n
"},{"location":"CosmPy/staking/#queries","title":"Queries","text":""},{"location":"CosmPy/staking/#stake-summary","title":"Stake Summary","text":"

At any point you can query the stake information of any particular address. This can be done using the LedgerClient as shown in the example below:

address = 'fetch1h2l3cnu7e23whmd5yrfeunacez9tv0plv5rxqy'\ns = ledger_client.query_staking_summary(address)\nprint(f\"Summary: Staked: {s.total_staked} Unbonding: {s.total_unbonding} Rewards: {s.total_rewards}\")\n
"},{"location":"CosmPy/swap-automation/","title":"Swap Automation","text":"

A mean-reversion strategy expects the prices to return to \u201cnormal\u201d levels or a certain moving average following a temporary price spike. We can construct a similar strategy using the Liquidity Pool, where we will set upper and lower bound prices that will trigger a sell and a buy transaction respectively. If the behavior of the LP prices works as expected always returning to a certain moving average, we could profit by selling high and buying low. We will do this by swapping atestfet and CW20 with the Liquidity Pool, we refer to a sell transaction when we sell atestfet and get CW20 tokens, a buy transaction would be exactly the opposite.

The code will require the following imports:

from time import sleep\nfrom cosmpy.aerial.client import LedgerClient, NetworkConfig\nfrom cosmpy.aerial.contract import LedgerContract\nfrom cosmpy.aerial.faucet import FaucetApi\nfrom cosmpy.aerial.wallet import LocalWallet\n

We will define the swap_native_for_cw20 function that trades swap_amount of atestfet from wallet for CW20 tokens by executing a pair_contract:

def swap_native_for_cw20(swap_amount, pair_contract, wallet):\ntx = pair_contract.execute({\n\"swap\": {\n\"offer_asset\": {\n\"info\": {\n\"native_token\": {\n\"denom\": \"atestfet\"\n}\n},\n\"amount\": str(swap_amount)\n}\n}\n},sender= wallet, funds= str(swap_amount) + \"atestfet\")\nprint(\"swapping native for cw20 tokens\")\ntx.wait_to_complete()\n
Now, we will define the swap_cw20_for_native function that does exactly the opposite of the function defined above: trades swap_amount of CW20 tokens from wallet for atestfet. This time the CW20 token_contract is executed using the pair_contract_address. Finally, you need to include the {\"swap\":{}} message in the \"msg\" field. However, this swap message has to be encoded into base64. When you encode {\"swap\":{}} message into base64 you get: eyJzd2FwIjp7fX0=

def swap_cw20_for_native(swap_amount, pair_contract_address, token_contract, wallet):\ntx = token_contract.execute({\n\"send\": {\n\"contract\": pair_contract_address,\n\"amount\": str(swap_amount),\n\"msg\": \"eyJzd2FwIjp7fX0=\"\n}\n},wallet)\nprint(\"swapping cw20 for native tokens\")\ntx.wait_to_complete()\n
Set the network configuration, define a local wallet and add some tokens to it using the FaucetApi

# Define any wallet\nwallet =  LocalWallet.generate()\n# Network configuration\nledger = LedgerClient(NetworkConfig.latest_stable_testnet())\n# Add tokens to wallet\nfaucet_api = FaucetApi(NetworkConfig.latest_stable_testnet())\nwallet_balance = ledger.query_bank_balance(wallet.address())\nwhile wallet_balance < (10**18):\nprint(\"Providing wealth to wallet...\")\nfaucet_api.get_wealth(wallet.address())\nwallet_balance = ledger.query_bank_balance(wallet.address())\n
Define the CW20, pair, and liquidity token contracts with the following addresses:

# Define cw20, pair and liquidity token contracts\ntoken_contract_address = (\n\"fetch1qr8ysysnfxmqzu7cu7cq7dsq5g2r0kvkg5e2wl2fnlkqss60hcjsxtljxl\"\n)\npair_contract_address = (\n\"fetch1vgnx2d46uvyxrg9pc5mktkcvkp4uflyp3j86v68pq4jxdc8j4y0s6ulf2a\"\n)\nliq_token_contract_address = (\n\"fetch1alzhf9yhghud3qhucdjs895f3aek2egfq44qm0mfvahkv4jukx4qd0ltxx\"\n)\ntoken_contract = LedgerContract(\npath=None, client=ledger, address=token_contract_address\n)\npair_contract = LedgerContract(\npath=None, client=ledger, address=pair_contract_address\n)\nliq_token_contract = LedgerContract(\npath=None, client=ledger, address=liq_token_contract_address\n)\n

We will define a trading wallet named tokens that will keep track of the amount of atestfet or CW20 tokens we hold at each moment. The currency variable will keep track of the token type. We will never have a mixed trading wallet since in this strategy, every time we perform a swap, we sell all the current tokens.

# Trading Wallet\ntokens = 1000000\ncurrency = \"atestfet\"\n

Now we will define the upper and lower price bounds (atestfet/CW20) that will trigger a buy and a sell transaction of atestfet. We also define the commission rate (0.3% in Terraswap) and the interval time step to query the pool's price.

upper_bound = 26\nlower_bound = 24\n# LP commission\ncommission = 0.003\n# Interval in seconds\ninterval = 5\n
Finally, we will initialize a loop, in every step it will:

  • Query the Liquidity Pool status
  • Check if current trading wallet's currency
  • Calculate the atestfet/CW20 price using the tokens received tokens_out if the whole trading wallet's balance tokens was to be swapped with the liquidity pool
  • If atestfet sell/buy price is equal or lower/higher than the lower/upper bound, it will trigger a sell/buy transaction of atestfet to buy/sell CW20 tokens.
  • Update trading wallet token balance and currency
  • Sleep interval and repeat
while True:\n# Query LP status\npool = pair_contract.query({\"pool\": {}})\nnative_amount = int(pool[\"assets\"][1][\"amount\"])\ncw20_amount = int(pool[\"assets\"][0][\"amount\"])\nif currency == \"atestfet\":\n# Calculate received tokens if tokens amount is given to LP\ntokens_out = round(((cw20_amount*tokens)/(native_amount+tokens))*(1-commission))\n# Sell price of atestfet => give atestfet, get cw20\nsell_price = tokens/tokens_out\nprint(\"atestfet sell price: \", sell_price)\nif sell_price <= lower_bound:\nswap_native_for_cw20(tokens, pair_contract, wallet)\ntokens = int(token_contract.query({\"balance\": {\"address\": str(wallet.address())}})[\"balance\"])\ncurrency = \"CW20\"\nelse:\n# Calculate received tokens if tokens amount is given to LP\ntokens_out = round(((native_amount*tokens)/(cw20_amount+tokens))*(1-commission))\n# Buy price of atestfet => give cw20, get atestfet\nbuy_price = tokens_out/tokens\nprint(\"atestfet buy price: \", buy_price)\nif buy_price >= upper_bound:\nswap_cw20_for_native(tokens, pair_contract_address, token_contract, wallet)\ntokens = tokens_out\ncurrency = \"atestfet\"\nsleep(interval)\n

This code assumes other traders performing transactions with the Liquidity Pool that will generate price movements. You can check out the full example at swap-automation

"},{"location":"CosmPy/wallet-topup/","title":"Wallet Top-up","text":"

In a case where you are performing multiple transactions from a certain task_wallet, you can set an algorithm to keep that wallet address topped-up. For this use case, we will use three different wallets: wallet, authz_wallet, and task_wallet. Wallet will be the main wallet address that we don't want to give full access to, therefore we will authorize authz_wallet to send a certain amount of tokens from wallet to task_wallet every time task_wallet balance falls below a certain minimum_balance threshold. This way, task_wallet can keep performing transactions using the main wallet's tokens by being topped-up by authz_wallet. Start by defining wallet, authz_wallet and task_wallet address.

from cosmpy.aerial.wallet import LocalWallet\nfrom cosmpy.crypto.keypairs import PrivateKey\nfrom cosmpy.aerial.client import LedgerClient, NetworkConfig\nledger = LedgerClient(NetworkConfig.latest_stable_testnet())\n# Define wallets with any private keys\nwallet = LocalWallet(PrivateKey(\"F7w1yHq1QIcQiSqV27YSwk+i1i+Y4JMKhkpawCQIh6s=\"))\nauthz_wallet = LocalWallet(\nPrivateKey(\"KI5AZQcr+FNl2usnSIQYpXsGWvBxKLRDkieUNIvMOV8=\")\n)\n# Define any task_wallet address\ntask_wallet_address = 'fetch1ay6grfwhlm00wydwa3nw0x2u44qz4hg2uku8dc'\n
Wallet will need to have enough tokens available to top-up task_wallet, and authz_wallet will need enough tokens to pay for transaction fees. Now you will need to give authorization to authz_wallet to send tokens from wallet. You will define the expiration and the spend limit of the authorization in total_authz_time and spend_amount. The code below shows how to perform this kind of transaction:

from cosmpy.protos.cosmos.base.v1beta1.coin_pb2 import Coin\nfrom cosmpy.aerial.client.utils import prepare_and_broadcast_basic_transaction\nfrom cosmpy.aerial.tx import Transaction\nfrom datetime import datetime, timedelta\nfrom google.protobuf import any_pb2, timestamp_pb2\nfrom cosmpy.protos.cosmos.authz.v1beta1.authz_pb2 import Grant\nfrom cosmpy.protos.cosmos.authz.v1beta1.tx_pb2 import MsgGrant\nfrom cosmpy.protos.cosmos.bank.v1beta1.authz_pb2 import SendAuthorization\n# Set total authorization time and spend amount\ntotal_authz_time = 10000\namount = 1000000000000000000\nspend_amount = Coin(amount=str(amount), denom=\"atestfet\")\n# Authorize authz_wallet to send tokens from wallet\nauthz_any = any_pb2.Any()\nauthz_any.Pack(\nSendAuthorization(spend_limit=[spend_amount]),\n\"\",\n)\nexpiry = timestamp_pb2.Timestamp()\nexpiry.FromDatetime(datetime.now() + timedelta(seconds=total_authz_time * 60))\ngrant = Grant(authorization=authz_any, expiration=expiry)\nmsg = MsgGrant(\ngranter=str(wallet.address()),\ngrantee=str(authz_wallet.address()),\ngrant=grant,\n)\ntx = Transaction()\ntx.add_message(msg)\ntx = prepare_and_broadcast_basic_transaction(ledger, tx, wallet)\ntx.wait_to_complete()\n

Next, you will need to define the amount to top-up, the threshold that will trigger the top-up, and the interval time to query the task_wallet balance. We will define these amounts in the following variables: top_up_amount, minimum_balance and interval_time.

# Top-up amount\namount = 10000000000000000\ntop_up_amount = Coin(amount=str(amount), denom=\"atestfet\")\n# Minimum balance for task_wallet\nminimum_balance = 1000000000000000\n# Interval to query task_wallet's balance in seconds\ninterval_time = 5\n

Finally, run a continuously running loop that will: * Check the main wallet's balance to make sure it has enough tokens to top up the task_wallet_address * Check task_wallet's balance, if it is lower than minimum_balance then authz_wallet will send top_up_amount of tokens from wallet to task_wallet * Sleep interval_time and repeat

import time\nfrom cosmpy.protos.cosmos.authz.v1beta1.tx_pb2 import MsgExec\nfrom cosmpy.protos.cosmos.bank.v1beta1.tx_pb2 import MsgSend\nwhile True:\nwallet_address = str(wallet.address())\nwallet_balance = ledger.query_bank_balance(wallet_address)\nif wallet_balance < amount:\nprint(\"Wallet doesn't have enough balance to top-up task_wallet\")\nbreak\ntask_wallet_balance = ledger.query_bank_balance(task_wallet_address)\nif task_wallet_balance < minimum_balance:\nprint(\"topping up task wallet\")\n# Top-up task_wallet\nmsg = any_pb2.Any()\nmsg.Pack(\nMsgSend(\nfrom_address=wallet_address,\nto_address=task_wallet_address,\namount=[top_up_amount],\n),\n\"\",\n)\ntx = Transaction()\ntx.add_message(MsgExec(grantee=str(authz_wallet.address()), msgs=[msg]))\ntx = prepare_and_broadcast_basic_transaction(ledger, tx, authz_wallet)\ntx.wait_to_complete()\ntime.sleep(interval_time)\n

While the code above keeps running, you can make sure that task_wallet is always topped-up as long as authz_wallet has authorization to send the required tokens and the main wallet has enough balance.

You can also check out the authorization and top-up code examples at authz and top-up respectively.

"},{"location":"CosmPy/wallets-and-keys/","title":"Wallets and Keys","text":"

To make changes on a network, you will need to start sending transactions to it. This in tern involves managing private keys and addresses. Luckily, CosmPy makes this relatively straightforward.

The following code outlines how to both generate a completely new private key and how to recover a previously generated one:

from cosmpy.aerial.wallet import LocalWallet\nfrom cosmpy.crypto.keypairs import PrivateKey\n# To create a random private key:\nprivate_key = PrivateKey()\n# To recover an existing private key:\nprivate_key = PrivateKey('<base64 encoded private key>')\n

The PrivateKey object is one of CosmPy's low level primitives. This is why it is generally paired with a Wallet object in most scenarios. Below, a LocalWallet (a kind of Wallet) is created using the private key:

wallet = LocalWallet(private_key)\n

Creating the wallet allows users to query useful information such as the address from the wallet directly.

print(wallet.address()) # will print the address for the wallet\n
"},{"location":"CosmPy/wallets-and-keys/#existing-account","title":"Existing account","text":"

To use cosmpy with an existing account, extract the private key and convert it into a base64 encoded string.

For example, to do this on macOS or Linux for the Fetch.ai network using its FetchD CLI:

fetchd keys export mykeyname --unsafe --unarmored-hex | xxd -r -p | base64\n
"},{"location":"CosmPy/wallets-and-keys/#from-mnemonic","title":"From mnemonic","text":"

If you have the mnemonic phrase to an account, you can get the associated private key as follows:

from bip_utils import Bip39SeedGenerator, Bip44, Bip44Coins\nmnemonic = \"person knife december tail tortoise jewel warm when worry limit reward memory piece cool sphere kitchen knee embody soft own victory sauce silly page\"\nseed_bytes = Bip39SeedGenerator(mnemonic).Generate()\nbip44_def_ctx = Bip44.FromSeed(seed_bytes, Bip44Coins.COSMOS).DeriveDefaultPath()\nwallet = LocalWallet(PrivateKey(bip44_def_ctx.PrivateKey().Raw().ToBytes()))\n

Danger

Of course in real applications, you should never include a mnemonic in public code.

"},{"location":"CosmPy/wallets-and-keys/#custom-prefix-network","title":"Custom prefix network:","text":"

In case you are using a network other than fetch.ai's, you can provide the custom prefix when creating the wallet:

alice = LocalWallet(PrivateKey(\"L1GsisFk+oaIug3XZlILWk2pJDVFS5aPJsrovvUEDrE=\"), prefix=\"custom_prefix\")\naddress = alice.address()\nprint(f\"Address: {address}\")\nbalance = client.query_bank_balance(address, \"uatom\")\n
"},{"location":"CosmPy/api/aerial/coins/","title":"Parse the coins","text":""},{"location":"CosmPy/api/aerial/coins/#cosmpyaerialcoins","title":"cosmpy.aerial.coins","text":"

Parse the coins.

"},{"location":"CosmPy/api/aerial/coins/#parse_coins","title":"parse_coins","text":"
def parse_coins(value: str) -> List[Coin]\n

Parse the coins.

Arguments:

  • value: coins

Raises:

  • RuntimeError: If unable to parse the value

Returns:

coins

"},{"location":"CosmPy/api/aerial/config/","title":"Network configurations","text":""},{"location":"CosmPy/api/aerial/config/#cosmpyaerialconfig","title":"cosmpy.aerial.config","text":"

Network configurations.

"},{"location":"CosmPy/api/aerial/config/#networkconfigerror-objects","title":"NetworkConfigError Objects","text":"
class NetworkConfigError(RuntimeError)\n

Network config error.

Arguments:

  • RuntimeError: Runtime error

"},{"location":"CosmPy/api/aerial/config/#networkconfig-objects","title":"NetworkConfig Objects","text":"
@dataclass\nclass NetworkConfig()\n

Network configurations.

Raises:

  • NetworkConfigError: Network config error
  • RuntimeError: Runtime error

"},{"location":"CosmPy/api/aerial/config/#validate","title":"validate","text":"
def validate()\n

Validate the network configuration.

Raises:

  • NetworkConfigError: Network config error

"},{"location":"CosmPy/api/aerial/config/#fetchai_dorado_testnet","title":"fetchai_dorado_testnet","text":"
@classmethod\ndef fetchai_dorado_testnet(cls) -> \"NetworkConfig\"\n

Fetchai dorado testnet.

Returns:

Network configuration

"},{"location":"CosmPy/api/aerial/config/#fetchai_alpha_testnet","title":"fetchai_alpha_testnet","text":"
@classmethod\ndef fetchai_alpha_testnet(cls)\n

Get the fetchai alpha testnet.

Raises:

  • RuntimeError: No alpha testnet available

"},{"location":"CosmPy/api/aerial/config/#fetchai_beta_testnet","title":"fetchai_beta_testnet","text":"
@classmethod\ndef fetchai_beta_testnet(cls)\n

Get the Fetchai beta testnet.

Raises:

  • RuntimeError: No beta testnet available

"},{"location":"CosmPy/api/aerial/config/#fetchai_stable_testnet","title":"fetchai_stable_testnet","text":"
@classmethod\ndef fetchai_stable_testnet(cls)\n

Get the fetchai stable testnet.

Returns:

fetchai stable testnet. For now dorado is fetchai stable testnet.

"},{"location":"CosmPy/api/aerial/config/#fetchai_mainnet","title":"fetchai_mainnet","text":"
@classmethod\ndef fetchai_mainnet(cls) -> \"NetworkConfig\"\n

Get the fetchai mainnet configuration.

Returns:

fetch mainnet configuration

"},{"location":"CosmPy/api/aerial/config/#fetch_mainnet","title":"fetch_mainnet","text":"
@classmethod\ndef fetch_mainnet(cls) -> \"NetworkConfig\"\n

Get the fetch mainnet.

Returns:

fetch mainnet configurations

"},{"location":"CosmPy/api/aerial/config/#latest_stable_testnet","title":"latest_stable_testnet","text":"
@classmethod\ndef latest_stable_testnet(cls) -> \"NetworkConfig\"\n

Get the latest stable testnet.

Returns:

latest stable testnet

"},{"location":"CosmPy/api/aerial/exceptions/","title":"Exceptions","text":""},{"location":"CosmPy/api/aerial/exceptions/#cosmpyaerialexceptions","title":"cosmpy.aerial.exceptions","text":"

Exceptions.

"},{"location":"CosmPy/api/aerial/exceptions/#queryerror-objects","title":"QueryError Objects","text":"
class QueryError(RuntimeError)\n

Invalid Query Error.

"},{"location":"CosmPy/api/aerial/exceptions/#notfounderror-objects","title":"NotFoundError Objects","text":"
class NotFoundError(QueryError)\n

Not found Error.

"},{"location":"CosmPy/api/aerial/exceptions/#querytimeouterror-objects","title":"QueryTimeoutError Objects","text":"
class QueryTimeoutError(QueryError)\n

Query timeout Error.

"},{"location":"CosmPy/api/aerial/exceptions/#broadcasterror-objects","title":"BroadcastError Objects","text":"
class BroadcastError(RuntimeError)\n

Broadcast Error.

"},{"location":"CosmPy/api/aerial/exceptions/#__init__","title":"__init__","text":"
def __init__(tx_hash: str, message: str)\n

Init Broadcast error.

Arguments:

  • tx_hash: transaction hash
  • message: message

"},{"location":"CosmPy/api/aerial/exceptions/#outofgaserror-objects","title":"OutOfGasError Objects","text":"
class OutOfGasError(BroadcastError)\n

Insufficient Fess Error.

"},{"location":"CosmPy/api/aerial/exceptions/#__init___1","title":"__init__","text":"
def __init__(tx_hash: str, gas_wanted: int, gas_used: int)\n

Initialize.

Arguments:

  • tx_hash: transaction hash
  • gas_wanted: gas required to complete the transaction
  • gas_used: gas used

"},{"location":"CosmPy/api/aerial/exceptions/#insufficientfeeserror-objects","title":"InsufficientFeesError Objects","text":"
class InsufficientFeesError(BroadcastError)\n

Insufficient Fess Error.

"},{"location":"CosmPy/api/aerial/exceptions/#__init___2","title":"__init__","text":"
def __init__(tx_hash: str, minimum_required_fee: str)\n

Initialize.

Arguments:

  • tx_hash: transaction hash
  • minimum_required_fee: Minimum required fee
"},{"location":"CosmPy/api/aerial/faucet/","title":"Ledger faucet API interface","text":""},{"location":"CosmPy/api/aerial/faucet/#cosmpyaerialfaucet","title":"cosmpy.aerial.faucet","text":"

Ledger faucet API interface.

"},{"location":"CosmPy/api/aerial/faucet/#faucetapi-objects","title":"FaucetApi Objects","text":"
class FaucetApi()\n

Faucet API.

"},{"location":"CosmPy/api/aerial/faucet/#__init__","title":"__init__","text":"
def __init__(net_config: NetworkConfig)\n

Init faucet API.

Arguments:

  • net_config: Ledger network configuration.

Raises:

  • ValueError: Network config has no faucet url set

"},{"location":"CosmPy/api/aerial/faucet/#get_wealth","title":"get_wealth","text":"
def get_wealth(address: Union[Address, str]) -> None\n

Get wealth from the faucet for the provided address.

Arguments:

  • address: the address.

Raises:

  • RuntimeError: Unable to create faucet claim
  • RuntimeError: Failed to check faucet claim status
  • RuntimeError: Failed to get wealth for address
  • ValueError: Faucet claim check timed out
"},{"location":"CosmPy/api/aerial/gas/","title":"Transaction gas strategy","text":""},{"location":"CosmPy/api/aerial/gas/#cosmpyaerialgas","title":"cosmpy.aerial.gas","text":"

Transaction gas strategy.

"},{"location":"CosmPy/api/aerial/gas/#gasstrategy-objects","title":"GasStrategy Objects","text":"
class GasStrategy(ABC)\n

Transaction gas strategy.

"},{"location":"CosmPy/api/aerial/gas/#estimate_gas","title":"estimate_gas","text":"
@abstractmethod\ndef estimate_gas(tx: Transaction) -> int\n

Estimate the transaction gas.

Arguments:

  • tx: Transaction

Returns:

None

"},{"location":"CosmPy/api/aerial/gas/#block_gas_limit","title":"block_gas_limit","text":"
@abstractmethod\ndef block_gas_limit() -> int\n

Get the block gas limit.

Returns:

None

"},{"location":"CosmPy/api/aerial/gas/#simulationgasstrategy-objects","title":"SimulationGasStrategy Objects","text":"
class SimulationGasStrategy(GasStrategy)\n

Simulation transaction gas strategy.

Arguments:

  • GasStrategy: gas strategy

"},{"location":"CosmPy/api/aerial/gas/#__init__","title":"__init__","text":"
def __init__(client: \"LedgerClient\", multiplier: Optional[float] = None)\n

Init the Simulation transaction gas strategy.

Arguments:

  • client: Ledger client
  • multiplier: multiplier, defaults to None

"},{"location":"CosmPy/api/aerial/gas/#estimate_gas_1","title":"estimate_gas","text":"
def estimate_gas(tx: Transaction) -> int\n

Get estimated transaction gas.

Arguments:

  • tx: transaction

Returns:

Estimated transaction gas

"},{"location":"CosmPy/api/aerial/gas/#block_gas_limit_1","title":"block_gas_limit","text":"
def block_gas_limit() -> int\n

Get the block gas limit.

Returns:

block gas limit

"},{"location":"CosmPy/api/aerial/gas/#offlinemessagetablestrategy-objects","title":"OfflineMessageTableStrategy Objects","text":"
class OfflineMessageTableStrategy(GasStrategy)\n

Offline message table strategy.

Arguments:

  • GasStrategy: gas strategy

"},{"location":"CosmPy/api/aerial/gas/#default_table","title":"default_table","text":"
@staticmethod\ndef default_table() -> \"OfflineMessageTableStrategy\"\n

offline message strategy default table.

Returns:

offline message default table strategy

"},{"location":"CosmPy/api/aerial/gas/#__init___1","title":"__init__","text":"
def __init__(fallback_gas_limit: Optional[int] = None,\nblock_limit: Optional[int] = None)\n

Init offline message table strategy.

Arguments:

  • fallback_gas_limit: Fallback gas limit, defaults to None
  • block_limit: Block limit, defaults to None

"},{"location":"CosmPy/api/aerial/gas/#update_entry","title":"update_entry","text":"
def update_entry(transaction_type: str, gas_limit: int)\n

Update the entry of the transaction.

Arguments:

  • transaction_type: transaction type
  • gas_limit: gas limit

"},{"location":"CosmPy/api/aerial/gas/#estimate_gas_2","title":"estimate_gas","text":"
def estimate_gas(tx: Transaction) -> int\n

Get estimated transaction gas.

Arguments:

  • tx: transaction

Returns:

Estimated transaction gas

"},{"location":"CosmPy/api/aerial/gas/#block_gas_limit_2","title":"block_gas_limit","text":"
def block_gas_limit() -> int\n

Get the block gas limit.

Returns:

block gas limit

"},{"location":"CosmPy/api/aerial/tx/","title":"Transaction","text":""},{"location":"CosmPy/api/aerial/tx/#cosmpyaerialtx","title":"cosmpy.aerial.tx","text":"

Transaction.

"},{"location":"CosmPy/api/aerial/tx/#txstate-objects","title":"TxState Objects","text":"
class TxState(Enum)\n

Transaction state.

Arguments:

  • Enum: Draft, Sealed, Final

"},{"location":"CosmPy/api/aerial/tx/#signingmode-objects","title":"SigningMode Objects","text":"
class SigningMode(Enum)\n

Signing mode.

Arguments:

  • Enum: Direct

"},{"location":"CosmPy/api/aerial/tx/#signingcfg-objects","title":"SigningCfg Objects","text":"
@dataclass\nclass SigningCfg()\n

Transaction signing configuration.

"},{"location":"CosmPy/api/aerial/tx/#direct","title":"direct","text":"
@staticmethod\ndef direct(public_key: PublicKey, sequence_num: int) -> \"SigningCfg\"\n

Transaction signing configuration using direct mode.

Arguments:

  • public_key: public key
  • sequence_num: sequence number

Returns:

Transaction signing configuration

"},{"location":"CosmPy/api/aerial/tx/#transaction-objects","title":"Transaction Objects","text":"
class Transaction()\n

Transaction.

"},{"location":"CosmPy/api/aerial/tx/#__init__","title":"__init__","text":"
def __init__()\n

Init the Transactions with transaction message, state, fee and body.

"},{"location":"CosmPy/api/aerial/tx/#state","title":"state","text":"
@property\ndef state() -> TxState\n

Get the transaction state.

Returns:

current state of the transaction

"},{"location":"CosmPy/api/aerial/tx/#msgs","title":"msgs","text":"
@property\ndef msgs()\n

Get the transaction messages.

Returns:

transaction messages

"},{"location":"CosmPy/api/aerial/tx/#fee","title":"fee","text":"
@property\ndef fee() -> Optional[str]\n

Get the transaction fee.

Returns:

transaction fee

"},{"location":"CosmPy/api/aerial/tx/#tx","title":"tx","text":"
@property\ndef tx()\n

Initialize.

Raises:

  • RuntimeError: If the transaction has not been completed.

Returns:

transaction

"},{"location":"CosmPy/api/aerial/tx/#add_message","title":"add_message","text":"
def add_message(msg: Any) -> \"Transaction\"\n

Initialize.

Arguments:

  • msg: transaction message (memo)

Raises:

  • RuntimeError: If the transaction is not in the draft state.

Returns:

transaction with message added

"},{"location":"CosmPy/api/aerial/tx/#seal","title":"seal","text":"
def seal(signing_cfgs: Union[SigningCfg, List[SigningCfg]],\nfee: str,\ngas_limit: int,\nmemo: Optional[str] = None) -> \"Transaction\"\n

Seal the transaction.

Arguments:

  • signing_cfgs: signing configs
  • fee: transaction fee
  • gas_limit: transaction gas limit
  • memo: transaction memo, defaults to None

Returns:

sealed transaction.

"},{"location":"CosmPy/api/aerial/tx/#sign","title":"sign","text":"
def sign(signer: Signer,\nchain_id: str,\naccount_number: int,\ndeterministic: bool = False) -> \"Transaction\"\n

Sign the transaction.

Arguments:

  • signer: Signer
  • chain_id: chain id
  • account_number: account number
  • deterministic: deterministic, defaults to False

Raises:

  • RuntimeError: If transaction is not sealed

Returns:

signed transaction

"},{"location":"CosmPy/api/aerial/tx/#complete","title":"complete","text":"
def complete() -> \"Transaction\"\n

Update transaction state to Final.

Returns:

transaction with updated state

"},{"location":"CosmPy/api/aerial/tx_helpers/","title":"Transaction helpers","text":""},{"location":"CosmPy/api/aerial/tx_helpers/#cosmpyaerialtx_helpers","title":"cosmpy.aerial.tx_helpers","text":"

Transaction helpers.

"},{"location":"CosmPy/api/aerial/tx_helpers/#messagelog-objects","title":"MessageLog Objects","text":"
@dataclass\nclass MessageLog()\n

Message Log.

"},{"location":"CosmPy/api/aerial/tx_helpers/#txresponse-objects","title":"TxResponse Objects","text":"
@dataclass\nclass TxResponse()\n

Transaction response.

Raises:

  • OutOfGasError: Out of gas error
  • InsufficientFeesError: Insufficient fees
  • BroadcastError: Broadcast Exception

"},{"location":"CosmPy/api/aerial/tx_helpers/#is_successful","title":"is_successful","text":"
def is_successful() -> bool\n

Check transaction is successful.

Returns:

transaction status

"},{"location":"CosmPy/api/aerial/tx_helpers/#ensure_successful","title":"ensure_successful","text":"
def ensure_successful()\n

Ensure transaction is successful.

Raises:

  • OutOfGasError: Out of gas error
  • InsufficientFeesError: Insufficient fees
  • BroadcastError: Broadcast Exception

"},{"location":"CosmPy/api/aerial/tx_helpers/#submittedtx-objects","title":"SubmittedTx Objects","text":"
class SubmittedTx()\n

Submitted transaction.

"},{"location":"CosmPy/api/aerial/tx_helpers/#__init__","title":"__init__","text":"
def __init__(client: \"LedgerClient\", tx_hash: str)\n

Init the Submitted transaction.

Arguments:

  • client: Ledger client
  • tx_hash: transaction hash

"},{"location":"CosmPy/api/aerial/tx_helpers/#tx_hash","title":"tx_hash","text":"
@property\ndef tx_hash() -> str\n

Get the transaction hash.

Returns:

transaction hash

"},{"location":"CosmPy/api/aerial/tx_helpers/#response","title":"response","text":"
@property\ndef response() -> Optional[TxResponse]\n

Get the transaction response.

Returns:

response

"},{"location":"CosmPy/api/aerial/tx_helpers/#contract_code_id","title":"contract_code_id","text":"
@property\ndef contract_code_id() -> Optional[int]\n

Get the contract code id.

Returns:

return contract code id if exist else None

"},{"location":"CosmPy/api/aerial/tx_helpers/#contract_address","title":"contract_address","text":"
@property\ndef contract_address() -> Optional[Address]\n

Get the contract address.

Returns:

return contract address if exist else None

"},{"location":"CosmPy/api/aerial/tx_helpers/#wait_to_complete","title":"wait_to_complete","text":"
def wait_to_complete(\ntimeout: Optional[Union[int, float, timedelta]] = None,\npoll_period: Optional[Union[int, float,\ntimedelta]] = None) -> \"SubmittedTx\"\n

Wait to complete the transaction.

Arguments:

  • timeout: timeout, defaults to None
  • poll_period: poll_period, defaults to None

Returns:

Submitted Transaction

"},{"location":"CosmPy/api/aerial/urls/","title":"Parsing the URL","text":""},{"location":"CosmPy/api/aerial/urls/#cosmpyaerialurls","title":"cosmpy.aerial.urls","text":"

Parsing the URL.

"},{"location":"CosmPy/api/aerial/urls/#protocol-objects","title":"Protocol Objects","text":"
class Protocol(Enum)\n

Protocol Enum.

Arguments:

  • Enum: Enum

"},{"location":"CosmPy/api/aerial/urls/#parsedurl-objects","title":"ParsedUrl Objects","text":"
@dataclass\nclass ParsedUrl()\n

Parse URL.

Returns:

Parsed URL

"},{"location":"CosmPy/api/aerial/urls/#host_and_port","title":"host_and_port","text":"
@property\ndef host_and_port() -> str\n

Get the host and port of the url.

Returns:

host and port

"},{"location":"CosmPy/api/aerial/urls/#rest_url","title":"rest_url","text":"
@property\ndef rest_url() -> str\n

Get the rest url.

Returns:

rest url

"},{"location":"CosmPy/api/aerial/urls/#parse_url","title":"parse_url","text":"
def parse_url(url: str) -> ParsedUrl\n

Initialize.

Arguments:

  • url: url

Raises:

  • RuntimeError: If url scheme is unsupported

Returns:

Parsed URL

"},{"location":"CosmPy/api/aerial/wallet/","title":"Wallet Generation","text":""},{"location":"CosmPy/api/aerial/wallet/#cosmpyaerialwallet","title":"cosmpy.aerial.wallet","text":"

Wallet Generation.

"},{"location":"CosmPy/api/aerial/wallet/#wallet-objects","title":"Wallet Objects","text":"
class Wallet(ABC, UserString)\n

Wallet Generation.

Arguments:

  • ABC: ABC abstract method
  • UserString: user string

"},{"location":"CosmPy/api/aerial/wallet/#address","title":"address","text":"
@abstractmethod\ndef address() -> Address\n

get the address of the wallet.

Returns:

None

"},{"location":"CosmPy/api/aerial/wallet/#public_key","title":"public_key","text":"
@abstractmethod\ndef public_key() -> PublicKey\n

get the public key of the wallet.

Returns:

None

"},{"location":"CosmPy/api/aerial/wallet/#signer","title":"signer","text":"
@abstractmethod\ndef signer() -> Signer\n

get the signer of the wallet.

Returns:

None

"},{"location":"CosmPy/api/aerial/wallet/#data","title":"data","text":"
@property\ndef data()\n

Get the address of the wallet.

Returns:

Address

"},{"location":"CosmPy/api/aerial/wallet/#__json__","title":"__json__","text":"
def __json__()\n

Return the address in string format.

Returns:

address in string format

"},{"location":"CosmPy/api/aerial/wallet/#localwallet-objects","title":"LocalWallet Objects","text":"
class LocalWallet(Wallet)\n

Generate local wallet.

Arguments:

  • Wallet: wallet

"},{"location":"CosmPy/api/aerial/wallet/#generate","title":"generate","text":"
@staticmethod\ndef generate(prefix: Optional[str] = None) -> \"LocalWallet\"\n

generate the local wallet.

Arguments:

  • prefix: prefix, defaults to None

Returns:

local wallet

"},{"location":"CosmPy/api/aerial/wallet/#from_mnemonic","title":"from_mnemonic","text":"
@staticmethod\ndef from_mnemonic(mnemonic: str,\nprefix: Optional[str] = None) -> \"LocalWallet\"\n

Generate local wallet from mnemonic.

Arguments:

  • mnemonic: mnemonic
  • prefix: prefix, defaults to None

Returns:

local wallet

"},{"location":"CosmPy/api/aerial/wallet/#from_unsafe_seed","title":"from_unsafe_seed","text":"
@staticmethod\ndef from_unsafe_seed(text: str,\nindex: Optional[int] = None,\nprefix: Optional[str] = None) -> \"LocalWallet\"\n

Generate local wallet from unsafe seed.

Arguments:

  • text: text
  • index: index, defaults to None
  • prefix: prefix, defaults to None

Returns:

Local wallet

"},{"location":"CosmPy/api/aerial/wallet/#__init__","title":"__init__","text":"
def __init__(private_key: PrivateKey, prefix: Optional[str] = None)\n

Init wallet with.

Arguments:

  • private_key: private key of the wallet
  • prefix: prefix, defaults to None

"},{"location":"CosmPy/api/aerial/wallet/#address_1","title":"address","text":"
def address() -> Address\n

Get the wallet address.

Returns:

Wallet address.

"},{"location":"CosmPy/api/aerial/wallet/#public_key_1","title":"public_key","text":"
def public_key() -> PublicKey\n

Get the public key of the wallet.

Returns:

public key

"},{"location":"CosmPy/api/aerial/wallet/#signer_1","title":"signer","text":"
def signer() -> PrivateKey\n

Get the signer of the wallet.

Returns:

signer

"},{"location":"CosmPy/api/aerial/client/__init__/","title":"Client functionality","text":""},{"location":"CosmPy/api/aerial/client/__init__/#cosmpyaerialclient__init__","title":"cosmpy.aerial.client.__init__","text":"

Client functionality.

"},{"location":"CosmPy/api/aerial/client/__init__/#account-objects","title":"Account Objects","text":"
@dataclass\nclass Account()\n

Account.

"},{"location":"CosmPy/api/aerial/client/__init__/#stakingposition-objects","title":"StakingPosition Objects","text":"
@dataclass\nclass StakingPosition()\n

Staking positions.

"},{"location":"CosmPy/api/aerial/client/__init__/#unbondingpositions-objects","title":"UnbondingPositions Objects","text":"
@dataclass\nclass UnbondingPositions()\n

Unbonding positions.

"},{"location":"CosmPy/api/aerial/client/__init__/#validator-objects","title":"Validator Objects","text":"
@dataclass\nclass Validator()\n

Validator.

"},{"location":"CosmPy/api/aerial/client/__init__/#coin-objects","title":"Coin Objects","text":"
@dataclass\nclass Coin()\n

Coins.

"},{"location":"CosmPy/api/aerial/client/__init__/#stakingsummary-objects","title":"StakingSummary Objects","text":"
@dataclass\nclass StakingSummary()\n

Get the staking summary.

"},{"location":"CosmPy/api/aerial/client/__init__/#total_staked","title":"total_staked","text":"
@property\ndef total_staked() -> int\n

Get the total staked amount.

"},{"location":"CosmPy/api/aerial/client/__init__/#total_rewards","title":"total_rewards","text":"
@property\ndef total_rewards() -> int\n

Get the total rewards.

"},{"location":"CosmPy/api/aerial/client/__init__/#total_unbonding","title":"total_unbonding","text":"
@property\ndef total_unbonding() -> int\n

total unbonding.

"},{"location":"CosmPy/api/aerial/client/__init__/#ledgerclient-objects","title":"LedgerClient Objects","text":"
class LedgerClient()\n

Ledger client.

"},{"location":"CosmPy/api/aerial/client/__init__/#__init__","title":"__init__","text":"
def __init__(cfg: NetworkConfig,\nquery_interval_secs: int = DEFAULT_QUERY_INTERVAL_SECS,\nquery_timeout_secs: int = DEFAULT_QUERY_TIMEOUT_SECS)\n

Init ledger client.

Arguments:

  • cfg: Network configurations
  • query_interval_secs: int. optional interval int seconds
  • query_timeout_secs: int. optional interval int seconds

"},{"location":"CosmPy/api/aerial/client/__init__/#network_config","title":"network_config","text":"
@property\ndef network_config() -> NetworkConfig\n

Get the network config.

Returns:

network config

"},{"location":"CosmPy/api/aerial/client/__init__/#gas_strategy","title":"gas_strategy","text":"
@property\ndef gas_strategy() -> GasStrategy\n

Get gas strategy.

Returns:

gas strategy

"},{"location":"CosmPy/api/aerial/client/__init__/#gas_strategy_1","title":"gas_strategy","text":"
@gas_strategy.setter\ndef gas_strategy(strategy: GasStrategy)\n

Set gas strategy.

Arguments:

  • strategy: strategy

Raises:

  • RuntimeError: Invalid strategy must implement GasStrategy interface

"},{"location":"CosmPy/api/aerial/client/__init__/#query_account","title":"query_account","text":"
def query_account(address: Address) -> Account\n

Query account.

Arguments:

  • address: address

Raises:

  • RuntimeError: Unexpected account type returned from query

Returns:

account details

"},{"location":"CosmPy/api/aerial/client/__init__/#query_params","title":"query_params","text":"
def query_params(subspace: str, key: str) -> Any\n

Query Prams.

Arguments:

  • subspace: subspace
  • key: key

Returns:

Query params

"},{"location":"CosmPy/api/aerial/client/__init__/#query_bank_balance","title":"query_bank_balance","text":"
def query_bank_balance(address: Address, denom: Optional[str] = None) -> int\n

Query bank balance.

Arguments:

  • address: address
  • denom: denom, defaults to None

Returns:

bank balance

"},{"location":"CosmPy/api/aerial/client/__init__/#query_bank_all_balances","title":"query_bank_all_balances","text":"
def query_bank_all_balances(address: Address) -> List[Coin]\n

Query bank all balances.

Arguments:

  • address: address

Returns:

bank all balances

"},{"location":"CosmPy/api/aerial/client/__init__/#send_tokens","title":"send_tokens","text":"
def send_tokens(destination: Address,\namount: int,\ndenom: str,\nsender: Wallet,\nmemo: Optional[str] = None,\ngas_limit: Optional[int] = None) -> SubmittedTx\n

Send tokens.

Arguments:

  • destination: destination address
  • amount: amount
  • denom: denom
  • sender: sender
  • memo: memo, defaults to None
  • gas_limit: gas limit, defaults to None

Returns:

prepare and broadcast the transaction and transaction details

"},{"location":"CosmPy/api/aerial/client/__init__/#query_validators","title":"query_validators","text":"
def query_validators(\nstatus: Optional[ValidatorStatus] = None) -> List[Validator]\n

Query validators.

Arguments:

  • status: validator status, defaults to None

Returns:

List of validators

"},{"location":"CosmPy/api/aerial/client/__init__/#query_staking_summary","title":"query_staking_summary","text":"
def query_staking_summary(address: Address) -> StakingSummary\n

Query staking summary.

Arguments:

  • address: address

Returns:

staking summary

"},{"location":"CosmPy/api/aerial/client/__init__/#delegate_tokens","title":"delegate_tokens","text":"
def delegate_tokens(validator: Address,\namount: int,\nsender: Wallet,\nmemo: Optional[str] = None,\ngas_limit: Optional[int] = None) -> SubmittedTx\n

Delegate tokens.

Arguments:

  • validator: validator address
  • amount: amount
  • sender: sender
  • memo: memo, defaults to None
  • gas_limit: gas limit, defaults to None

Returns:

prepare and broadcast the transaction and transaction details

"},{"location":"CosmPy/api/aerial/client/__init__/#redelegate_tokens","title":"redelegate_tokens","text":"
def redelegate_tokens(current_validator: Address,\nnext_validator: Address,\namount: int,\nsender: Wallet,\nmemo: Optional[str] = None,\ngas_limit: Optional[int] = None) -> SubmittedTx\n

Redelegate tokens.

Arguments:

  • current_validator: current validator address
  • next_validator: next validator address
  • amount: amount
  • sender: sender
  • memo: memo, defaults to None
  • gas_limit: gas limit, defaults to None

Returns:

prepare and broadcast the transaction and transaction details

"},{"location":"CosmPy/api/aerial/client/__init__/#undelegate_tokens","title":"undelegate_tokens","text":"
def undelegate_tokens(validator: Address,\namount: int,\nsender: Wallet,\nmemo: Optional[str] = None,\ngas_limit: Optional[int] = None) -> SubmittedTx\n

Undelegate tokens.

Arguments:

  • validator: validator
  • amount: amount
  • sender: sender
  • memo: memo, defaults to None
  • gas_limit: gas limit, defaults to None

Returns:

prepare and broadcast the transaction and transaction details

"},{"location":"CosmPy/api/aerial/client/__init__/#claim_rewards","title":"claim_rewards","text":"
def claim_rewards(validator: Address,\nsender: Wallet,\nmemo: Optional[str] = None,\ngas_limit: Optional[int] = None) -> SubmittedTx\n

claim rewards.

Arguments:

  • validator: validator
  • sender: sender
  • memo: memo, defaults to None
  • gas_limit: gas limit, defaults to None

Returns:

prepare and broadcast the transaction and transaction details

"},{"location":"CosmPy/api/aerial/client/__init__/#estimate_gas_for_tx","title":"estimate_gas_for_tx","text":"
def estimate_gas_for_tx(tx: Transaction) -> int\n

Estimate gas for transaction.

Arguments:

  • tx: transaction

Returns:

Estimated gas for transaction

"},{"location":"CosmPy/api/aerial/client/__init__/#estimate_fee_from_gas","title":"estimate_fee_from_gas","text":"
def estimate_fee_from_gas(gas_limit: int) -> str\n

Estimate fee from gas.

Arguments:

  • gas_limit: gas limit

Returns:

Estimated fee for transaction

"},{"location":"CosmPy/api/aerial/client/__init__/#estimate_gas_and_fee_for_tx","title":"estimate_gas_and_fee_for_tx","text":"
def estimate_gas_and_fee_for_tx(tx: Transaction) -> Tuple[int, str]\n

Estimate gas and fee for transaction.

Arguments:

  • tx: transaction

Returns:

estimate gas, fee for transaction

"},{"location":"CosmPy/api/aerial/client/__init__/#wait_for_query_tx","title":"wait_for_query_tx","text":"
def wait_for_query_tx(tx_hash: str,\ntimeout: Optional[timedelta] = None,\npoll_period: Optional[timedelta] = None) -> TxResponse\n

Wait for query transaction.

Arguments:

  • tx_hash: transaction hash
  • timeout: timeout, defaults to None
  • poll_period: poll_period, defaults to None

Raises:

  • QueryTimeoutError: timeout

Returns:

transaction response

"},{"location":"CosmPy/api/aerial/client/__init__/#query_tx","title":"query_tx","text":"
def query_tx(tx_hash: str) -> TxResponse\n

query transaction.

Arguments:

  • tx_hash: transaction hash

Raises:

  • NotFoundError: Tx details not found
  • grpc.RpcError: RPC connection issue

Returns:

query response

"},{"location":"CosmPy/api/aerial/client/__init__/#simulate_tx","title":"simulate_tx","text":"
def simulate_tx(tx: Transaction) -> int\n

simulate transaction.

Arguments:

  • tx: transaction

Raises:

  • RuntimeError: Unable to simulate non final transaction

Returns:

gas used in transaction

"},{"location":"CosmPy/api/aerial/client/__init__/#broadcast_tx","title":"broadcast_tx","text":"
def broadcast_tx(tx: Transaction) -> SubmittedTx\n

Broadcast transaction.

Arguments:

  • tx: transaction

Returns:

Submitted transaction

"},{"location":"CosmPy/api/aerial/client/bank/","title":"Bank send message","text":""},{"location":"CosmPy/api/aerial/client/bank/#cosmpyaerialclientbank","title":"cosmpy.aerial.client.bank","text":"

Bank send message.

"},{"location":"CosmPy/api/aerial/client/bank/#create_bank_send_msg","title":"create_bank_send_msg","text":"
def create_bank_send_msg(from_address: Address, to_address: Address,\namount: int, denom: str) -> MsgSend\n

Create bank send message.

Arguments:

  • from_address: from address
  • to_address: to address
  • amount: amount
  • denom: denom

Returns:

bank send message

"},{"location":"CosmPy/api/aerial/client/distribution/","title":"Distribution","text":""},{"location":"CosmPy/api/aerial/client/distribution/#cosmpyaerialclientdistribution","title":"cosmpy.aerial.client.distribution","text":"

Distribution.

"},{"location":"CosmPy/api/aerial/client/distribution/#create_withdraw_delegator_reward","title":"create_withdraw_delegator_reward","text":"
def create_withdraw_delegator_reward(delegator: Address, validator: Address)\n

Create withdraw delegator reward.

Arguments:

  • delegator: delegator address
  • validator: validator address

Returns:

withdraw delegator reward message

"},{"location":"CosmPy/api/aerial/client/staking/","title":"Staking functionality","text":""},{"location":"CosmPy/api/aerial/client/staking/#cosmpyaerialclientstaking","title":"cosmpy.aerial.client.staking","text":"

Staking functionality.

"},{"location":"CosmPy/api/aerial/client/staking/#validatorstatus-objects","title":"ValidatorStatus Objects","text":"
class ValidatorStatus(Enum)\n

Validator status.

"},{"location":"CosmPy/api/aerial/client/staking/#from_proto","title":"from_proto","text":"
@classmethod\ndef from_proto(cls, value: int) -> \"ValidatorStatus\"\n

Get the validator status from proto.

Arguments:

  • value: value

Raises:

  • RuntimeError: Unable to decode validator status

Returns:

Validator status

"},{"location":"CosmPy/api/aerial/client/staking/#create_delegate_msg","title":"create_delegate_msg","text":"
def create_delegate_msg(delegator: Address, validator: Address, amount: int,\ndenom: str) -> MsgDelegate\n

Create delegate message.

Arguments:

  • delegator: delegator
  • validator: validator
  • amount: amount
  • denom: denom

Returns:

Delegate message

"},{"location":"CosmPy/api/aerial/client/staking/#create_redelegate_msg","title":"create_redelegate_msg","text":"
def create_redelegate_msg(delegator_address: Address,\nvalidator_src_address: Address,\nvalidator_dst_address: Address, amount: int,\ndenom: str) -> MsgBeginRedelegate\n

Create redelegate message.

Arguments:

  • delegator_address: delegator address
  • validator_src_address: source validation address
  • validator_dst_address: destination validation address
  • amount: amount
  • denom: denom

Returns:

Redelegate message

"},{"location":"CosmPy/api/aerial/client/staking/#create_undelegate_msg","title":"create_undelegate_msg","text":"
def create_undelegate_msg(delegator_address: Address,\nvalidator_address: Address, amount: int,\ndenom: str) -> MsgUndelegate\n

Create undelegate message.

Arguments:

  • delegator_address: delegator address
  • validator_address: validator address
  • amount: amount
  • denom: denom

Returns:

Undelegate message

"},{"location":"CosmPy/api/aerial/client/utils/","title":"Helper functions","text":""},{"location":"CosmPy/api/aerial/client/utils/#cosmpyaerialclientutils","title":"cosmpy.aerial.client.utils","text":"

Helper functions.

"},{"location":"CosmPy/api/aerial/client/utils/#prepare_and_broadcast_basic_transaction","title":"prepare_and_broadcast_basic_transaction","text":"
def prepare_and_broadcast_basic_transaction(\nclient: \"LedgerClient\",\ntx: \"Transaction\",\nsender: \"Wallet\",\naccount: Optional[\"Account\"] = None,\ngas_limit: Optional[int] = None,\nmemo: Optional[str] = None) -> SubmittedTx\n

Prepare and broadcast basic transaction.

Arguments:

  • client: Ledger client
  • tx: The transaction
  • sender: The transaction sender
  • account: The account
  • gas_limit: The gas limit
  • memo: Transaction memo, defaults to None

Returns:

broadcast transaction

"},{"location":"CosmPy/api/aerial/client/utils/#ensure_timedelta","title":"ensure_timedelta","text":"
def ensure_timedelta(interval: Union[int, float, timedelta]) -> timedelta\n

Return timedelta for interval.

Arguments:

  • interval: timedelta or seconds in int or float

Returns:

timedelta

"},{"location":"CosmPy/api/aerial/client/utils/#get_paginated","title":"get_paginated","text":"
def get_paginated(\ninitial_request: Any,\nrequest_method: Callable,\npages_limit: int = 0,\nper_page_limit: Optional[int] = DEFAULT_PER_PAGE_LIMIT) -> List[Any]\n

Get pages for specific request.

Arguments:

  • initial_request: request supports pagination
  • request_method: function to perform request
  • pages_limit: max number of pages to return. default - 0 unlimited
  • per_page_limit: Optional int: amount of records per one page. default is None, determined by server

Returns:

List of responses

"},{"location":"CosmPy/api/aerial/contract/__init__/","title":"Cosmwasm contract functionality","text":""},{"location":"CosmPy/api/aerial/contract/__init__/#cosmpyaerialcontract__init__","title":"cosmpy.aerial.contract.__init__","text":"

cosmwasm contract functionality.

"},{"location":"CosmPy/api/aerial/contract/__init__/#ledgercontract-objects","title":"LedgerContract Objects","text":"
class LedgerContract(UserString)\n

Ledger contract.

"},{"location":"CosmPy/api/aerial/contract/__init__/#__init__","title":"__init__","text":"
def __init__(path: Optional[str],\nclient: LedgerClient,\naddress: Optional[Address] = None,\ndigest: Optional[bytes] = None,\nschema_path: Optional[str] = None,\ncode_id: Optional[int] = None)\n

Initialize the Ledger contract.

Arguments:

  • path: Path
  • client: Ledger client
  • address: address, defaults to None
  • digest: digest, defaults to None
  • schema_path: path to contract schema, defaults to None
  • code_id: optional int. code id of the contract stored

"},{"location":"CosmPy/api/aerial/contract/__init__/#path","title":"path","text":"
@property\ndef path() -> Optional[str]\n

Get contract path.

Returns:

contract path

"},{"location":"CosmPy/api/aerial/contract/__init__/#digest","title":"digest","text":"
@property\ndef digest() -> Optional[bytes]\n

Get the contract digest.

Returns:

contract digest

"},{"location":"CosmPy/api/aerial/contract/__init__/#code_id","title":"code_id","text":"
@property\ndef code_id() -> Optional[int]\n

Get the code id.

Returns:

code id

"},{"location":"CosmPy/api/aerial/contract/__init__/#address","title":"address","text":"
@property\ndef address() -> Optional[Address]\n

Get the contract address.

Returns:

contract address

"},{"location":"CosmPy/api/aerial/contract/__init__/#store","title":"store","text":"
def store(sender: Wallet,\ngas_limit: Optional[int] = None,\nmemo: Optional[str] = None) -> int\n

Store the contract.

Arguments:

  • sender: sender wallet address
  • gas_limit: transaction gas limit, defaults to None
  • memo: transaction memo, defaults to None

Raises:

  • RuntimeError: Runtime error

Returns:

code id

"},{"location":"CosmPy/api/aerial/contract/__init__/#instantiate","title":"instantiate","text":"
def instantiate(args: Any,\nsender: Wallet,\nlabel: Optional[str] = None,\ngas_limit: Optional[int] = None,\nadmin_address: Optional[Address] = None,\nfunds: Optional[str] = None) -> Address\n

Instantiate the contract.

Arguments:

  • args: args
  • sender: sender wallet address
  • label: label, defaults to None
  • gas_limit: transaction gas limit, defaults to None
  • admin_address: admin address, defaults to None
  • funds: funds, defaults to None

Raises:

  • RuntimeError: Unable to extract contract code id

Returns:

contract address

"},{"location":"CosmPy/api/aerial/contract/__init__/#upgrade","title":"upgrade","text":"
def upgrade(args: Any,\nsender: Wallet,\nnew_path: str,\ngas_limit: Optional[int] = None) -> SubmittedTx\n

Store new contract code and migrate the current contract address.

Arguments:

  • args: args
  • sender: sender wallet address
  • new_path: path to new contract
  • gas_limit: transaction gas limit, defaults to None

Raises:

  • RuntimeError: Unable to extract contract code id

Returns:

contract address

"},{"location":"CosmPy/api/aerial/contract/__init__/#migrate","title":"migrate","text":"
def migrate(args: Any,\nsender: Wallet,\nnew_code_id: int,\ngas_limit: Optional[int] = None) -> SubmittedTx\n

Migrate the current contract address to new code id.

Arguments:

  • args: args
  • sender: sender wallet address
  • new_code_id: Code id of the newly deployed contract
  • gas_limit: transaction gas limit, defaults to None

Raises:

  • RuntimeError: Unable to extract contract code id

Returns:

contract address

"},{"location":"CosmPy/api/aerial/contract/__init__/#deploy","title":"deploy","text":"
def deploy(args: Any,\nsender: Wallet,\nlabel: Optional[str] = None,\nstore_gas_limit: Optional[int] = None,\ninstantiate_gas_limit: Optional[int] = None,\nadmin_address: Optional[Address] = None,\nfunds: Optional[str] = None) -> Address\n

Deploy the contract.

Arguments:

  • args: args
  • sender: sender address
  • label: label, defaults to None
  • store_gas_limit: store gas limit, defaults to None
  • instantiate_gas_limit: instantiate gas limit, defaults to None
  • admin_address: admin address, defaults to None
  • funds: funds, defaults to None

Returns:

instantiate contract details

"},{"location":"CosmPy/api/aerial/contract/__init__/#execute","title":"execute","text":"
def execute(args: Any,\nsender: Wallet,\ngas_limit: Optional[int] = None,\nfunds: Optional[str] = None) -> SubmittedTx\n

execute the contract.

Arguments:

  • args: args
  • sender: sender address
  • gas_limit: transaction gas limit, defaults to None
  • funds: funds, defaults to None

Raises:

  • RuntimeError: Contract appears not to be deployed currently

Returns:

transaction details broadcast

"},{"location":"CosmPy/api/aerial/contract/__init__/#query","title":"query","text":"
def query(args: Any) -> Any\n

Query on contract.

Arguments:

  • args: args

Raises:

  • RuntimeError: Contract appears not to be deployed currently

Returns:

query result

"},{"location":"CosmPy/api/aerial/contract/__init__/#data","title":"data","text":"
@property\ndef data()\n

Get the contract address.

Returns:

contract address

"},{"location":"CosmPy/api/aerial/contract/__init__/#__json__","title":"__json__","text":"
def __json__()\n

Get the contract details in json.

Returns:

contract details in json

"},{"location":"CosmPy/api/aerial/contract/cosmwasm/","title":"Cosmwasm contract store, instantiate, execute messages","text":""},{"location":"CosmPy/api/aerial/contract/cosmwasm/#cosmpyaerialcontractcosmwasm","title":"cosmpy.aerial.contract.cosmwasm","text":"

Cosmwasm contract store, instantiate, execute messages.

"},{"location":"CosmPy/api/aerial/contract/cosmwasm/#create_cosmwasm_store_code_msg","title":"create_cosmwasm_store_code_msg","text":"
def create_cosmwasm_store_code_msg(contract_path: str,\nsender_address: Address) -> MsgStoreCode\n

Create cosmwasm store code message.

Arguments:

  • contract_path: contract path
  • sender_address: sender address

Returns:

cosmwasm store code message

"},{"location":"CosmPy/api/aerial/contract/cosmwasm/#create_cosmwasm_instantiate_msg","title":"create_cosmwasm_instantiate_msg","text":"
def create_cosmwasm_instantiate_msg(\ncode_id: int,\nargs: Any,\nlabel: str,\nsender_address: Address,\nfunds: Optional[str] = None,\nadmin_address: Optional[Address] = None) -> MsgInstantiateContract\n

Create cosmwasm instantiate message.

Arguments:

  • code_id: code id
  • args: args
  • label: label
  • sender_address: sender address
  • funds: funds, defaults to None
  • admin_address: admin address, defaults to None

Returns:

cosmwasm instantiate message

"},{"location":"CosmPy/api/aerial/contract/cosmwasm/#create_cosmwasm_migrate_msg","title":"create_cosmwasm_migrate_msg","text":"
def create_cosmwasm_migrate_msg(code_id: int, args: Any,\ncontract_address: Address,\nsender_address: Address) -> MsgMigrateContract\n

Create cosmwasm migrate message.

Arguments:

  • code_id: code id
  • args: args
  • contract_address: sender address
  • sender_address: sender address

Returns:

cosmwasm migrate message

"},{"location":"CosmPy/api/aerial/contract/cosmwasm/#create_cosmwasm_execute_msg","title":"create_cosmwasm_execute_msg","text":"
def create_cosmwasm_execute_msg(\nsender_address: Address,\ncontract_address: Address,\nargs: Any,\nfunds: Optional[str] = None) -> MsgExecuteContract\n

Create cosmwasm execute message.

Arguments:

  • sender_address: sender address
  • contract_address: contract address
  • args: args
  • funds: funds, defaults to None

Returns:

cosmwasm execute message

"},{"location":"Jenesis/","title":"Introduction","text":"

Jenesis is a command line tool for rapid contract and service development for the Fetch.ai blockchain ecosystem and other CosmWasm-enabled blockchains.

"},{"location":"Jenesis/#system-requirements","title":"System Requirements","text":"

Jenesis currently requires:

  • OS: Linux, MacOS
  • Python: 3.8 to 3.10
  • Docker: 20.10.22 or higher recommended
  • git: Any
"},{"location":"Jenesis/#installation","title":"Installation","text":"

Install via PyPI:

pip install jenesis\n
"},{"location":"Jenesis/#getting-started","title":"Getting started","text":"

There are multiple commands integrated into jenesis that allow you to perform a variety of tasks these commands are:

  • new
  • init
  • add
  • update
  • attach
  • compile
  • keys
  • deploy
  • run
  • shell
  • network
"},{"location":"Jenesis/#create-a-new-project","title":"Create a new project","text":"

Create a project using the new command

jenesis new my_project [--profile my_profile] [--network network_name]\n

This will create a new directory called my_project. You can use --profile and --network optional arguments; when they aren't used, profile and network will be set to testing and fetchai-testnet respectively. Inside this directory a jenesis.toml file will be created containing the following information:

[project]\nname = \"my_project\"\nauthors = [ \"Alice Tyler <alice@mail.com>\"]\nkeyring_backend = \"os\"\n[profile.my_profile]\ndefault = true\n[profile.my_profile.network]\nname = \"fetchai-testnet\"\nchain_id = \"dorado-1\"\nfee_minimum_gas_price = 5000000000\nfee_denomination = \"atestfet\"\nstaking_denomination = \"atestfet\"\nurl = \"grpc+https://grpc-dorado.fetch.ai\"\nfaucet_url = \"https://faucet-dorado.fetch.ai\"\nis_local = false\n[profile.my_profile.contracts]\n

The project name is the argument passed to the new command while the authors field is populated by querying the user's GitHub username and email address. The profile's network will be filled with the relevant configuration variables. The contracts field will remain empty until new contracts are added. This my_profile profile will be set as the default profile, this means that every time you use a jenesis command without specifying a profile, my_profile will be used.

An empty contracts folder will also be created inside my_project directory that will eventually contain all the information needed to compile and deploy the desired contracts.

The init command is similar to the new command, but in this case, you won't need a project name argument since this command is intended to run inside an existing project directory.

jenesis init [--profile my_profile] [--network network_name]\n

This command will create the same files and folders inside your project directory as the ones described for the new command.

If using a cargo workspace, you just need to navigate to the top level of your project and run the init command shown above. This will create the jenesis.toml configuration file inside your workspace including all the relevant information from existing contracts.

"},{"location":"Jenesis/#configure-a-network","title":"Configure a network","text":"

By default, jenesis will configure the project to run on the latest stable Fetch.ai testnet. Use fetchai-mainnet to configure for the Fetch.ai mainnet or directly edit the jenesis.toml file to configure for other networks.

To test on a local node, pass the argument --network fetchai-localnode when creating a project:

jenesis new my_project --network fetchai-localnode\n
or
jenesis init --network fetchai-localnode\n

The configuration can be found under the network heading in the jenesis.toml file and can be changed as desired:

[profile.testing.network]\nname = \"fetchai-localnode\"\nchain_id = \"localnode\"\nfee_minimum_gas_price = 5000000000\nfee_denomination = \"atestfet\"\nstaking_denomination = \"atestfet\"\nurl = \"grpc+http://127.0.0.1:9090/\"\nis_local = true\nkeep_running = false\ncli_binary = \"fetchd\"\nvalidator_key_name = \"validator\"\nmnemonic = \"gap bomb bulk border original scare assault pelican resemble found laptop skin gesture height inflict clinic reject giggle hurdle bubble soldier hurt moon hint\"\npassword = \"12345678\"\nmoniker = \"test-node\"\ngenesis_accounts = [ \"fetch1vas6cc9650z0s08230ytqjphgzl5tcq9crqhhu\",]\ntimeout_commit = \"5s\"\ndebug_trace = true\n
In particular, to fund some accounts for testing, replace the genesis_accounts field with the addresses to be funded.

When running any of the commands deploy, run, shell, and attach, jenesis will check for a currently running local node, and if there is none, a new one will be created in a docker container. If you wish to keep a local node running, you need to set the keep_running parameter to true. Otherwise, nodes will be stopped after any of the command mentioned above finish running.

At any time, you can start or stop a local node by running:

jenesis network start [--profile my_profile]\n
or
jenesis network stop [--profile my_profile]\n

To view the logs from the local node, run:

jenesis network logs [--profile my_profile]\n

"},{"location":"Jenesis/add-contracts/","title":"Add contract templates","text":"

Once you have successfully created your project, you can add contract templates. You first need to navigate to your project's directory and run the following command:

jenesis add contract <template> <contract_name>\n
You can find all the contract templates available in Jenesis Templates. An example of how to add the template cw20-base with the name my_token is given below:

jenesis add contract cw20-base my_token\n

If you need multiple deployments of the same contract, you can use the --deployments or -d flag to specify multiple deployments and name them.

jenesis add contract <template> <contract_name> [--deployments <deployments>]\n
Jenesis will add the deployments to all profiles for the specified contract. In the example below, token_1 and token_2 deployments have been added. This will allow you to deploy my_token contract with two different configurations. You can add as many deployments as you wish.
jenesis add contract cw20-base my_token -d token_1 token_2\n

If no deployments are selected when adding a contract, the default deployment name will be equal to the contract name.

This add contract command will add a contract template to your jenesis project inside contracts/my_token/ folder. It will also update the jenesis.toml configuration file with the contract information.

[profile.testing.contracts.token_1]\nname = \"token_1\"\ncontract = \"my_token\"\nnetwork = \"fetchai-testnet\"\ndeployer_key = \"\"\ninit_funds = \"\"\n[profile.testing.contracts.token_2]\nname = \"token_2\"\ncontract = \"my_token\"\nnetwork = \"fetchai-testnet\"\ndeployer_key = \"\"\ninit_funds = \"\"\n[profile.testing.contracts.token_1.init]\n[profile.testing.contracts.token_2.init]\n

The deployer_key field can be manually specified, you can choose any private key locally available to deploy any specific contract. You can also leave this field empty since the deploy command has an optional argument to deploy all contracts inside a specified profile with the same key, overriding this deployer_key argument in the jenesis.toml file. See deploy contracts for more information.

Finally, the init section contains the parameters needed in the instantiation message for this contract to be deployed. The required parameters are taken from the schema file inside the contracts directory. Since this contract template doesn't include a schema, it will be generated when compiling the my_token contract loading the init fields to the jenesis.toml file. You will need to manually add the values for these parameters in their correct variable type, which are listed on the schema file. For this my_token contract, we need to fill the following init fields for each deployment after compiling. Here is an example:

[profile.testing.contracts.token_1.init]\ndecimals = 6\nname = \"my_token_name\"\nsymbol = \"SYMBOL\"\ninitial_balances = [{ address = \"fetch1d25ap9danl4726uk2nt307y630v87h3h2vq6pl\", amount =  \"5000\"}]\n\n[profile.testing.contracts.token_2.init]\ndecimals = 6\nname = \"my_token_name_2\"\nsymbol = \"SYMBOL\"\ninitial_balances = [{ address = \"fetch1d25ap9danl4726uk2nt307y630v87h3h2vq6pl\", amount =  \"2000\"}]\n

If your contract requires nested instantiation messages you may add fields following this structure:

[profile.testing.contracts.example-nested-contract.init]\nprice = {amount = 1000, denom = DLS}\ninfo = {performance = {max_speed = 200, unit = kph}, fuel = {consumption = 7, unit = kmpl}}\n

NOTE: Before editing the jenesis.toml configuration file with the desired deployer_key and init parameters, make sure to first compile your contract. All configuration parameters will restart every time a contract is compiled if the schema has changed.

You can also add contracts manually by copying and pasting the contract directory from another project you may have, however, they need to follow the same directory structure as the starter template mentioned above.

When you add a contract manually, you need to update the jenesis.toml file with the contract information by running:

jenesis update\n
The update command will automatically detect which contract is missing in the jenesis.toml configuration file by revising the contracts directory.

"},{"location":"Jenesis/add-contracts/#add-contract-deployments","title":"Add contract deployments","text":"

You can also add further deployments for a given contract by specifying the contract name and the deployment name. If we want to add a third token called token_3 using my_token contract, we can run:

jenesis add deployment my_token token_3\n
This will automatically create another deployment entry called token_3.

"},{"location":"Jenesis/add-contracts/#attach-deployed-contracts","title":"Attach deployed contracts","text":"

If you add a contract into the project's contract folder that has already been deployed in the network, you can attach the deployment to your project for future interaction using the attach command.

To add a deployment to yout project you can run:

jenesis add contract cw20-base my_token -d token_1\n

Then compile the contract:

jenesis compile\n

To attach the contract, you will need to specify the deployment's name and address. You can optionally specify the profile where you wish to insert the contract into. If this is not specified, the deployment will be attached to the default profile, which is the first profile created in your project, unless the default settings are manually changed.

jenesis attach token_1 fetch18xs97q6h9zgh4sz730a42pp0dqa9sh4eef7eutfkv69q3v2y3x8s72pkua\n

This will add the relevant deployment information into a jenesis.lock file and you will now be able to interact with token_1 using contract interactions.

"},{"location":"Jenesis/add-profile/","title":"Add profiles","text":"

You can add more profiles than the one specified using the new command by running the following add profile command:

jenesis add profile my_second_profile\n
By default, the profile's network will be set to fetchai-testnet, but you can specify it using the --network optional argument. The following will be added to the existing information in your jenesis.toml file:

[profile.my_second_profile.network]\nname = \"fetchai-testnet\"\nchain_id = \"dorado-1\"\nfee_minimum_gas_price = 5000000000\nfee_denomination = \"atestfet\"\nstaking_denomination = \"atestfet\"\nurl = \"grpc+https://grpc-dorado.fetch.ai\"\nfaucet_url = \"https://faucet-dorado.fetch.ai\"\nis_local = false\n[profile.my_second_profile.contracts]\n

Currently available network configurations are fetchai-testnet, fetchai-mainnet, and fetchai-localnode, but Jenesis is easily configurable for other networks by directly editing the jenesis.toml file.

"},{"location":"Jenesis/compile-contracts/","title":"Compile contracts","text":""},{"location":"Jenesis/compile-contracts/#compile-contracts","title":"Compile contracts","text":"

Compile your contracts by running the following command inside your project directory:

jenesis compile [--optimize] [--rebuild] [--no-log]\n
This will compile all packages in your project's contracts directory and output the wasm code under the artifacts directory. If using a cargo workspace, jenesis will automatically detect this and the compiled contracts will appear in the contracts/artifacts/. Otherwise, they will go to the artifacts directory under the individual contracts.

By default, the contracts are simply compiled and not optimized. For an optimized build, use the flag --optimize or -o. To force a rebuild, use the flag --rebuild or -r. To suppress contract compilation logs, use the flag --no-log. In case of compilation failure, the logs will show by default.

Note: jenesis compile requires that docker is running and configured with permissions for your user.

"},{"location":"Jenesis/deploy-contracts/","title":"Deploy contracts","text":"

Once you have successfully compiled your contracts, make sure to fill out the necessary instantiation message information under the init field in the jenesis.toml file.

*Note: jenesis deploy currently requires that each contract's directory name matches the .wasm file name under the artifacts directory.

To deploy all the contracts inside a profile you have two options:

  1. Fill the deployer_key field for each contract inside the jenesis.toml file (keys can be different for each contract) and run the following command:

jenesis deploy [--profile profile_name]\n
Each contract inside the specified profile will be deployed with the specified key.

  1. Simply specify a certain key as an argument of the deploy command:
jenesis deploy key_name [--profile profile_name]\n

The deployer_key field will be ignored in this case and all contracts inside the specified profile will be deployed using the key key_name.

After running either of the commands mentioned above, all the deployment information will be saved in the jenesis.lock file inside your project's directory

[profile.testing.my_first_contract]\nchecksum = \"ecf640a7512be3777c72ec42aff01fdb22897b71953011af3c41ee5dbf3d3bc5\"\ndigest = \"be4a4bdfeb4ed8f504c7b7ac84e31ad3876627398a6586b49cac586633af8b85\"\naddress = \"fetch16l239ggyr4z7pvsxec0ervlyw03mn6pz62l9ss6la94cf06awv0q36cq7u\"\ncode_id = 2594\n
"},{"location":"Jenesis/deploy-contracts/#deploy-contracts-that-depend-on-other-deployments","title":"Deploy contracts that depend on other deployments","text":"

You can point to other contract addresses in any contract's instantiation message if required. For example: if you have contracts A, B, and C within your project, but contract A requires contract's B deployment address in its instantiation message and contract B requires contract's C deployment address, they will need to be deployed in the following order: C, B, A. In order to provide this information to Jenesis you will need to specify where exactly these contract addresses need to be inserted inside the instantiation messages. You can do this by writing the $ symbol followed by the contract name in the corresponding field in the init parameters:

[profile.testing.contracts.A.init]\nname = \"A\"\ntoken_contract_address = \"$B\"\n[profile.testing.contracts.B.init]\ntoken_name = \"my_token\"\nliquidity_contract_address = \"$C\"\n[profile.testing.contracts.C.init]\ncount = 5\n

Finally, Jenesis will detect this information and deploy the contracts in the correct order: C, B, A.

"},{"location":"Jenesis/keys/","title":"Keys","text":"

With the keys command you can either list all the locally available keys or show the address of a specific key. To list all the keys available run the following command:

jenesis keys list\n

To look up the address for a specified key you can use the show command and pass the key name as an argument:

jenesis keys show my_key\n
To access other key functionalities such as adding new keys, looking up an address, and recovering keys you can use fetchd CLI - Managing Keys

"},{"location":"Jenesis/use-contracts/","title":"Contract Interaction","text":"

You can interact with your project's contracts by using the shell or run commands.

"},{"location":"Jenesis/use-contracts/#previous-steps","title":"Previous steps","text":"

To reproduce the examples in this document, add and compile a basic starter contract and a cw20 token contract to your project with the following commands:

jenesis add contract starter my_first_contract -d deployment_1\njenesis add contract cw20-base my_token -d token_1\njenesis compile\n

For more contract template examples visit Jenesis Templates

"},{"location":"Jenesis/use-contracts/#interactive-shell","title":"Interactive Shell","text":"

To open a shell where you can easily interact with your contracts, run:

jenesis shell\n
If a profile is not selected, the default profile will be selected automatically. You can specify any profile using the --profile optional argument:

jenesis shell --profile my_profile\n

You will observe the following text indicating the available contracts in your project.

Network: fetchai-testnet\nDetecting contracts...\nC deployment_1\nC token_1\nDetecting contracts...complete\n

NOTE: jenesis shell currently requires that contract names use accepted python variable names. For example, using token-1 instead of token_1 will generate an error when trying to interact with it.

In this case, we can see that deployment_1 and token_1 deployments are available for this project. If these contracts have been already deployed you can directly interact with them by performing contract querys and executions such as:

>>> deployment_1.query(args = {'msg_name': {...}}\n>>> deployment_1.execute(args = {'msg_name': {...}}\n

A ledger client (ledger) and your locally stored wallet keys will also be available in the shell. For example, if you have a local key named alice, you will find this under wallets['alice'] and you can query the balance as follows:

>>> ledger.query_bank_balance(wallets['alice'])\n10000000000000000000\n

If the ledger is a testnet with a faucet url, you can get funds using the faucet:

>>> faucet.get_wealth(wallets['alice'])\n

"},{"location":"Jenesis/use-contracts/#dynamic-methods","title":"Dynamic Methods","text":"

Jenesis also attaches the contract query, execution and deploy messages as dynamic methods.

For example, the following query

>>> token_1.query({\"balance\": {\"address\": str(wallet.address())}}))\n
can also be run with:
>>> token_1.balance(address=str(wallet.address()))\n{'balance': '4000'}\n

Similarly, instead of using token_1.execute... , a transfer can be executed with:

>>> token_1.transfer(amount='1000', recipient=str(wallet2.address()), sender=wallet)\n

Jensesis also has an autocompletion helper for query, execution and deployment arguments. It will show automatically when typing in the shell.

We will now show an example assuming that the token_1 deployment contract has only been compiled and not yet deployed, going through deployment, execution, and querying using dynamic methods.

For this example, we will first generate two wallets. We provide wealth to the sender wallet in atestfet so it can pay for transaction fees.

>>> wallet = LocalWallet.generate()\n>>> wallet2 = LocalWallet.generate()\n>>> faucet.get_wealth(wallet)\n

We now proceed to deploy my_token contract using token_1 deployment configuration, we define the arguments for the cw20 token: name, symbol, decimal, and the addresses that will be funded with these cw20 tokens. In this case we will fund wallet's address with 5000 tokens.

>>> token_1.deploy(name=\"Crab Coin\", symbol=\"CRAB\", decimals=6, initial_balances=[{ \"address\": str(wallet.address()), \"amount\" :  \"5000\"}], sender=wallet)\n

We can query wallet balance to make sure it has been funded with cw20 tokens

>>> token_1.balance(address=str(wallet.address()))\n{'balance': '5000'}\n

We now execute a cw20 token transfer of 1000 tokens from wallet to wallet2

>>> token_1.transfer(amount='1000', recipient=str(wallet2.address()), sender=wallet)\n

Finally, we query both wallet's balance

>>> token_1.balance(address=str(wallet.address()))\n{'balance': '4000'}\n>>> token_1.balance(address=str(wallet2.address()))\n{'balance': '1000'}\n
We can observe that wallet has sent 1000 tokens to wallet2.

"},{"location":"Jenesis/use-contracts/#executing-scripts","title":"Executing Scripts","text":"

You can also assemble the above commands into a script that is executable by the run command:

from cosmpy.aerial.wallet import LocalWallet\nwallet = LocalWallet.generate()\nfaucet.get_wealth(wallet.address())\nwallet2 = LocalWallet.generate()\ntoken_1.deploy(name=\"Crab Coin\", symbol=\"CRAB\", decimals=6, initial_balances=[{ \"address\": str(wallet.address()), \"amount\" :  \"5000\"}], sender=wallet)\nprint(\"wallet initial cw20 MT balance: \", token_1.balance(address=str(wallet.address())))\ntx = token_1.transfer(amount='1000', recipient=str(wallet2.address()), sender=wallet)\nprint(\"transfering 1000 cw20 MT tokens from wallet to wallet2\")\ntx.wait_to_complete()\nprint(\"wallet final cw20 MT balance: \", token_1.balance(address=str(wallet.address())))\nprint(\"wallet2 final cw20 MT balance: \", token_1.balance(address=str(wallet2.address())))\n

If we paste the above code into the file script.py inside the project's directory, we can run it with:

jenesis run script.py\n

And you will observe the same output as before. You can also specify the profile as an optional argument using --profile.

Finally, you can pass arguments to the script just as you would to a standard python script by placing all the arguments to the script after the -- delimiter:

jenesis run script.py [--profile profile_name] -- arg1 arg2 --key1 value1 --key2 value2\n

You can visit CosmPy for more contract interaction examples.

"},{"location":"aea-framework-documentation/","title":"AEA Framework Documentation","text":"

Vision

Our aim with the AEA framework is to enable businesses of all sizes, from independent developers to large corporations and consortiums, to create and deploy agent-based solutions in various domains, thus contributing to and advancing a decentralized mixed-initiative economy: one whose actors are both humans and machines.

"},{"location":"aea-framework-documentation/#what-is-an-aea","title":"What is an AEA?","text":"

Definition

An Autonomous Economic Agent (AEA) is an intelligent agent that acts on its owner's behalf, with limited or no interference, and whose goal is to generate economic value for its owner.

Breaking it down:

AGENT: An AEA represents an individual, organisation or object and looks after their interests.

AUTONOMOUS: AEAs operate independently of constant input from their owners and act autonomously to achieve their goals.

ECONOMIC: AEAs have a narrow and specific focus: creating economic value for their owner.

"},{"location":"aea-framework-documentation/#what-can-you-do-with-aeas","title":"What Can You Do with AEAs?","text":"

Some examples of the kinds of applications you can build with AEAs:

Automation

AEAs can automate well-defined processes in different domains, such as supply chain, mobility, finance, ...

Micro-transactions

AEAs make it economically viable to execute trade involving small values. An example is use-cases with many small sellers (e.g. of data) on the supply side.

Wallet

AEAs can simplify interactions with blockchains. By acting as \"smart wallets\", they can hide away the majority of the complexities involved in using blockchains for end users.

IoT

Agents representing objects in the IoT (Internet of Things) space. For example, AEAs paired with hardware devices such as drones, laptops, heat sensors, etc., providing control and receiving data from the device. An example is a thermometer agent.

Web 2.0 <--> Web 3.0 interface

Agents that interface and bridge the gap between existing (Web 2.0) and new (Web 3.0) economic models. An example is an AEA that communicates with HTTP clients/servers.

Traders

Agents with access to some data sources that sell the data, access to the data, or access to the usage of the data. An example is an AEA that continuously sells data to another AEA, who in turn uses it to improve their reinforcement learning model.

"},{"location":"aea-framework-documentation/#who-is-this-for","title":"Who is This For?","text":"

The AEA technology is for anyone who wants to build or contribute to a \"mixed-initiative economy\": one whose actors are humans as well as machines.

This includes (amongst others): developers, data scientists and machine learning experts, economists, students, academics and researchers (in Artificial Intelligence, Machine Learning, Multi-Agent Systems, etc), engineers, and so forth.

"},{"location":"aea-framework-documentation/#the-aea-framework","title":"The AEA Framework","text":"

The AEA framework is a development suite which equips you with an efficient and accessible set of tools for building and running AEAs and their components.

The framework attempts to make agent development as straightforward an experience as possible, similar to what popular web frameworks enable for web development.

Some of the characteristics of the AEA framework are:

  • Python: Using Python as an approachable programming language improves the on-boarding for those who just want to get started with agent development.
  • Open source: The framework is open source and licensed under Apache 2.0.
  • Modular: Modularity is at the heart of the framework's design. This makes it easy to extend the framework, add new functionality, and re-use others' contributions, therefore reducing the development cost.
  • Blockchain ready: Integration with blockchains is baked into the framework, enabling the creation of agents that take full advantage of the blockchain technology.
  • Modern: The framework is built from and can be integrated with the latest technologies (e.g. asynchronous programming, blockchains and smart contracts, machine-learning ready, ...).
"},{"location":"aea-framework-documentation/#the-ecosystem","title":"The Ecosystem","text":"

Though they can work in isolation, AEAs are truly valuable when situated in a wider ecosystem consisting of tools and infrastructure that enable them to cooperate and compete, and interact with services as well as traditional or modern systems. These include:

  • The Agent Communication Network (ACN): A peer-to-peer communication infrastructure that enables AEAs to directly communicate with one another without any intermediaries.
  • The sOEF: A search and discovery system allowing AEAs to register themselves and the services they offer, and search for agents who offer specific services.
  • The AEA Registry: A space to store and share AEAs or individual agent components for anyone to find and use.
  • Blockchains: AEAs can use blockchains as a financial and commitment layer. Each ledger plug-in provided by the framework adds the ability for AEAs to interact with a specific ledger, such as the Fetch.ai blockchain or Ethereum.
  • Smart Contracts: Contract packages are wrappers around smart contracts that allow AEAs to interact with them through a common interface.
"},{"location":"aea-framework-documentation/#how-to-get-involved","title":"How to get involved?","text":"

There are many ways for you to get involved. You can create agents, develop new agent components, extend existing components, and contribute to the development of the framework or other related tools. Please refer to the Contribution and Development guides.

"},{"location":"aea-framework-documentation/#next-steps","title":"Next Steps","text":"

To get started developing your own AEA, check out the getting started section.

To learn more about some of the distinctive characteristics of agent-oriented development, check out the guide on agent-oriented development.

If you would like to develop an AEA in a language different to Python then check out our language agnostic AEA definition.

If you want to run a demo, check out the demo guides.

"},{"location":"aea-framework-documentation/#help-us-improve","title":"Help us Improve","text":"

Note

This developer documentation is a work in progress. If you spot any errors please open an issue on Github or contact us in the developer Discord channel.

"},{"location":"aea-framework-documentation/12-factor/","title":"Relationship with the Twelve-Factor App Methodology","text":"

The Twelve-Factor App is a set of best practices to build modern web applications, or software-as-a-service.

In this section, we will see how the AEA framework facilitates the achievement of those in the development, release and deployment phases of an AEA project.

Note that an AEA instance, as a software agent, can be seen as a more general case of a web app, as it not only shows reactive behaviour, but it is also proactive, depending on the goals assigned to it.

"},{"location":"aea-framework-documentation/12-factor/#codebase","title":"Codebase","text":"

Factor 1

One codebase tracked in revision control, many deploys

Support: Excellent

The framework does not impose any particular requirement or convention on the type of version control software to be used to store an AEA project.

"},{"location":"aea-framework-documentation/12-factor/#dependencies","title":"Dependencies","text":"

Factor 2

Explicitly declare and isolate dependencies

Support: Good

The framework allows an AEA project to explicitly declare the AEA package dependencies, and the PyPI dependencies needed to proper working.

However, it does not provide built-in support for checking platform-specific dependencies, e.g. specific Python version, or needed system-wide available libraries. Nevertheless, this can be indirectly achieved by means of build scripts called on aea build, which can do the checks manually according to the specific requirements of the project.

"},{"location":"aea-framework-documentation/12-factor/#configuration","title":"Configuration","text":"

Factor 3

Store configuration in the environment

Support: Good

An AEA project can specify an environment configuration file .env, stored in the project root, that the framework will use to update environment variables before the execution of the AEA instance.

The CLI tool command aea run accepts the option --env PATH to change the default configuration file. However, the framework does not automatically switch between, nor allows to add, different types of configuration files, one for each deployment step (e.g. development, staging, production), without using the --env option.

"},{"location":"aea-framework-documentation/12-factor/#backing-services","title":"Backing Services","text":"

Factor 4

Treat backing services as attached resources

Support: Good

A persistent storage of an AEA can be seen as an attached resource in the 12-factor terminology. The default storage is SQLite, but the interface AbstractStorageBacked allows to implement specific wrappers to other backing services, without changing the AEA project code. The support for integrating different storage back-end implementations in an AEA project by using a plug-in mechanism is currently missing.

Moreover, new adapters to backing services can be implemented as custom connections, which can connect to attached resources. This does not usually require a change in the skill code, especially in the case when a custom protocol can abstract the details of the interaction with the specific resource.

"},{"location":"aea-framework-documentation/12-factor/#build-release-run","title":"Build, Release, Run","text":"

Factor 5

Strictly separate build and run stages

Support: Excellent

The phases of build, release and run of an AEA project are neatly separated, both for programmatic usage and through the usage of the CLI tool, as each of them corresponds to different subcommands.

"},{"location":"aea-framework-documentation/12-factor/#processes","title":"Processes","text":"

Factor 6

Execute the app as one or more stateless processes

Support: Excellent

Whether the process is stateless depends on the specific AEA. No strict enforcement is applied by the framework. Moreover, dialogue histories can be stored with persistent storage, if enabled by the developer.

"},{"location":"aea-framework-documentation/12-factor/#port-binding","title":"Port Binding","text":"

Factor 7

Export services via port binding

Support: Excellent

An AEA project may not need to expose services via HTTP. This property depends on the specific choices of the project developer, and the framework does not impose any restriction.

One of the provided package, the \"HTTP server\" connection, relies on aiohttp, which makes the connection completely self-contained\u2014therefore, it satisfies the requirement.

Another relevant example is the ACN node, which exposes its service to the Libp2p AEA connection

"},{"location":"aea-framework-documentation/12-factor/#concurrency","title":"Concurrency","text":"

Factor 8

Scale out via the process model

Support: Not Supported

The framework does not easily allow to scale up an AEA instance with multiple processes, as it is bound to a process. However, note that its attached services can live in a different process, which could give better scalability.

"},{"location":"aea-framework-documentation/12-factor/#disposability","title":"Disposability","text":"

Factor 9

Maximize robustness with fast startup and graceful shutdown

Support: Good

Disposability of an AEA instance depends, in general, on the AEA itself; whether the connections can be quickly connected and disconnected, whether skills can be easily torn down or not, whether other resources can be detached successfully like the persistent storage, just to name a few examples.

There has been put some effort into reducing startup time, and to ensure that a graceful shut-down can happen when the process receives a SIGTERM under normal circumstances, but robustness cannot be ensured for individual components, as it depends on their implementation.

Additionally, the framework does provide some features to control some aspects of AEA disposability, e.g. the possibility to change execution timeout for behaviours or handlers, implementation of an effective exception propagation from a component code to the main agent loop.

"},{"location":"aea-framework-documentation/12-factor/#devprod-parity","title":"Dev/Prod Parity","text":"

Factor 10

Keep development, staging, and production as similar as possible

Support: Good

This aspect mostly depends on the specific AEA project, and the framework does not impose particular restrictions on best deployment practices (e.g. continuous integration, same backing services between development and production stages).

"},{"location":"aea-framework-documentation/12-factor/#logs","title":"Logs","text":"

Factor 11

Treat logs as event streams

Support: Excellent

Thanks to the seamless integration with the Python standard library logging, the developer or the deployer has great control on the routing and filtering of log records. The behaviour can be changed by providing a proper configuration in the AEA project configuration file, according to the standard library specification. The framework facilitates this by creating ad-hoc logger names that can be used for finer-grained routing or filtering; for example, each AEA instance uses its own logging namespace to send logging events. Integration with other log handlers is delegated to extensions of the standard library, hence not necessarily coupled with the AEA framework.

"},{"location":"aea-framework-documentation/12-factor/#admin-processes","title":"Admin Processes","text":"

Factor 12

Run admin/management tasks as one-off processes

Support: Good

The CLI tool provides commands to manage private keys and ledger related operations, and it is possible to extend it with a plugin to manage databases of AEA's persistent storage for maintenance operations.

Moreover, the Python programming language makes it easy to run one-off scripts or running a console (also known as REPL) to do management tasks. It follows that it is also easy to ensure dependency isolation and same configurations of the running AEA instance.

"},{"location":"aea-framework-documentation/acn-internals/","title":"ACN Internals","text":"

The aim of this document is to describe at a high-level the main implementation of the Agent Communication Network (ACN).

In particular:

  • the libp2p_node Golang library;
  • the p2p_libp2p AEA connection written in Python, that implements the direct connection with an ACN peer;
  • the p2p_libp2p_client AEA connection written in Python, which implements the delegate connection with an ACN peer.

It is assumed the reader already knows what is the ACN and its purposes; if not, we suggest reading this page.

This documentation page is structured as follows:

  • Firstly, the ACN protocol is described: all the messages and data structures involved, as well as some example of interaction protocol with these messages;
  • Then, it is explained how a peer can join an existing ACN network, and the message exchange involved;
  • It follows the description of the journey of an envelope in the ACN network: from the agent connection to its contact peer, between ACN peers, and then from the contact peer of the destination agent to the target agent;
  • The following section describes the functionalities of the AEA connections that allow to communicate through the ACN: fetchai/p2p_libp2p and fetchia/p2p_libp2p_delegate;
  • The documentation ends with a section of known issues and limitations of the current implementation.
"},{"location":"aea-framework-documentation/acn-internals/#messages-and-data-structures","title":"Messages and Data Structures","text":"

At the foundation of the ACN there is the ACN protocol. The protocol messages and the reply structure are generated from this protocol specification, using the protocol generator. Therefore, it uses Protocol Buffers as a serialization format, and the definition of the data structures involved is defined in this .proto file.

To know more about the protocol generator, refer to the relevant section of the documentation: Protocol Generator.

"},{"location":"aea-framework-documentation/acn-internals/#agent-record","title":"Agent Record","text":"

An agent record is a data structure containing information about an agent and its Proof-of-Representation (PoR) to be used by a peer for other peers. This data structure is used as a payload in other ACN messages (see below).

The AgentRecord data structure contains the following fields:

  • service_id: a string describing the service identifier.
  • ledger_id: a string. It is the identifier of the ledger this agent record is associated to. Currently, the allowed values are:
    • fetchai, the identifier for the Fetch.AI ledger;
    • ethereum, the identifier for the Ethereum ledger;
    • cosmos, the identifier for the Cosmos ledger;
  • address: a string. It is the public key of a public-private key pair. It is used as an identifier for routing purposes.
  • public_key: a string. The representative's public key. Used in case of (PoR).
  • peer_public_key: a string. The public key of the peer.
  • signature: a string. The signature for PoR.
  • not_before: a string. Specify the lower bound for certificate validity. If it is a string, it must follow the format: YYYY-MM-DD. It will be interpreted as time zone UTC-0
  • not_after: a string. Specify the upper bound for certificate validity. If it is a string, it must follow the format: YYYY-MM-DD. It will be interpreted as time zone UTC-0.
"},{"location":"aea-framework-documentation/acn-internals/#acn-message","title":"ACN Message","text":"

Entities in the ACN (i.e. either agents or peers) exchange ACN messages. An ACN message contains a payload field, which is the actual content of the message.

There are different types of payloads:

  • Status
  • Register
  • LookupRequest
  • LookupResponse
  • AeaEnvelope
"},{"location":"aea-framework-documentation/acn-internals/#status","title":"Status","text":"

The Status payload is used as a response message to inform the sender about the handling of certain requests. The payload contains:

  • the status_code, a positive integer among the ones in the Protobuf file.
  • a list of error messages (string).

A status code 0, identified as SUCCESS, means that the request has been processed successfully. Status codes greater than 0 can be:

  • Generic errors: errors that occur under generic circumstances.

    • ERROR_UNSUPPORTED_VERSION, with integer value 1: the receiver of the message does not support the protocol version of the sender;
    • ERROR_UNEXPECTED_PAYLOAD, with integer value 2: the payload could not be deserialized on the receiver side;
    • ERROR_GENERIC, with integer value 3: an internal error;
    • ERROR_SERIALIZATION, with integer value 4: a serialization error occurred on the receiving end;
  • Register errors: errors that occur during agent registration operations in the ACN.

    • ERROR_WRONG_AGENT_ADDRESS, with integer value 10: the PoR by a peer from another peer does not match the destination address of the envelope to be routed by the receiving peer.
    • ERROR_WRONG_PUBLIC_KEY, with integer value 11: the representative peer public key does not match the one in the agent record;
    • ERROR_INVALID_PROOF, with integer value 12: the signature is invalid;
    • ERROR_UNSUPPORTED_LEDGER, with integer value 13: the ledger of the PoR is not supported by the peer;
  • Lookup and delivery errors: errors that occur during lookup to the DHT and envelope delivery operations in the ACN.

    • ERROR_UNKNOWN_AGENT_ADDRESS, with integer value 20: the requested agent address has not been found in the local DHT of the peer;
    • ERROR_AGENT_NOT_READY, with integer value 21: the agent is not ready for envelope delivery.
"},{"location":"aea-framework-documentation/acn-internals/#register","title":"Register","text":"

The Register payload is used to request a peer to register an agent among his known ones. The payload contains the field record, which is an instance of AgentRecord.

"},{"location":"aea-framework-documentation/acn-internals/#lookuprequest","title":"LookupRequest","text":"

The LookupRequest payload is sent between peer to look-up addresses in the Distributed Hash Table (DHT). It contains the agent address (a string) that the sender needs to correctly route an envelope.

"},{"location":"aea-framework-documentation/acn-internals/#lookupresponse","title":"LookupResponse","text":"

The LookupResponse payload is the response sent by a peer that received a LookupRequest. It contains the AgentRecord associated to the requested address.

"},{"location":"aea-framework-documentation/acn-internals/#aeaenvelope","title":"AeaEnvelope","text":"

The AeaEnvelope payload contains the envelope sent by an agent and to be delivered to another agent. It contains:

  • envelope: the envelope to be forwarded, in byte representation;
  • an AgentRecord (see above).
"},{"location":"aea-framework-documentation/acn-internals/#acn-protocol-interactions","title":"ACN Protocol Interactions","text":"

The ACN protocol specifies three different possible interactions:

  • the registration interaction
  • the look-up interaction
  • the routing interaction
"},{"location":"aea-framework-documentation/acn-internals/#registration-interaction","title":"\"Registration\" Interaction","text":"

The registration interaction is used by delegate agents or relayed peers to register themselves to another peer.

    sequenceDiagram\n        participant Agent/RelayedPeer\n        participant Peer\n        Agent/RelayedPeer->>Peer: Register(AgentRecord)\n        alt success\n            note over Peer: check PoR\n            Peer->>Agent/RelayedPeer: Status(SUCCESS)\n        else wrong agent address\n            Peer->>Agent/RelayedPeer: Status(ERROR_WRONG_AGENT_ADDRESS)\n        else wrong public key\n            Peer->>Agent/RelayedPeer: Status(ERROR_WRONG_PUBLIC_KEY)\n        else invalid proof of representation\n            Peer->>Agent/RelayedPeer: Status(ERROR_INVALID_PROOF)\n        else unsupported ledger\n            Peer->>Agent/RelayedPeer: Status(ERROR_UNSUPPORTED_LEDGER)\n        end
"},{"location":"aea-framework-documentation/acn-internals/#look-up-interaction","title":"\"Look-up\" Interaction","text":"

The look-up interaction is used by a peer to request information to another peer about an agent address.

    sequenceDiagram\n        participant Peer1\n        participant Peer2\n        Peer1->>Peer2: LookupRequest(address)\n        alt success\n            Peer2->>Peer1: LookupResponse(AgentRecord)\n        else unknown agent address\n            Peer2->>Peer1: Status(ERROR_UNKNOWN_AGENT_ADDRESS)\n        end
"},{"location":"aea-framework-documentation/acn-internals/#routing-interaction","title":"\"Routing\" Interaction","text":"

The routing interaction is used by agents and peers to route the envelope through the ACN.

    sequenceDiagram\n        participant Peer1\n        participant Peer2\n        Peer1->>Peer2: AeaEnvelope(envelope, AgentRecord)\n        alt success\n            note over Peer2: check PoR\n            Peer2->>Peer1: Status(SUCCESS)\n        else error on decoding of Envelope payload\n            Peer2->>Peer1: Status(ERROR_SERIALIZATION)\n        else PoR errors\n            note over Peer1,Peer2: see above \n        end
"},{"location":"aea-framework-documentation/acn-internals/#joining-the-acn-network","title":"Joining the ACN network","text":"

When an ACN peer wants to join the network, it has to start from a list of bootstrap peers, i.e. a list of ACN peers to connect with (at least one).

Each node handles four different types of libp2p streams:

  • the notification stream, identified by the URI /aea-notif/: this stream is used by new peers to notify their existence to
  • the address stream, identified by the URI /aea-address/: used to send look-up requests and look-up responses;
  • the envelope stream, identified by the URI /aea/: used to forward and to receive ACN envelopes;
  • the register relay stream, identified by the URI /aea-register/: this is to receive messages from clients that want to register their agents addresses; this peer, and then it can register their addresses.

To begin with, the node process initializes the transport connections with the bootstrap peers, the local copy of the Kademlia Distributed Hash Table (DHT), the persistent storage for agent records, and performs other non-functional operations like setting up the Prometheus monitoring system. Optionally, can also start listening for relay connections and delegate connections.

Then, it sets up the notification stream and notifies the bootstrap peers (if any).

    sequenceDiagram\n        participant Peer1\n        participant Peer2\n        participant Peer3\n        note over Peer1: notify<br/>bootstrap peers\n        Peer1->>Peer2: notify\n        Peer2->>Peer2: wait until notifying peer <br/>added to DHT\n        activate Peer2\n        Peer1->>Peer3: notify\n        Peer3->>Peer3: wait until notifying peer <br/>added to DHT\n        activate Peer3\n        note over Peer2,Peer3: Peer1 registered to DHT\n        deactivate Peer2\n        deactivate Peer3\n        loop for each local/relay/delegate address \n            Peer1->>Peer1: compute CID from address\n            Peer1->>Peer2: register address\n            Peer1->>Peer3: register address\n        end\n        note over Peer1: set up:<br/>- address stream<br/>- envelope stream<br/>- register relay stream
"},{"location":"aea-framework-documentation/acn-internals/#relay-connections","title":"Relay Connections","text":"

If the ACN node is configured to run the relay service, it sets up the register relay stream, waiting for registration requests.

The following diagram shows an example of the message exchanged during a registration request:

    sequenceDiagram\n        participant Agent\n        participant Peer\n        Agent->>Peer: Register\n        alt decoding error of ACN message\n            Peer->>Agent: Status(ERROR_SERIALIZATION)\n        else wrong payload\n            Peer->>Agent: Status(ERROR_UNEXPECTED_PAYLOAD)\n        else PoR check fails\n            alt wrong agent address\n                Peer->>Agent: Status(ERROR_WRONG_AGENT_ADDRESS)\n            else unsupported ledger\n                Peer->>Agent: Status(ERROR_UNSUPPORTED_LEDGER)\n            else agent address and public key don't match\n                Peer->>Agent: Status(ERROR_WRONG_AGENT_ADDRESS)\n            else invalid proof\n                Peer->>Agent: Status(ERROR_INVALID_PROOF)\n            end\n        else PoR check succeeds\n            Peer->>Agent: Status(SUCCESS)\n            note over Peer: announce agent address<br/>to other peers\n        end
"},{"location":"aea-framework-documentation/acn-internals/#delegate-connections","title":"Delegate Connections","text":"

If the ACN node is configured to run the delegate service, it starts listening from a TCP socket at a configurable URI.

To see a diagram of the message exchanged during a registration request read this section.

"},{"location":"aea-framework-documentation/acn-internals/#acn-transport","title":"ACN Transport","text":"

In the following sections, we describe the main three steps of the routing of an envelope through the ACN:

  • ACN entrance: when an envelope sent by an agent enters the peer-to-peer network via the peer the agent is connected to i.e. agent-to-peer communication;
  • ACN routing: when an envelope gets routed through the peer-to-peer network, i.e. peer-to-peer communication;
  • ACN exit: when an envelope gets delivered to the receiving agent through its representative peer, i.e. peer-to-agent communication.
"},{"location":"aea-framework-documentation/acn-internals/#acn-envelope-entrance-agent-peer","title":"ACN Envelope Entrance: Agent -> Peer","text":"

In this section, we will describe the interaction protocols between agents and peers for the messages sent by the agent to the ACN network; in particular, the communication from the contact peer of an agent to the agent.

The following diagram explains the exchange of messages on entering an envelope in the ACN.

In the case of direct connection, Agent is a Python process, whereas Peer is in a separate (Golang) process. The logic of the Python Agent client is implemented in the AEA connection fetchai/p2p_libp2p The communication between Agent and Peer is done through an OS pipe for Inter-Process Communication (IPC) between the AEA's process and the libp2p node process; then, the message gets enqueued to an output queue by an input coroutine. Finally, the envelope ends up in an output queue, which is processed by an output coroutine and routed to the next peer.

In the case of delegate connection, the message exchange is very similar; however, instead of using pipes, the communication is done through the network, i.e. TCP, with a peer which has the delegate service enabled. The logic of the Agent client connected with a delegate connection is implemented in the AEA connection fetchai/p2p_libp2p_client

    sequenceDiagram\n        participant Agent\n        participant Peer\n        loop until Status(success) received\n            Agent->>Peer: AeaEnvelope\n            Agent->>Agent: wait\n            note left of Agent: Wait until<br/>Status(success)\n            alt successful case\n                Peer->>Agent: Status(success)\n                note over Agent: break loop\n            else ack-timeout OR conn-error\n                note left of Agent: continue: Try to<br/>resend/reconnect\n            else version not supported\n                Peer->>Agent: Status(ERROR_UNSUPPORTED_VERSION)\n            else error on decoding of ACN message\n                Peer->>Agent: Status(ERROR_SERIALIZATION)\n            else error on decoding of Envelope payload\n                Peer->>Agent: Status(ERROR_SERIALIZATION)\n            else the payload cannot be handled\n                Peer->>Agent: Status(ERROR_UNEXPECTED_PAYLOAD)\n            end\n        end\n        note over Peer: route envelope<br/>to next peer
"},{"location":"aea-framework-documentation/acn-internals/#acn-envelope-routing","title":"ACN Envelope Routing","text":"

In this section, we describe the interaction between peers when it comes to envelope routing.

Assume an envelope arrives from an agent to peer Peer1, i.e. Peer1 is the first hop of the routing. Let Agent be the local agent directly connected to Peer1, Peer2 a direct peer of peer Peer1.

When the envelope is leaving Peer1, we may have different scenario:

  1. In case of direct connection, and the field sender of the envelope is not the local agent address: the message is considered invalid, and it is dropped.

        sequenceDiagram\n        participant Agent\n        participant Peer1\n        participant Peer2\n        Agent->>Peer1: AeaEnvelope\n        alt envelope sender not registered locally\n            note over Peer1: stop, log error\n        end
  2. the target of the envelope is the local agent connected to the peer: the envelope is routed to the local agent.

        sequenceDiagram\n        participant Agent\n        participant Peer1\n        participant Peer2\n        Agent->>Peer1: AeaEnvelope\n        alt target == peer1.my_agent\n            note over Peer1: envelope destinated<br/> to local agent,<br/> not routing\n            loop agent not ready\n                note over Peer1: sleep for 100ms\n            end\n            Peer1->>Agent: AeaEnvelope\n            Agent->>Peer1: Status(Success)\n        end
  3. the target is a delegate client. Send the envelope via TCP.

        sequenceDiagram\n        participant Delegate\n        participant Peer1\n        participant Peer2\n        Delegate->>Peer1: AeaEnvelope\n        alt destination is a delegate\n            note over Peer1: send envelope<br/> to delegate via TCP\n            Peer1->>Delegate: AeaEnvelope\n            Delegate->>Peer1: Status(Success)\n        end
  4. Otherwise, look up the local DHT. If an entry is found, use it; otherwise, send a look-up request to connected peers.

    sequenceDiagram\n        participant Agent\n        participant Peer1\n        participant Peer2\n        Agent->>Peer1: AeaEnvelope\n        alt address found in DHT\n            note over Peer1: destination is a<br/>relay client\n        else lookup address in DHT\n            note over Peer1: send lookup request<br/> to all peers\n            Peer1->>Peer2: LookupRequest\n            alt generic error\n                Peer2->>Peer1: Status(GENERIC_ERROR)\n            else look-up response\n                Peer2->>Peer1: LookupResponse\n                note over Peer1: Check PoR\n            else not found\n                Peer2->>Peer1:Status(UNKNOWN_AGENT_ADDRESS)\n            end\n        end\n        note over Peer1,Peer2: Now Peer1 knows the contact peer<br/>is PeerX

In particular, when a peer receives a LookupRequest message, it does the following:

    sequenceDiagram\n        participant Peer1\n        participant Peer2\n        Peer1->>Peer2: LookupRequest\n        alt error\n            Peer2->>Peer1: Status(Error)\n        else local agent/relay/delegate\n            note over Peer2: requested address is<br/>a local agent<br/>OR<br/>requested address is<br/>in my relay clients<br/>OR<br/>requested address is<br/>in my delegate clients\n            Peer2->>Peer1: LookupResponse\n            note over Peer1: Check PoR\n        else not found locally\n            note over Peer2: send lookup request<br/>to other peers...\n            alt found\n                Peer2->>Peer1: LookupResponse\n                note over Peer1: Check PoR\n            else not found\n                Peer2->>Peer1:Status(UNKNOWN_AGENT_ADDRESS)\n            end\n        end

Let Peer3 the contact peer of the recipient of the envelope. The following diagram shows how the contact peer of the envelope recipient handles the incoming envelope:

    sequenceDiagram\n        participant Peer1\n        participant Peer3\n        Peer1->>Peer3: AeaEnvelope\n        alt decoding error of ACN message\n            Peer3->>Peer1: Status(ERROR_SERIALIZATION)\n        else unexpected payload\n            Peer3->>Peer1: Status(ERROR_UNEXPECTED_PAYLOAD)\n        else decoding error of envelope payload\n            Peer3->>Peer1: Status(ERROR_SERIALIZATION)        \n        else PoR check fails\n            alt wrong agent address\n                Peer3->>Peer1: Status(ERROR_WRONG_AGENT_ADDRESS)\n            else unsupported ledger\n                Peer3->>Peer1: Status(ERROR_UNSUPPORTED_LEDGER)\n            else agent address and public key don't match\n                Peer3->>Peer1: Status(ERROR_WRONG_AGENT_ADDRESS)\n            else invalid proof\n                Peer3->>Peer1: Status(ERROR_INVALID_PROOF)\n            end\n        else PoR check succeeds\n            alt target is delegate, not ready\n                Peer3->>Peer1: Status(ERROR_AGENT_NOT_READY)\n            else exists delegate, ready\n                note over Peer3: forward envelope via<br/>delegate connection\n                Peer3->>Peer1: Status(SUCCESS)\n            else target is local agent, not ready\n                Peer3->>Peer1: Status(ERROR_AGENT_NOT_READY)\n            else target is local agent, ready\n                note over Peer3: forward envelope via<br/>direct connection\n                Peer3->>Peer1: Status(SUCCESS)\n            else agent does not exist\n                Peer3->>Peer1: Status(ERROR_UNKNOWN_AGENT_ADDRESS)\n            end\n        end
"},{"location":"aea-framework-documentation/acn-internals/#acn-envelope-exit-peer-agent","title":"ACN Envelope Exit: Peer -> Agent","text":"

The following diagram explains the exchange of messages on exiting an envelope in the ACN. That is, the communication from the contact peer of an agent to the agent.

The same message exchange is done both in the case of direct connection and delegate connection, similarly for what has been described for the envelope entrance (see above).

    sequenceDiagram\n        participant Agent\n        participant Peer\n        Peer->>Agent: AeaEnvelope\n        alt successful case\n            Agent->>Peer: Status(success)\n        else ack-timeout OR conn-error\n            note left of Peer: do nothing\n        else error on decoding of ACN message\n            Agent->>Peer: Status(GENERIC_ERROR)\n        else error on decoding of Envelope payload\n            Agent->>Peer: Status(GENERIC_ERROR)\n        else wrong payload\n            Agent->>Peer: Status(GENERIC_ERROR)\n        end
"},{"location":"aea-framework-documentation/acn-internals/#connect-your-aea-to-the-acn","title":"Connect your AEA to the ACN","text":"

To connect the AEA to the ACN network, there are two AEA connections available:

  • the fetchai/p2p_libp2p, that implements a direct connection, and
  • the fetchai/p2p_libp2p_delegate connection, that implements the delegate connection.

For more information on the AEA connection package type, refer to this guide.

"},{"location":"aea-framework-documentation/acn-internals/#the-fetchaip2p_libp2p-connection","title":"The fetchai/p2p_libp2p Connection","text":"

The source code of the fetchai/p2p_libp2p connection can be downloaded from the AEA Registry, or from the main AEA framework repository.

The package provides the connection class P2PLibp2pConnection, which implements the Connection interface and therefore can be used by the Multiplexer as any other connection.

  • The connect method of this connection spawns a new instance of the libp2p_node program (i.e. an ACN peer node) and connects to it through OS pipes. Then, it sets up the message receiving loop, which enqueues messages in the input queue to be read by read method calls, and the message sending loop, which dequeues messages from the output queue and forwards them to the Libp2p node. The loops are run concurrently in the Multiplexer thread, using the Python asynchronous programming library asyncio.
    sequenceDiagram\n        participant Libp2p Connection\n        participant sending loop\n        participant receiving loop\n        participant Libp2p Node\n        Libp2p Connection->>Libp2p Node: spawn process\n        activate Libp2p Node\n        Libp2p Connection->>sending loop: start recv loop\n        sending loop->>sending loop: wait messages from output queue\n        activate sending loop\n        Libp2p Connection->>receiving loop: start send loop\n        receiving loop->>receiving loop: wait messages from input queue\n        activate receiving loop\n        deactivate Libp2p Node\n        deactivate sending loop\n        deactivate receiving loop
  • The send method enqueues a message in the output queue. The message is then dequeued by the sending loop, and then sent to the Libp2p node.
    sequenceDiagram\n        participant Libp2p Connection\n        participant sending loop\n        participant Libp2p Node\n        activate sending loop\n        Libp2p Connection->>Libp2p Connection: enqueue message to output queue\n        sending loop->>sending loop: dequeue message from output queue\n        deactivate sending loop\n        sending loop->>Libp2p Node: AeaEnvelope\n        sending loop->>sending loop: wait for status\n        activate sending loop\n        alt success\n            note over Libp2p Node: route envelope\n            Libp2p Node->>sending loop: Status(SUCCESS)\n            deactivate sending loop\n            note over sending loop: OK\n        else timed out\n            note over sending loop: raise with error\n        else acn message decoding error \n            Libp2p Node->>sending loop: Status(ERROR_SERIALIZATION)\n        else unexpected payload\n            Libp2p Node->>sending loop: Status(ERROR_UNEXPECTED_PAYLOAD)\n        else envelope decoding error \n            Libp2p Node->>sending loop: Status(ERROR_SERIALIZATION)\n        end
  • The receive method dequeues a message from the input queue. The queue is populated by the receiving loop, which receives messages from the Libp2p node.
    sequenceDiagram\n        participant Libp2p Connection\n        participant receiving loop\n        participant Libp2p Node\n        activate receiving loop\n        Libp2p Node->>receiving loop: AeaEnvelope\n        deactivate receiving loop\n        Libp2p Node->>Libp2p Node: wait for status\n        activate Libp2p Node\n        alt success\n            note over receiving loop: enqueue envelope<br/>to input queue\n            receiving loop->>Libp2p Node: Status(SUCCESS)\n            deactivate Libp2p Node\n            note over receiving loop: OK\n        else timed out\n            note over Libp2p Node: ignore\n        else acn message decoding error \n            receiving loop->>Libp2p Node: Status(ERROR_SERIALIZATION)\n        else unexpected payload\n            receiving loop->>Libp2p Node: Status(ERROR_UNEXPECTED_PAYLOAD)\n        else envelope decoding error \n            receiving loop->>Libp2p Node: Status(ERROR_SERIALIZATION)\n        end\n        Libp2p Connection->>receiving loop: read message from output queue\n        note over Libp2p Connection: return message<br/>to Multiplexer
  • the disconnect method stops both the receiving loop and the sending loop, and stops the Libp2p node.
"},{"location":"aea-framework-documentation/acn-internals/#the-fetchaip2p_libp2p_delegate-connection","title":"The fetchai/p2p_libp2p_delegate Connection","text":"

The source code of the fetchai/p2p_libp2p_delegate connection can be downloaded from the main AEA framework repository. or from the main AEA framework repository.

The package provides the connection class P2PLibp2pClientConnection, which implements the Connection interface and therefore can be used by the Multiplexer as any other connection.

  • The connect method of this connection will set up a TCP connection to the URI of the delegate peer. Then, it will send a Register request to register the agent among the peer's client connections. On registration success, it sets up the message receiving loop, which enqueues messages in the input queue to be read by read method calls, and the message sending loop, which dequeues messages from the output queue and forwards them to the Libp2p node. The loops are run concurrently in the Multiplexer thread, using the Python asynchronous programming library asyncio.
    sequenceDiagram\n        participant Libp2p Client Connection\n        participant Libp2p Node\n        activate Libp2p Node\n        Libp2p Node->>Libp2p Node: listening for TCP connections\n        Libp2p Client Connection->>Libp2p Node: Register (via TCP)\n        deactivate Libp2p Node\n        alt decoding error of ACN message\n            Libp2p Node->>Libp2p Client Connection: Status(ERROR_SERIALIZATION)\n        else wrong payload\n            Libp2p Node->>Libp2p Client Connection: Status(ERROR_UNEXPECTED_PAYLOAD)\n        else PoR check fails\n            alt wrong agent address\n                Libp2p Node->>Libp2p Client Connection: Status(ERROR_WRONG_AGENT_ADDRESS)\n            else unsupported ledger\n                Libp2p Node->>Libp2p Client Connection: Status(ERROR_UNSUPPORTED_LEDGER)\n            else agent address and public key don't match\n                Libp2p Node->>Libp2p Client Connection: Status(ERROR_WRONG_AGENT_ADDRESS)\n            else invalid proof\n                Libp2p Node->>Libp2p Client Connection: Status(ERROR_INVALID_PROOF)\n            end\n        else PoR check succeeds\n            Libp2p Node->>Libp2p Client Connection: Status(SUCCESS)\n            note over Libp2p Node: announce agent<br/>address to<br/>other peers\n            Libp2p Node->>Libp2p Node: wait data from socket \n            activate Libp2p Node\n            deactivate Libp2p Node\n        end
  • The send method and the receive methods behave similarly to the send and receive methods of the p2p_libp2p connection, in terms of message exchange; however, the communication is done via TCP rather than pipes.

  • The disconnect method interrupts the connection with the delegate peer, without explicitly unregistering.

"},{"location":"aea-framework-documentation/acn-internals/#known-issues-and-limitations","title":"Known Issues and Limitations","text":"

In this section, we provide a list of known issues and limitations of the current implementation of the ACN, considering both the ACN nodes (written in Golang) and the AEA connections, for the Python AEA framework, to interact with them.

"},{"location":"aea-framework-documentation/acn-internals/#delegate-client-on-client-disconnectionreconnection","title":"Delegate Client on Client Disconnection/Reconnection","text":"

In case of disconnection/reconnection, delegate client record will be removed. This can cause two problems: either the delegate client is not found, or connection is closed during the send operation.

Possible solutions:

  • Create more complicated structure for clients storage;
  • Keep the delegate client record for longer;
  • Clean up the record by timeout, per client queues.

Code references:

  • record removed: https://github.com/fetchai/agents-aea/blob/1db1720081969bcec1be5a2000ca176475d2b487/libs/go/libp2p_node/dht/dhtpeer/dhtpeer.go#L864
  • send code: https://github.com/fetchai/agents-aea/blob/1db1720081969bcec1be5a2000ca176475d2b487/libs/go/libp2p_node/dht/dhtpeer/dhtpeer.go#L955
"},{"location":"aea-framework-documentation/acn-internals/#golang-node-python-client-libp2p-connection","title":"Golang Node <> Python Client libp2p Connection","text":"

In case of connection between the Golang side (i.e. ACN node) and the Python side (i.e. the libp2p AEA connection) is broken, there is no reconnection attempt. The Golang side connect to the Python server opened, but if the connection is broken Golang can try to reconnect; however, the Python side does not know about this and will restart the node completely.

Possible solutions: the problem requires updates on both sides and assume possible timeouts on broken connection. If connection is broken, the Python side awaits for reconnection from Golang side, and restart node completely after timeout.

"},{"location":"aea-framework-documentation/acn-internals/#what-a-peer-should-do-if-it-receives-an-acknowledgement-with-an-error","title":"What a Peer Should Do if it Receives an Acknowledgement with an Error?","text":"

If an ACN response is the Status with error code different from SUCCESS, the forwarding to other peers is not repeated.

A possible solution is to resend the message; however, not clear why it should help in case of healthy connection, how many times the sender should retry, and how it would help.

Discussion on GitHub: https://github.com/fetchai/agents-aea/pull/2509#discussion_r642628983

"},{"location":"aea-framework-documentation/acn-internals/#no-possibility-of-switching-peers","title":"No Possibility of Switching Peers","text":"

In case of a peer becoming unavailable, a delegate client or relay client currently has no means to automatically switch the peer. In particular, the DHT should be updated when a client switches peers.

"},{"location":"aea-framework-documentation/acn/","title":"Agent Communication Network","text":"

The agent communication network (ACN) provides a system for agents to find each other and communicate, solely based on their wallet addresses. It addresses the message delivery problem.

"},{"location":"aea-framework-documentation/acn/#message-delivery-problem","title":"Message Delivery Problem","text":"

Agents need to contact each others. Given the wallet address of a target agent, how can the originator agent deliver a message to it whilst guaranteeing certain properties?

The properties we would like to have, are:

  • Reliability: with guarantees on message reception
  • Authentication: to prevent impersonation
  • Confidentiality: to prevent exposing sensitive information within the message
  • Availability: some guarantees about the liveness of the service (tampering detection)

The problem statement and the agent framework context impose a number of design constraints:

  • Distributed environment: no assumption are placed about the location of the agent, they can be anywhere in the publicly reachable internet
  • Decentralized environment: no trusted central authority
  • Support for resource-constrained devices

The ACN solves the above problem whilst providing the above guarantees and satisfying the constraints.

"},{"location":"aea-framework-documentation/acn/#peers","title":"Peers","text":"

The ACN is maintained by peers. Peers are not to be equated with agents. They are processes (usually distributed and decentralized) that together maintain the service. To use the service, agents need to associate themselves with peers. Thanks to digital signatures, the association between a given peer and agent can be verified by any participant in the system.

"},{"location":"aea-framework-documentation/acn/#distributed-hash-table","title":"Distributed Hash Table","text":"

At its core, the ACN implements a distributed hash table (DHT). A DHT is similar to a regular hash table in that it stores key-value pairs. However, storage is distributed across the participating machines (peers) with an efficient lookup operation. This is enabled by:

  • Consistent hashing: decide responsibility for assignment of the DHT key-value storage
  • Structured overlays: organize the participating peers in a well-defined topology for efficient routing

For the ACN, we use the DHT to store and maintain association between an agent address and the (network) location of its peer.

"},{"location":"aea-framework-documentation/acn/#n-tier-architecture","title":"N-Tier Architecture","text":"

To satisfy different resource constraints and flexible deployment the ACN is implemented as a multi-tier architecture. As such, it provides an extension of the client-server model. The agent framework exploits this by implementing different tiers as different Connections:

Note

The p2p_libp2p_mailbox connection is not available yet.

"},{"location":"aea-framework-documentation/acn/#trust-and-security","title":"Trust and Security","text":"

An agent can choose which connection to use depending on the resource and trust requirements:

  • p2p_libp2p connection: the agent maintains a peer of the ACN. The agent has full control over the peer and does not need to trust any other entity.
  • p2p_libp2p_client connection: the agent maintains a client connection to a server which is operated by a peer of the ACN. The agent does need to trust the entity operating the peer.

All communication protocols use public cryptography to ensure security (authentication, confidentiality, and availability) using TLS handshakes with pre-shared public keys.

"},{"location":"aea-framework-documentation/aea-vs-mvc/","title":"AEA and Web Frameworks","text":"

The AEA framework borrows several concepts from popular web frameworks like Django and Ruby on Rails.

"},{"location":"aea-framework-documentation/aea-vs-mvc/#mvc","title":"MVC","text":"

Both aforementioned web frameworks use the MVC (model-view-controller) architecture.

  • Models: contain business logic and data representations
  • View: contain the HTML templates
  • Controller: deals with the request-response handling
"},{"location":"aea-framework-documentation/aea-vs-mvc/#comparison-with-the-aea-framework","title":"Comparison with the AEA Framework","text":"

The AEA framework is based on asynchronous messaging and other agent-oriented development assumptions. Hence, there is not a direct one-to-one relationship between MVC based architectures and the AEA framework. Nevertheless, there are some parallels which can help a developer familiar with MVC make quick progress in the AEA framework, in particular the development of Skills:

  • Handler: receives messages for the protocol it is registered against and is supposed to handle these messages. Handlers are the reactive parts of a skill and can be thought of as similar to the Controller in MVC. They can also send new messages.
  • Behaviour: a behaviour encapsulates proactive components of the agent. Since web apps do not have any goals or intentions, they do not proactively pursue an objective. Therefore, there is no equivalent concept in MVC. Behaviours also can, but do not have to, send messages.
  • Task: they are meant to deal with long-running executions and can be thought of as the equivalent of background tasks in traditional web apps.
  • Model: they implement business logic and data representation, and as such, they are similar to the Model in MVC.

The View concept is probably best compared to the Message of a given Protocol in the AEA framework. Whilst views represent information to the client, messages represent information sent to other agents, other agent components and services.

"},{"location":"aea-framework-documentation/aea-vs-mvc/#next-steps","title":"Next Steps","text":"

We recommend you continue with the next step in the 'Getting Started' series:

  • Build a skill for an AEA
"},{"location":"aea-framework-documentation/aeas/","title":"Autonomous Economic Agents (AEAs)","text":""},{"location":"aea-framework-documentation/aeas/#what-is-an-aea","title":"What is an AEA?","text":"

Definition

An Autonomous Economic Agent (AEA) is an intelligent agent that acts on its owner's behalf, with limited or no interference, and whose goal is to generate economic value for its owner.

Let's break down the term Autonomous Economic Agent (AEA):

  • Agent: An AEA is first and foremost an agent, representing an individual, organisation or object (a.k.a. its \"owner\") in the digital world. An AEA looks after its owner's interests and has their preferences in mind when acting on their behalf.
  • Autonomous: AEAs operate independently of constant input from their owners and act autonomously to achieve their goals.
  • Economic: AEAs have a narrow and specific focus: creating economic value for their owner.

Some of the other characteristics AEAs typically have:

  • Proactive: AEAs are proactive; they take the initiative and perform actions that take them closer to their goals.
  • Reactive: AEAs are also reactive; they are aware of the environment they are in, perceive changes in the environment, and react to these changes in accordance to their goals.
  • Self-interested: An AEA primarily looks after its own interests (which is aligned with those of its owner) and not necessarily the interests of other agents or the larger system.
"},{"location":"aea-framework-documentation/aeas/#what-is-not-an-aea","title":"What is NOT an AEA?","text":"
  • Any agent: AEAs are NOT meant to address any needs their owners might have. They have a clear and well-defined focus, which is generating economic value for their owner and this is manifested in a variety of different ways in their design.
  • Digital twins: An AEA is NOT it's owner's twin in the digital world; i.e. mirroring their preferences, values, and priorities. An AEA can be given whatever preference, value, and priority its owner wants them to have.
  • APIs or Sensors: These do NOT have any agency, nor proactiveness. They just \"sit there\" and respond to requests or changes in the environment.
  • Smart contracts: Similar to APIs and sensors, smart contracts do NOT display any proactiveness; they are purely reactive to external requests (in their case, contract calls and transactions).
  • An agent with Artificial General Intelligence (AGI): AEAs have a well-defined, narrow, and goal directed focus that involves some economic gain.

Agents and AEAs

In the rest of the documentation, unless specified, we use the terms AEA and Agent interchangeably to mean AEA as defined above description.

"},{"location":"aea-framework-documentation/agent-oriented-development/","title":"Agent-Oriented Development","text":"

In this section, we discuss some of the most fundamental characteristics of an agent-oriented approach to solution development, which might be different from existing paradigms and methodologies that you may be used to. We hope that with this, we can guide you towards the right mindset when designing your own agent-based solutions to real world problems.

"},{"location":"aea-framework-documentation/agent-oriented-development/#decentralisation","title":"Decentralisation","text":"

Multi-Agent Systems (MAS) are inherently decentralized. The vision is of an environment in which every agent is able to directly connect with everyone else and interact with them without having to rely on a third party acting as an intermediary or match-maker. This is in direct contrast to centralized systems in which a single entity is the central point of authority, through which all interactions happen. The conventional client-server model is an example of a centralized architecture where clients interact with one another regarding specific services (e.g. communication, commerce) only through a server.

This is not to say that facilitators and middlemen have no place in a multi-agent system; rather it is the 'commanding reliance on middlemen' that MAS rejects.

Division of responsibilities: In a decentralized system, every agent is equally privileged, and (in principle) should be able to interact with any other agent. The idea is very much aligned with the peer-to-peer paradigm, in which it is the voluntary participation and contribution of the peers that create the infrastructure. Therefore, in a decentralized system, there is no central 'enforcer'. This means all the work that would typically fall under the responsibilities of a central entity must be performed by individual parties in a decentralized system. Blockchain-based cryptocurrencies are a good example of this. A notable characteristic of cryptocurrencies is the absence of central trusted entities (e.g. banks). But this in turn means that most security precautions related to the handling of digital assets and the execution of transactions are the responsibility of individuals.

Decentralisation vs distribution: It is important to emphasise that by decentralisation we do not mean distribution; although multi-agent systems typically do also tend to be distributed. A distributed system is one whose components are physically located in different places and connected over a network. A fully centralized system, owned and operated by a single entity, may in fact be highly distributed. Google or Microsoft's cloud infrastructure are examples of this, where their components are distributed across the globe yet designed to work together harmoniously and function in unison. Decentralisation on the other hand refers to a system whose components may be owned, operated, and managed by different stakeholders, each with their own personal objectives, interests, and preferences which may not necessarily be aligned with one another or the system itself. Therefore, distribution refers to the physical placement of a system's components, whereas decentralisation refers to a. the diversity of ownership and control over a system's constituents, and b. the absence of central authorities between them.

Example: To better illustrate the distinction between centralized and decentralized systems, consider another example: search and discoverability in a commerce environment. In a centralized system (say Amazon), there is a single search service -- provided, owned and run by the commerce company itself -- which takes care of all search-related functionality for every product within their domain. So to be discoverable in this system, all sellers must register their products with this particular service. However, in a decentralized system, there may not necessarily be a single search service provider. There may be multiple such services, run by different, perhaps competing entities. Each seller has the freedom to register with (i.e. make themselves known to) one or a handful of services. On the buyers side, the more services they contact and query, the higher their chances of finding the product they are looking for.

"},{"location":"aea-framework-documentation/agent-oriented-development/#conflicting-environment","title":"Conflicting Environment","text":"

As discussed above, the notion of decentralisation extends as far as ownership and control. Therefore, the different components that make up a decentralized system may each be owned by a different entity, designed according to very different principles and standards, with heterogeneous software and hardware, and each with internal objectives that may be fundamentally inconsistent, worse yet contradictory, with those of others.

As such, a distinctive characteristic of a multi-agent environment, is that it is inhabited by more than one agent (as the name suggests), where each agent may be owned potentially by a different stakeholder (individual, company, government). Since by design, each agent represents and looks after the interests of its owner(s), and because different stakeholders may have unaligned, conflicting, or contradictory interests, it is very common to have multi-agent systems in which the agents' objectives, values and preferences are unaligned, conflicting, or contradictory.

In practice: There are practical implications that follow from the above when it comes to designing an agent. For example, it is not rational for an agent to automatically rely on the information it receives from other agents. The information could be:

  • Incomplete: what is unrevealed may have been deemed private for strategic reasons.
  • Uncertain: it may be the result of an inaccurate prediction.
  • Incorrect: it could be an outright lie, due to the adversarial nature of the environment.

Therefore, one can argue that there is a degree of uncertainty attached to almost all information an agent receives or infers in a multi-agent system. It wouldn't then be illogical for an agent to take a sceptical approach: treating everything as uncertain, unless proved otherwise.

"},{"location":"aea-framework-documentation/agent-oriented-development/#asynchronization","title":"Asynchronization","text":"

The conflicting nature of multi-agent systems, consisting of self-interested autonomous agents, points to asynchronization as the preferred method of designing and managing processes and interactions.

Synchronisation vs asynchronization: In general, asynchronization refers to the decoupling of events that do interact with one another but do not occur at predetermined intervals, not necessarily relying on each other's existence to function. This is in contrast with synchronous systems in which processes are aware of one another, where one's execution depends in some way on the other.

Asynchronization in MAS: In the context of multi-agent systems, the decentralized and potentially conflicting nature of the environment creates uncertainty over the behaviour of the whole system, in particular of other agents. For example, suppose an agent i sends a message requesting some resources from an agent j. Since MAS often tends to be distributed, there is the usual uncertainties with communication over a network: j may never receive i's request, or may receive it after a long delay. Furthermore, j could receive the request in time and respond immediately, but as mentioned in the last section, its answer might be incomplete (gives only some of the requested resources), uncertain (promises to give the resources, but cannot be fully trusted), or incorrect (sends a wrong resource). In addition, since agents are self-interested, j may decide to reply much later, to the point that the resource is no longer useful to agent i, or j may simply decide not to respond at all. There might be a myriad of reasons why it may choose to do that; it could be because j assigns a low priority to answering i over its other tasks. But that's beside the point. The takeaway is that agents' autonomy strongly influences what can be expected of them, and of an environment inhabited by them. As such, developing for a system whose constituents are autonomous, e.g. agents in a multi-agent system, is fundamentally different from one whose constituents aren't, e.g. objects in an object-oriented system.

Objects vs agents: In object-oriented systems, objects are entities that encapsulate state and perform actions, i.e. call methods, on this state. In object-oriented languages, like C++ and Java, it is common practice to declare methods as public, so they can be invoked by other objects in the system whenever they wish. This implies that an object does not control its own behaviour. If an object\u2019s method is public, the object has no control over whether that method is executed.

We cannot take for granted that an agent j will execute an action (the equivalent of a method in object-oriented systems) just because another agent i wants it to; this action may not be in the best interests of agent j. So we do not think of agents as invoking methods on one another, rather as requesting actions. If i requests j to perform an action, then j may or may not perform the action. It may choose to do it later or do it in exchange for something. The locus of control is therefore different in object-oriented and agent-oriented systems. In the former, the decision lies with the object invoking the method, whereas in the latter, the decision lies with the agent receiving the request. This distinction could be summarised by the following slogan (from An Introduction to MultiAgent Systems by Michael Wooldridge):

objects do it for free; agents do it because they want to.

All of this makes asynchronization the preferred method for designing agent processes and interactions. An agent's interactions should be independent of each other, as much as possible, and of the agent's decision-making processes and actions. This means the success or failure of, or delay in any single interaction does not block the agent's other tasks.

"},{"location":"aea-framework-documentation/agent-oriented-development/#time","title":"Time","text":"

Closely related with the discussion of asynchronicity, is the idea that in multi-agent systems, time is not a universally agreed notion. Agents may not necessarily share the same clock and this fact must be taken into account when designing agent-based systems. For example, you cannot necessarily expect agents to synchronise their behaviour according to time (e.g. perform a certain task at a time X).

Another related issue, is that unlike some agent-based simulation (ABS) systems where there is a global tick rate for all agents, in AEA-based systems tick rates may be different for different agents. This is due to the fundamental difference that ABS systems control some aspects of all of their agents' executions while in AEA-based systems, agents are truly decoupled from one another - most likely distributed and running on different machines and networks - and there is absolutely no central unit that moderates any aspect of their behaviour.

"},{"location":"aea-framework-documentation/agent-oriented-development/#complex-incomplete-inconsistent-and-uncertain","title":"Complex, Incomplete, Inconsistent and Uncertain","text":"

The fourth characteristic(s) relate to the environment in which agents are expected to operate in, and these have been mentioned a number of times in the previous sections.

The environment agents are suited for typically tend to be complex, to the point that it is usually impossible for any single agent to perceive the whole of the environment on its own. This means that at any point in time, any agent has a limited knowledge about the state of the environment. In other words, the agents;' information tend to be incomplete due to the complexity and sophistication of the world in which they reside.

Consider an agent which represents a driver-less vehicle. The complexity of the problem of driving on the road makes it impossible for a single vehicle to have an accurate and up-to-date knowledge of the overall state of the world . This means that an agent's model of the world is at best uncertain. For instance, the vehicle, through its sensor may detect green light at a junction, and by being aware of what it means, it may infer that it is safe to cross a junction. However, that simply may not be true as another car in the opposite direction may still cross the junction violating their red light. Therefore, there is uncertainty associated with the knowledge \"it is safe to cross the road because the light is green\", and the agent must recognise that.

Furthermore, the often conflicting nature of the environment means information obtained from multiple sources (agents) may be inconsistent. Again, this must be taken into consideration when designing an agent which is expected to operate successfully in a potentially conflicting environment.

"},{"location":"aea-framework-documentation/agent-oriented-development/#further-reading","title":"Further Reading","text":"
  • Wooldridge, M. (2009). An Introduction to MultiAgent Systems. Wiley, Second edition.
  • Shoham, Y. and Leyton-Brown, K. (2008). Multiagent Systems: Algorithmic, Game-Theoretic, and Logical Foundations. Cambridge University Press
"},{"location":"aea-framework-documentation/agent-vs-aea/","title":"AEAs vs Agents","text":"

AEAs are more than just agents.

In this guide, we show some of the differences in terms of code.

The Build an AEA programmatically guide shows how to programmatically build an AEA. We can build an agent of the Agent class programmatically as well.

First, import the python and application specific libraries. (Get the packages directory from the AEA repository svn export https://github.com/fetchai/agents-aea.git/trunk/packages.)

import os\nimport time\nfrom threading import Thread\nfrom typing import List\nfrom aea.agent import Agent\nfrom aea.configurations.base import ConnectionConfig\nfrom aea.connections.base import Connection\nfrom aea.helpers.file_io import write_with_lock\nfrom aea.identity.base import Identity\nfrom aea.mail.base import Envelope\nfrom packages.fetchai.connections.stub.connection import StubConnection\nfrom packages.fetchai.protocols.default.message import DefaultMessage\n

Unlike an AEA, an Agent does not require a Wallet, LedgerApis or Resources module.

However, we need to implement 4 abstract methods:

  • setup()
  • act()
  • handle_envelope()
  • teardown()

When we run an agent, start() calls setup() and then the main agent loop. The main agent loop calls act(), react() and update() on each tick. When the agent is stopped via stop() then teardown() is called.

Such a lightweight agent can be used to implement simple logic.

"},{"location":"aea-framework-documentation/agent-vs-aea/#code-an-agent","title":"Code an Agent","text":"

We define our Agent which simply receives envelopes, prints the sender address and protocol_id and returns it unopened.

INPUT_FILE = \"input_file\"\nOUTPUT_FILE = \"output_file\"\nclass MyAgent(Agent):\n\"\"\"A simple agent.\"\"\"\ndef __init__(self, identity: Identity, connections: List[Connection]):\n\"\"\"Initialise the agent.\"\"\"\nsuper().__init__(identity, connections)\ndef setup(self):\n\"\"\"Setup the agent.\"\"\"\ndef act(self):\n\"\"\"Act implementation.\"\"\"\nprint(\"Act called for tick {}\".format(self.tick))\ndef handle_envelope(self, envelope: Envelope) -> None:\n\"\"\"\n        Handle envelope.\n        :param envelope: the envelope received\n        :return: None\n        \"\"\"\nprint(\"React called for tick {}\".format(self.tick))\nif (\nenvelope is not None\nand envelope.protocol_specification_id\n== DefaultMessage.protocol_specification_id\n):\nsender = envelope.sender\nreceiver = envelope.to\nenvelope.to = sender\nenvelope.sender = receiver\nenvelope.message = DefaultMessage.serializer.decode(envelope.message_bytes)\nenvelope.message.sender = receiver\nenvelope.message.to = sender\nprint(\n\"Received envelope from {} with protocol_specification_id={}\".format(\nsender, envelope.protocol_specification_id\n)\n)\nself.outbox.put(envelope)\ndef teardown(self):\n\"\"\"Teardown the agent.\"\"\"\n
"},{"location":"aea-framework-documentation/agent-vs-aea/#instantiate-an-agent","title":"Instantiate an Agent","text":"
    # Ensure the input and output files do not exist initially\nif os.path.isfile(INPUT_FILE):\nos.remove(INPUT_FILE)\nif os.path.isfile(OUTPUT_FILE):\nos.remove(OUTPUT_FILE)\n# Create an addresses identity:\nidentity = Identity(\nname=\"my_agent\", address=\"some_address\", public_key=\"public_key\"\n)\n# Set up the stub connection\nconfiguration = ConnectionConfig(\ninput_file_path=INPUT_FILE,\noutput_file_path=OUTPUT_FILE,\nconnection_id=StubConnection.connection_id,\n)\nstub_connection = StubConnection(\nconfiguration=configuration, data_dir=\".\", identity=identity\n)\n# Create our Agent\nmy_agent = MyAgent(identity, [stub_connection])\n
"},{"location":"aea-framework-documentation/agent-vs-aea/#start-the-agent","title":"Start the Agent","text":"

We run the agent from a different thread so that we can still use the main thread to pass it messages.

    # Set the agent running in a different thread\ntry:\nt = Thread(target=my_agent.start)\nt.start()\n# Wait for everything to start up\ntime.sleep(3)\n
"},{"location":"aea-framework-documentation/agent-vs-aea/#send-and-receive-an-envelope","title":"Send and Receive an Envelope","text":"

We use the input and output text files to send an envelope to our agent and receive a response

        # Create a message inside an envelope and get the stub connection to pass it into the agent\nmessage_text = b\"my_agent,other_agent,fetchai/default:1.0.0,\\x12\\r\\x08\\x01*\\t*\\x07\\n\\x05hello,\"\nwith open(INPUT_FILE, \"wb\") as f:\nwrite_with_lock(f, message_text)\n# Wait for the envelope to get processed\ntime.sleep(2)\n# Read the output envelope generated by the agent\nwith open(OUTPUT_FILE, \"rb\") as f:\nprint(\"output message: \" + f.readline().decode(\"utf-8\"))\n
"},{"location":"aea-framework-documentation/agent-vs-aea/#shutdown","title":"Shutdown","text":"

Finally, stop our agent and wait for it to finish

    finally:\n# Shut down the agent\nmy_agent.stop()\nt.join()\n
"},{"location":"aea-framework-documentation/agent-vs-aea/#your-turn","title":"Your Turn","text":"

Now it is your turn to develop a simple agent with the Agent class.

"},{"location":"aea-framework-documentation/agent-vs-aea/#entire-code-listing","title":"Entire Code Listing","text":"

If you just want to copy and paste the entire script in you can find it here:

Click here to see full listing
import os\nimport time\nfrom threading import Thread\nfrom typing import List\nfrom aea.agent import Agent\nfrom aea.configurations.base import ConnectionConfig\nfrom aea.connections.base import Connection\nfrom aea.helpers.file_io import write_with_lock\nfrom aea.identity.base import Identity\nfrom aea.mail.base import Envelope\nfrom packages.fetchai.connections.stub.connection import StubConnection\nfrom packages.fetchai.protocols.default.message import DefaultMessage\nINPUT_FILE = \"input_file\"\nOUTPUT_FILE = \"output_file\"\nclass MyAgent(Agent):\n\"\"\"A simple agent.\"\"\"\ndef __init__(self, identity: Identity, connections: List[Connection]):\n\"\"\"Initialise the agent.\"\"\"\nsuper().__init__(identity, connections)\ndef setup(self):\n\"\"\"Setup the agent.\"\"\"\ndef act(self):\n\"\"\"Act implementation.\"\"\"\nprint(\"Act called for tick {}\".format(self.tick))\ndef handle_envelope(self, envelope: Envelope) -> None:\n\"\"\"\n        Handle envelope.\n        :param envelope: the envelope received\n        :return: None\n        \"\"\"\nprint(\"React called for tick {}\".format(self.tick))\nif (\nenvelope is not None\nand envelope.protocol_specification_id\n== DefaultMessage.protocol_specification_id\n):\nsender = envelope.sender\nreceiver = envelope.to\nenvelope.to = sender\nenvelope.sender = receiver\nenvelope.message = DefaultMessage.serializer.decode(envelope.message_bytes)\nenvelope.message.sender = receiver\nenvelope.message.to = sender\nprint(\n\"Received envelope from {} with protocol_specification_id={}\".format(\nsender, envelope.protocol_specification_id\n)\n)\nself.outbox.put(envelope)\ndef teardown(self):\n\"\"\"Teardown the agent.\"\"\"\ndef run():\n\"\"\"Run demo.\"\"\"\n# Ensure the input and output files do not exist initially\nif os.path.isfile(INPUT_FILE):\nos.remove(INPUT_FILE)\nif os.path.isfile(OUTPUT_FILE):\nos.remove(OUTPUT_FILE)\n# Create an addresses identity:\nidentity = Identity(\nname=\"my_agent\", address=\"some_address\", public_key=\"public_key\"\n)\n# Set up the stub connection\nconfiguration = ConnectionConfig(\ninput_file_path=INPUT_FILE,\noutput_file_path=OUTPUT_FILE,\nconnection_id=StubConnection.connection_id,\n)\nstub_connection = StubConnection(\nconfiguration=configuration, data_dir=\".\", identity=identity\n)\n# Create our Agent\nmy_agent = MyAgent(identity, [stub_connection])\n# Set the agent running in a different thread\ntry:\nt = Thread(target=my_agent.start)\nt.start()\n# Wait for everything to start up\ntime.sleep(3)\n# Create a message inside an envelope and get the stub connection to pass it into the agent\nmessage_text = b\"my_agent,other_agent,fetchai/default:1.0.0,\\x12\\r\\x08\\x01*\\t*\\x07\\n\\x05hello,\"\nwith open(INPUT_FILE, \"wb\") as f:\nwrite_with_lock(f, message_text)\n# Wait for the envelope to get processed\ntime.sleep(2)\n# Read the output envelope generated by the agent\nwith open(OUTPUT_FILE, \"rb\") as f:\nprint(\"output message: \" + f.readline().decode(\"utf-8\"))\nfinally:\n# Shut down the agent\nmy_agent.stop()\nt.join()\nif __name__ == \"__main__\":\nrun()\n
"},{"location":"aea-framework-documentation/aggregation-demo/","title":"Aggregation Skill","text":"

This demo shows how AEAs can aggregate values over the peer-to-peer network.

"},{"location":"aea-framework-documentation/aggregation-demo/#discussion","title":"Discussion","text":"

This demonstration shows how to set up a simple aggregation network in which several AEAs take an average of values fetched from different sources for the same real-world quantity. For this particular example, we take an average of Bitcoin prices from four public APIs.

"},{"location":"aea-framework-documentation/aggregation-demo/#preparation-instructions","title":"Preparation Instructions","text":""},{"location":"aea-framework-documentation/aggregation-demo/#dependencies","title":"Dependencies","text":"

Follow the Preliminaries and Installation sections from the AEA quick start.

"},{"location":"aea-framework-documentation/aggregation-demo/#demo","title":"Demo","text":""},{"location":"aea-framework-documentation/aggregation-demo/#create-the-aeas","title":"Create the AEAs","text":"

Repeat the following process four times in four different terminals (for each {i=0, i=1, i=2, i=3}):

Fetch the aggregator AEA:

agent_name=\"agg$i\"\naea fetch fetchai/simple_aggregator:0.5.5 --alias $agent_name\ncd $agent_name\naea install\naea build\n
Alternatively, create from scratch:

Create the AEA:

agent_name=\"agg$i\"\naea create agent_name\ncd agent_name\naea add connection fetchai/http_client:0.24.6\naea add connection fetchai/http_server:0.23.6\naea add connection fetchai/p2p_libp2p:0.27.5\naea add connection fetchai/soef:0.27.6\naea add connection fetchai/prometheus:0.9.6\naea add skill fetchai/advanced_data_request:0.7.6\naea add skill fetchai/simple_aggregation:0.3.6\n\naea config set agent.default_connection fetchai/p2p_libp2p:0.27.5\naea install\naea build\n

Set the desired decimal precision for the quantity:

aea config set --type int vendor.fetchai.skills.advanced_data_request.models.advanced_data_request_model.args.decimals 0\n

Disable the http server since it is not used in this demo:

aea config set --type bool vendor.fetchai.skills.advanced_data_request.models.advanced_data_request_model.args.use_http_server false\n

Set the cert requests for the peer-to-peer connection:

aea config set --type list vendor.fetchai.connections.p2p_libp2p.cert_requests \\\n'[{\"identifier\": \"acn\", \"ledger_id\": \"fetchai\", \"not_after\": \"2023-01-01\", \"not_before\": \"2022-01-01\", \"public_key\": \"fetchai\", \"message_format\": \"{public_key}\", \"save_path\": \".certs/conn_cert.txt\"}]'\n

Match the agent index i to the COIN_URL and JSON_PATH below:

  • agg0: COIN_URL=\"https://api.coinbase.com/v2/prices/BTC-USD/buy\" && JSON_PATH=\"data.amount\"
  • agg1: COIN_URL=\"https://api.coinpaprika.com/v1/tickers/btc-bitcoin\" && JSON_PATH=\"quotes.USD.price\"
  • agg2: COIN_URL=\"https://api.cryptowat.ch/markets/kraken/btcusd/price\" && JSON_PATH=\"result.price\"
  • agg3: COIN_URL=\"https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies=usd\" && JSON_PATH=\"bitcoin.usd\"

Set the following configuration for the advanced_data_request skill:

aea config set vendor.fetchai.skills.advanced_data_request.models.advanced_data_request_model.args.url $COIN_URL\naea config set vendor.fetchai.skills.advanced_data_request.models.advanced_data_request_model.args.outputs '[{\"name\": \"price\", \"json_path\": '\"\\\"$JSON_PATH\\\"\"'}]'\n

Set the name of the quantity to aggregate and choose an aggregation function for the AEAs (the currently implemented options are mean, median, and mode):

aea config set vendor.fetchai.skills.simple_aggregation.models.strategy.args.quantity_name price\naea config set vendor.fetchai.skills.simple_aggregation.models.strategy.args.aggregation_function mean\n

Specify a name for your aggregation service:

SERVICE_ID=my_btc_aggregation_service\naea config set vendor.fetchai.skills.simple_aggregation.models.strategy.args.service_id $SERVICE_ID\naea config set vendor.fetchai.skills.simple_aggregation.models.strategy.args.search_query.search_value $SERVICE_ID\n

Additionally, create private keys for use with the ledger and the peer-to-peer connection:

aea generate-key fetchai\naea add-key fetchai\naea generate-key fetchai fetchai_connection_private_key.txt\naea add-key fetchai fetchai_connection_private_key.txt --connection\n

Finally, certify the keys for use by the connections that request them:

aea issue-certificates\n
"},{"location":"aea-framework-documentation/aggregation-demo/#configure-the-peer-to-peer-network","title":"Configure the Peer-to-Peer Network","text":"

Set the multiaddress of the first AEA as an initial peer to help the remaining AEAs find each other on the network. Also, if these AEAs are all running on the same machine, set different ports for their connections to ensure there are no conflicts (from the agg1, agg2, and agg3 directories):

MULTIADDR=$(cd ../agg0 && aea get-multiaddress fetchai --connection)\naea config set --type dict vendor.fetchai.connections.p2p_libp2p.config \\\n'{\n\"delegate_uri\": \"127.0.0.1:'$((11000+i))'\",\n\"entry_peers\": [\"/dns4/127.0.0.1/tcp/9000/p2p/'\"$MULTIADDR\\\"\"'],\n\"local_uri\": \"127.0.0.1:'$((9000+i))'\",\n\"log_file\": \"libp2p_node.log\",\n\"public_uri\": \"127.0.0.1:'$((9000+i))'\"\n}'\naea config set vendor.fetchai.connections.prometheus.config.port $((20000+i))\naea config set vendor.fetchai.connections.http_server.config.port $((8000+i))\n
"},{"location":"aea-framework-documentation/aggregation-demo/#oracle-integration-optional","title":"Oracle Integration (optional)","text":"

To publish the aggregated value to an oracle smart contract, add the ledger connection and simple oracle skill to one of the aggregators:

aea add connection fetchai/ledger:0.21.5\naea add skill fetchai/simple_oracle:0.16.5\n

Configure the simple oracle skill for the fetchai ledger:

aea config set vendor.fetchai.skills.simple_oracle.models.strategy.args.ledger_id fetchai\naea config set vendor.fetchai.skills.simple_oracle.models.strategy.args.update_function update_oracle_value\n

Generate some wealth to use for transactions on the testnet ledger:

aea generate-wealth fetchai\n

Set the name of the oracle value to match the value collected by the aggregators:

aea config set vendor.fetchai.skills.simple_oracle.models.strategy.args.oracle_value_name price_mean\n
"},{"location":"aea-framework-documentation/aggregation-demo/#run-the-aeas","title":"Run the AEAs","text":"

Run each of the aggregator AEAs in separate terminals:

aea run\n

After a few moments, you should see the AEAs finding peers, making observations, sending them to peers, and taking the average of their observations:

info: [agg_i] found agents...\n...\ninfo: [agg_i] Fetching data from...\n...\ninfo: [agg_i] Observation: {'price': {'value':...\n...\ninfo: [agg_i] sending observation to peer...\n...\ninfo: [agg_i] received observation from sender...\n...\ninfo: [agg_i] Observations:...\n...\ninfo: [agg_i] Aggregation (mean):...\n
"},{"location":"aea-framework-documentation/application/","title":"Application Areas","text":""},{"location":"aea-framework-documentation/application/#environments","title":"Environments","text":"

AEAs are most suited for environments which are:

  • Decentralized: there isn't a central authority that controls, manages, or makes decisions.
  • Multi-Stakeholder: the domain, problem, or solutions involve multiple distinct stakeholders.
  • Peer-to-Peer: interactions are (or could be made) direct and peer-to-peer.
  • Complex, Incomplete, and Uncertain: to the point that off-loading tasks to computational entities becomes valuable.
"},{"location":"aea-framework-documentation/application/#applications","title":"Applications","text":"

We identify a number of application areas for AEA-based solutions. This list is by no means comprehensive. In fact, we are most excited about applications which we have not thought of before.

Automation

AEAs can automate well-defined processes in different domains, such as supply chain, mobility, finance, ...

Micro-transactions

AEAs make it economically viable to execute trade involving small values. An example is use-cases with many small sellers (e.g. of data) on the supply side.

Wallet

AEAs can simplify interactions with blockchains. By acting as \"smart wallets\", they can hide away the majority of the complexities involved in using blockchains for end users.

IoT

Agents representing objects in the IoT (Internet of Things) space. For example, AEAs paired with hardware devices such as drones, laptops, heat sensors, etc., providing control and receiving data from the device. An example is a thermometer agent.

Web 2.0 <--> Web 3.0 interface

Agents that interface and bridge the gap between existing (Web 2.0) and new (Web 3.0) economic models. An example is an AEA that communicates with HTTP clients/servers.

Digital data sales

Agents with access to some data sources that sell the data, access to the data, or access to the usage of the data. An example is an AEA that continuously sells data to another AEA, who in turn uses it to improve their reinforcement learning model.

"},{"location":"aea-framework-documentation/application/#multi-agent-system-vs-agent-based-modelling","title":"Multi-Agent System VS Agent-Based Modelling","text":"

The AEA framework enables the creation of multi-agent systems as technological solutions to real world problems.

Although there are some overlap, the framework is not designed from the outset as an agent-based modelling software, where the goal is scientific behavioural observation rather than practical economic gain.

Moreover, there is no restriction to multi; single-agent applications are also supported.

"},{"location":"aea-framework-documentation/aries-cloud-agent-demo/","title":"Aries Cloud Agents Demo","text":"

Note

This demo is incomplete and will soon be updated.

Demonstrating an entire decentralized identity scenario involving AEAs and instances of Aries Cloud Agents (ACAs).

"},{"location":"aea-framework-documentation/aries-cloud-agent-demo/#discussion","title":"Discussion","text":"

This demo corresponds with the one here from Aries cloud agent repository .

The aim of this demo is to illustrate how AEAs can connect to ACAs, thus gaining all of their capabilities, such as issuing and requesting verifiable credentials, selective disclosure and zero knowledge proofs.

    sequenceDiagram\n        participant faea as Faber_AEA\n        participant faca as Faber_ACA\n        participant aaca as Alice_ACA\n        participant aaea as Alice_AEA\n\n        activate faea\n        activate faca\n        activate aaca\n        activate aaea\n\n        Note right of aaea: Shows P2P ID\n\n        faea->>faca: Request status?\n        faca->>faea: status\n        faea->>faca: Register schema\n        faca->>faea: schema_id\n        faea->>faca: Register credential definition\n        faca->>faea: credential_definition_id\n        faea->>faca: create-invitation\n        faca->>faea: connection inc. invitation\n        faea->>aaea: invitation detail\n        aaea->>aaca: receive-invitation\n\n        deactivate faea\n        deactivate faca\n        deactivate aaca\n        deactivate aaea

There are two AEAs:

  • Alice_AEA
  • Faber_AEA

and two ACAs:

  • Alice_ACA
  • Faber_ACA

Each AEA is connected to its corresponding ACA: Alice_AEA to Alice_ACA and Faber_AEA to Faber_ACA.

The following lists the sequence of interactions between the four agents:

  • Alice_AEA: starts
  • Alice_AEA: shows its P2P address in the terminal and waits for an invitation detail from Faber_AEA.
  • Alice_AEA: registers itself on the SOEF.
  • Faber_AEA: starts
  • Faber_AEA: searches the SOEF and finds Alice_AEA.
  • Faber_AEA: tests its connection to Faber_ACA.
  • Faber_ACA: responds to Faber_AEA.
  • Faber_AEA: registers a DID on the ledger.
  • Faber_AEA: request Faber_ACA to register a schema on the ledger.
  • Faber_ACA: responds by sending back the schema_id.
  • Faber_AEA: request Faber_ACA to register a credential definition on the ledger.
  • Faber_ACA: responds by sending back the credential_definition_id.
  • Faber_AEA: requests Faber_ACA to create an invitation.
  • Faber_ACA: responds by sending back the connection detail, which contains an invitation field.
  • Faber_AEA: sends the invitation detail to Alice_AEA.
  • Alice_AEA: receives invitation detail from Faber_AEA.
  • Alice_AEA: requests Alice_ACA to accept the invitation, by passing it the invitation detail it received in the last step.

All messages from an AEA to an ACA are http requests (using http_client connection).

All messages from an AEA to another AEA utilise the P2P communication network accessed via the p2p_libp2p connection.

All messages initiated from an ACA to an AEA are webhooks (using webhook connection).

This is the extent of the demo at this point. The rest of the interactions require an instance of the Indy ledger to run. This is what will be implemented next.

The rest of the interactions are broadly as follows:

  • Alice_ACA: accepts the invitation.
  • Alice_ACA: sends a matching invitation request to Faber_ACA.
  • Faber_ACA: accepts

At this point, the two ACAs are connected to each other.

  • Faber_AEA: requests Faber_ACA to issue a credential (e.g. university degree) to Alice_AEA, which Faber_ACA does via Alice_ACA.
  • Faber_AEA: requests proof that Alice_AEA's age is above 18.
  • Alice_AEA: presents proof that it's age is above 18, without presenting its credential.
"},{"location":"aea-framework-documentation/aries-cloud-agent-demo/#preparation-instructions","title":"Preparation Instructions","text":""},{"location":"aea-framework-documentation/aries-cloud-agent-demo/#dependencies","title":"Dependencies","text":"

Follow the Preliminaries and Installation sections from the AEA quick start.

Install Aries cloud-agents (for more info see here) if you do not have it on your machine:

pip install aries-cloudagent\n

This demo has been successfully tested with aca-py version 0.4.5.

This demo requires an instance of von network running in docker locally (for more info see here)

This demo has been successfully tested with the von-network git repository pulled on 07 Aug 2020 (commit number ad1f84f64d4f4c106a81462f5fbff496c5fbf10e).

"},{"location":"aea-framework-documentation/aries-cloud-agent-demo/#terminals","title":"Terminals","text":"

Open five terminals. The first terminal is used to run an instance of von-network locally in docker. The other four terminals will be used to run each of the four agents in this demo.

"},{"location":"aea-framework-documentation/aries-cloud-agent-demo/#von-network","title":"VON Network","text":"

In the first terminal move to the von-network directory and run an instance of von-network locally in docker.

This tutorial has information on starting (and stopping) the network locally.

./manage build\n./manage start --logs\n

Once the ledger is running, you can see the ledger by going to the web server running on port 9000. On localhost, that means going to http://localhost:9000.

"},{"location":"aea-framework-documentation/aries-cloud-agent-demo/#alice-and-faber-acas","title":"Alice and Faber ACAs","text":"

To learn about the command for starting an ACA and its various options:

aca-py start --help\n
"},{"location":"aea-framework-documentation/aries-cloud-agent-demo/#faber_aca","title":"Faber_ACA","text":"

In the first terminal:

aca-py start --admin 127.0.0.1 8021 --admin-insecure-mode --inbound-transport http 0.0.0.0 8020 --outbound-transport http --webhook-url http://127.0.0.1:8022/webhooks\n

Make sure the ports above are unused.

Take note of the specific IP addresses and ports you used in the above command. We will refer to them by the following names:

  • Faber admin IP: 127.0.0.1
  • Faber admin port: 8021
  • Faber webhook port: 8022

The admin IP and port will be used to send administrative commands to this ACA from an AEA.

The webhook port is where the ACA will send notifications to. We will expose this from the AEA so it receives this ACA's notifications.

"},{"location":"aea-framework-documentation/aries-cloud-agent-demo/#alice_aca","title":"Alice_ACA","text":"

In the second terminal:

aca-py start --admin 127.0.0.1 8031 --admin-insecure-mode --inbound-transport http 0.0.0.0 8030 --outbound-transp http --webhook-url http://127.0.0.1:8032/webhooks\n

Again, make sure the above ports are unused and take note of the specific IP addresses and ports. In this case:

  • Alice admin IP: 127.0.0.1
  • Alice admin port: 8031
  • Alice webhook port: 8032
"},{"location":"aea-framework-documentation/aries-cloud-agent-demo/#alice-and-faber-aeas","title":"Alice and Faber AEAs","text":"

Now you can create Alice_AEA and Faber_AEA in terminals 3 and 4 respectively.

"},{"location":"aea-framework-documentation/aries-cloud-agent-demo/#alice_aea","title":"Alice_AEA","text":"

In the third terminal, fetch Alice_AEA and move into its project folder:

aea fetch fetchai/aries_alice:0.32.5\ncd aries_alice\n
Alternatively, create from scratch:

The following steps create Alice_AEA from scratch:

aea create aries_alice\ncd aries_alice\naea add connection fetchai/p2p_libp2p:0.27.5\naea add connection fetchai/soef:0.27.6\naea add connection fetchai/http_client:0.24.6\naea add connection fetchai/webhook:0.20.6\naea add skill fetchai/aries_alice:0.26.6\n
"},{"location":"aea-framework-documentation/aries-cloud-agent-demo/#configure-the-aries_alice-skill","title":"Configure the aries_alice Skill","text":"

(configuration file: alice/vendor/fetchai/skills/aries_alice/skill.yaml)

Ensure admin_host and admin_port values match with the values you noted above for Alice_ACA. You can use the framework's handy config CLI command to set these values:

aea config set vendor.fetchai.skills.aries_alice.models.strategy.args.admin_host 127.0.0.1\n
aea config set --type int vendor.fetchai.skills.aries_alice.models.strategy.args.admin_port 8031\n
"},{"location":"aea-framework-documentation/aries-cloud-agent-demo/#configure-the-webhook-connection","title":"Configure the webhook Connection","text":"

(configuration file: alice/vendor/fetchai/connections/webhook/connection.yaml).

First ensure the value of webhook_port matches with what you used above for Alice_ACA.

aea config set --type int vendor.fetchai.connections.webhook.config.webhook_port 8032\n

Next, make sure the value of webhook_url_path is /webhooks/topic/{topic}/.

aea config set vendor.fetchai.connections.webhook.config.webhook_url_path /webhooks/topic/{topic}/\n
"},{"location":"aea-framework-documentation/aries-cloud-agent-demo/#configure-the-p2p_libp2p-connection","title":"Configure the p2p_libp2p Connection","text":"
aea config set --type dict vendor.fetchai.connections.p2p_libp2p.config \\\n'{\n  \"delegate_uri\": \"127.0.0.1:11000\",\n  \"entry_peers\": [],\n  \"local_uri\": \"127.0.0.1:7000\",\n  \"log_file\": \"libp2p_node.log\",\n  \"public_uri\": \"127.0.0.1:7000\"\n}'\n
"},{"location":"aea-framework-documentation/aries-cloud-agent-demo/#install-the-dependencies-and-run-alice_aea","title":"Install the Dependencies and Run Alice_AEA","text":"

Now install all the dependencies:

aea install\naea build\n

Finally, run Alice_AEA:

aea run\n

Once you see a message of the form To join its network use multiaddr 'SOME_ADDRESS' take note of the address. (Alternatively, use aea get-multiaddress fetchai -c -i fetchai/p2p_libp2p:0.27.5 -u public_uri to retrieve the address.) We will refer to this as Alice_AEA's P2P address.

"},{"location":"aea-framework-documentation/aries-cloud-agent-demo/#faber_aea","title":"Faber_AEA","text":"

In the fourth terminal, fetch Faber_AEA and move into its project folder:

aea fetch fetchai/aries_faber:0.32.5\ncd aries_faber\n
Alternatively, create from scratch:

The following steps create Faber_AEA from scratch:

aea create aries_faber\ncd aries_faber\naea add connection fetchai/p2p_libp2p:0.27.5\naea add connection fetchai/soef:0.27.6\naea add connection fetchai/http_client:0.24.6\naea add connection fetchai/webhook:0.20.6\naea add skill fetchai/aries_faber:0.24.5\n
"},{"location":"aea-framework-documentation/aries-cloud-agent-demo/#configure-the-aries_faber-skill","title":"Configure the aries_faber Skill","text":"

(configuration file: faber/vendor/fetchai/skills/aries_alice/skill.yaml)

Ensure admin_host and admin_port values match with those you noted above for Faber_ACA.

aea config set vendor.fetchai.skills.aries_faber.models.strategy.args.admin_host 127.0.0.1\n
aea config set --type int vendor.fetchai.skills.aries_faber.models.strategy.args.admin_port 8021\n
"},{"location":"aea-framework-documentation/aries-cloud-agent-demo/#configure-the-webhook-connection_1","title":"Configure the webhook Connection","text":"

(configuration file: faber/vendor/fetchai/connections/webhook/connection.yaml).

First, ensure the value of webhook_port matches with what you used above for Faber_ACA.

aea config set --type int vendor.fetchai.connections.webhook.config.webhook_port 8022\n

Next, make sure the value of webhook_url_path is /webhooks/topic/{topic}/.

aea config set vendor.fetchai.connections.webhook.config.webhook_url_path /webhooks/topic/{topic}/\n
"},{"location":"aea-framework-documentation/aries-cloud-agent-demo/#configure-the-p2p_libp2p-connection_1","title":"Configure the p2p_libp2p Connection","text":"
aea config set --type dict vendor.fetchai.connections.p2p_libp2p.config \\\n'{\n  \"delegate_uri\": \"127.0.0.1:11001\",\n  \"entry_peers\": [\"SOME_ADDRESS\"],\n  \"local_uri\": \"127.0.0.1:7001\",\n  \"log_file\": \"libp2p_node.log\",\n  \"public_uri\": \"127.0.0.1:7001\"\n}'\n

where SOME_ADDRESS is Alice_AEA's P2P address as displayed in the third terminal.

"},{"location":"aea-framework-documentation/aries-cloud-agent-demo/#install-the-dependencies-and-run-faber_aea","title":"Install the Dependencies and Run Faber_AEA","text":"

Now install all the dependencies:

aea install\naea build\n

Finally run Faber_AEA:

aea run\n

You should see Faber_AEA running and showing logs of its activities. For example:

Looking now at Alice_AEA terminal, you should also see more activity by Alice_AEA after Faber_AEA was started. For example:

The last error line in Alice_AEA's terminal is caused due to the absence of an Indy ledger instance. In the next update to this demo, this will be resolved.

"},{"location":"aea-framework-documentation/aries-cloud-agent-demo/#terminate-and-delete-the-agents","title":"Terminate and Delete the Agents","text":"

You can terminate each agent by pressing Ctrl+C.

To delete the AEAs, go to the projects' parent directory and delete the AEAs:

aea delete aries_faber\naea delete aries_alice\n
"},{"location":"aea-framework-documentation/aries-cloud-agent-demo/#further-developments","title":"Further Developments","text":"

In the next update to this demo, the remaining interactions between AEAs and ACAs must be implemented. This means:

  • An instance of Indy ledger must be installed and running. See here for more detail.
  • The commands for running the ACAs need to be adjusted. Additional options relating to a wallet (wallet-name, type, key, storage-type, configuration, credentials) need to be fed to the ACAs as well as the ledger's genesis file so the ACAs can connect to the ledger.
  • The remaining interactions between the AEAs and ACAs as described here need to be implemented.
"},{"location":"aea-framework-documentation/build-aea-programmatically/","title":"Build an AEA Programmatically","text":"

These instructions detail the Python code you need for running an AEA outside the cli tool, using the code interface.

"},{"location":"aea-framework-documentation/build-aea-programmatically/#preparation","title":"Preparation","text":"

Get the packages directory from the AEA repository:

svn export https://github.com/fetchai/agents-aea.git/trunk/packages\n

Also, install aea-ledger-fetchai plug-in:

pip install aea-ledger-fetchai\n
"},{"location":"aea-framework-documentation/build-aea-programmatically/#imports","title":"Imports","text":"

First, import the necessary common Python libraries and classes.

import os\nimport time\nfrom threading import Thread\n

Then, import the application specific libraries.

from aea_ledger_fetchai import FetchAICrypto\nfrom aea.aea_builder import AEABuilder\nfrom aea.configurations.base import SkillConfig\nfrom aea.crypto.helpers import PRIVATE_KEY_PATH_SCHEMA, create_private_key\nfrom aea.helpers.file_io import write_with_lock\nfrom aea.skills.base import Skill\n

Set up a variable pointing to where the packages directory is located - this should be our current directory - and where the input and output files are located.

ROOT_DIR = \"./\"\nINPUT_FILE = \"input_file\"\nOUTPUT_FILE = \"output_file\"\nFETCHAI_PRIVATE_KEY_FILE = PRIVATE_KEY_PATH_SCHEMA.format(FetchAICrypto.identifier)\n
"},{"location":"aea-framework-documentation/build-aea-programmatically/#create-a-private-key","title":"Create a Private Key","text":"

We need a private key to populate the AEA's wallet.

    # Create a private key\ncreate_private_key(FetchAICrypto.identifier, FETCHAI_PRIVATE_KEY_FILE)\n
"},{"location":"aea-framework-documentation/build-aea-programmatically/#clearing-the-input-and-output-files","title":"Clearing the Input and Output Files","text":"

We will use the stub connection to pass envelopes in and out of the AEA. Ensure that any input and output text files are removed before we start.

    # Ensure the input and output files do not exist initially\nif os.path.isfile(INPUT_FILE):\nos.remove(INPUT_FILE)\nif os.path.isfile(OUTPUT_FILE):\nos.remove(OUTPUT_FILE)\n
"},{"location":"aea-framework-documentation/build-aea-programmatically/#initialise-the-aea","title":"Initialise the AEA","text":"

We use the AEABuilder to readily build an AEA. By default, the AEABuilder adds the fetchai/default:1.1.7, fetchai/state_update:1.1.7 and fetchai/signing:1.1.7 protocols.

    # Instantiate the builder and build the AEA\n# By default, the default protocol, error skill and stub connection are added\nbuilder = AEABuilder()\n

We set the name, add the private key for the AEA to use and set the ledger configurations for the AEA to use.

    builder.set_name(\"my_aea\")\nbuilder.add_private_key(FetchAICrypto.identifier, FETCHAI_PRIVATE_KEY_FILE)\n

Next, we add the fetchai/stub:0.15.0 connection which will read/write messages from file:

    # Add the stub connection (assuming it is present in the local directory 'packages')\nbuilder.add_connection(\"./packages/fetchai/connections/stub\")\n

Next, we add the echo skill which will bounce our messages back to us. We first need to place the echo skill into a relevant directory (see path), either by downloading the packages directory from the AEA repo or by getting the package from the registry.

    # Add the echo skill (assuming it is present in the local directory 'packages')\nbuilder.add_skill(\"./packages/fetchai/skills/echo\")\n

Also, we can add a component that was instantiated programmatically. :

    # create skill and handler manually\nfrom aea.protocols.base import Message\nfrom aea.skills.base import Handler\nfrom packages.fetchai.protocols.default.message import DefaultMessage\nclass DummyHandler(Handler):\n\"\"\"Dummy handler to handle messages.\"\"\"\nSUPPORTED_PROTOCOL = DefaultMessage.protocol_id\ndef setup(self) -> None:\n\"\"\"Noop setup.\"\"\"\ndef teardown(self) -> None:\n\"\"\"Noop teardown.\"\"\"\ndef handle(self, message: Message) -> None:\n\"\"\"Handle incoming message.\"\"\"\nself.context.logger.info(\"You got a message: {}\".format(str(message)))\nconfig = SkillConfig(name=\"test_skill\", author=\"fetchai\")\nskill = Skill(configuration=config)\ndummy_handler = DummyHandler(\nname=\"dummy_handler\", skill_context=skill.skill_context\n)\nskill.handlers.update({dummy_handler.name: dummy_handler})\nbuilder.add_component_instance(skill)\n

Finally, we can build our AEA:

    # Create our AEA\nmy_aea = builder.build()\n
"},{"location":"aea-framework-documentation/build-aea-programmatically/#start-the-aea","title":"Start the AEA","text":"

We run the AEA from a different thread so that we can still use the main thread to pass it messages.

    # Set the AEA running in a different thread\ntry:\nt = Thread(target=my_aea.start)\nt.start()\n# Wait for everything to start up\ntime.sleep(4)\n
"},{"location":"aea-framework-documentation/build-aea-programmatically/#send-and-receive-an-envelope","title":"Send and Receive an Envelope","text":"

We use the input and output text files to send an envelope to our AEA and receive a response (from the echo skill)

        # Create a message inside an envelope and get the stub connection to pass it on to the echo skill\nmessage_text = b\"my_aea,other_agent,fetchai/default:1.0.0,\\x12\\x10\\x08\\x01\\x12\\x011*\\t*\\x07\\n\\x05hello,\"\nwith open(INPUT_FILE, \"wb\") as f:\nwrite_with_lock(f, message_text)\nprint(b\"input message: \" + message_text)\n# Wait for the envelope to get processed\ntime.sleep(4)\n# Read the output envelope generated by the echo skill\nwith open(OUTPUT_FILE, \"rb\") as f:\nprint(b\"output message: \" + f.readline())\n
"},{"location":"aea-framework-documentation/build-aea-programmatically/#shutdown","title":"Shutdown","text":"

Finally, stop our AEA and wait for it to finish

    finally:\n# Shut down the AEA\nmy_aea.stop()\nt.join()\nt = None\n
"},{"location":"aea-framework-documentation/build-aea-programmatically/#running-the-aea","title":"Running the AEA","text":"

If you now run this python script file, you should see this output:

input message: my_aea,other_agent,fetchai/default:1.0.0,\\x12\\x10\\x08\\x01\\x12\\x011*\\t*\\x07\\n\\x05hello,\noutput message: other_agent,my_aea,fetchai/default:1.0.0,...\\x05hello\n
"},{"location":"aea-framework-documentation/build-aea-programmatically/#entire-code-listing","title":"Entire Code Listing","text":"

If you just want to copy and past the entire script in you can find it here:

Click here to see full listing:
import os\nimport time\nfrom threading import Thread\nfrom aea_ledger_fetchai import FetchAICrypto\nfrom aea.aea_builder import AEABuilder\nfrom aea.configurations.base import SkillConfig\nfrom aea.crypto.helpers import PRIVATE_KEY_PATH_SCHEMA, create_private_key\nfrom aea.helpers.file_io import write_with_lock\nfrom aea.skills.base import Skill\nROOT_DIR = \"./\"\nINPUT_FILE = \"input_file\"\nOUTPUT_FILE = \"output_file\"\nFETCHAI_PRIVATE_KEY_FILE = PRIVATE_KEY_PATH_SCHEMA.format(FetchAICrypto.identifier)\ndef run():\n\"\"\"Run demo.\"\"\"\n# Create a private key\ncreate_private_key(FetchAICrypto.identifier, FETCHAI_PRIVATE_KEY_FILE)\n# Ensure the input and output files do not exist initially\nif os.path.isfile(INPUT_FILE):\nos.remove(INPUT_FILE)\nif os.path.isfile(OUTPUT_FILE):\nos.remove(OUTPUT_FILE)\n# Instantiate the builder and build the AEA\n# By default, the default protocol, error skill and stub connection are added\nbuilder = AEABuilder()\nbuilder.set_name(\"my_aea\")\nbuilder.add_private_key(FetchAICrypto.identifier, FETCHAI_PRIVATE_KEY_FILE)\n# Add the stub connection (assuming it is present in the local directory 'packages')\nbuilder.add_connection(\"./packages/fetchai/connections/stub\")\n# Add the echo skill (assuming it is present in the local directory 'packages')\nbuilder.add_skill(\"./packages/fetchai/skills/echo\")\n# create skill and handler manually\nfrom aea.protocols.base import Message\nfrom aea.skills.base import Handler\nfrom packages.fetchai.protocols.default.message import DefaultMessage\nclass DummyHandler(Handler):\n\"\"\"Dummy handler to handle messages.\"\"\"\nSUPPORTED_PROTOCOL = DefaultMessage.protocol_id\ndef setup(self) -> None:\n\"\"\"Noop setup.\"\"\"\ndef teardown(self) -> None:\n\"\"\"Noop teardown.\"\"\"\ndef handle(self, message: Message) -> None:\n\"\"\"Handle incoming message.\"\"\"\nself.context.logger.info(\"You got a message: {}\".format(str(message)))\nconfig = SkillConfig(name=\"test_skill\", author=\"fetchai\")\nskill = Skill(configuration=config)\ndummy_handler = DummyHandler(\nname=\"dummy_handler\", skill_context=skill.skill_context\n)\nskill.handlers.update({dummy_handler.name: dummy_handler})\nbuilder.add_component_instance(skill)\n# Create our AEA\nmy_aea = builder.build()\n# Set the AEA running in a different thread\ntry:\nt = Thread(target=my_aea.start)\nt.start()\n# Wait for everything to start up\ntime.sleep(4)\n# Create a message inside an envelope and get the stub connection to pass it on to the echo skill\nmessage_text = b\"my_aea,other_agent,fetchai/default:1.0.0,\\x12\\x10\\x08\\x01\\x12\\x011*\\t*\\x07\\n\\x05hello,\"\nwith open(INPUT_FILE, \"wb\") as f:\nwrite_with_lock(f, message_text)\nprint(b\"input message: \" + message_text)\n# Wait for the envelope to get processed\ntime.sleep(4)\n# Read the output envelope generated by the echo skill\nwith open(OUTPUT_FILE, \"rb\") as f:\nprint(b\"output message: \" + f.readline())\nfinally:\n# Shut down the AEA\nmy_aea.stop()\nt.join()\nt = None\nif __name__ == \"__main__\":\nrun()\n
"},{"location":"aea-framework-documentation/build-aea-step-by-step/","title":"Build an AEA with the CLI","text":"

Building an AEA step by step (ensure you have followed the Preliminaries and Installation sections from the AEA quick start first):

  1. Set up your AEA project with the CLI: aea create my_aea && cd my_aea
  2. Look at, then add the right connections for your use case: aea search connections, then aea add connection [public_id]
  3. Look for, then add or generate the protocols you require: aea search protocols, then aea add protocol [public_id] or aea generate protocol [path_to_specification]
  4. Look for, then add or code the skills you need: aea search skills, then aea add skill [public_id]. This guide shows you step by step how to develop a skill.
  5. Where required, scaffold any of the above resources with the scaffolding tool or generate a protocol with the protocol generator.
  6. Now, run your AEA: aea run --connections [public_id]

See information on the CLI tool here for all the available commands.

"},{"location":"aea-framework-documentation/car-park-skills/","title":"Car park skills","text":"

The AEA car-park skills demonstrate an interaction between two AEAs.

  • The carpark_detection AEA provides information on the number of car parking spaces available in a given vicinity.
  • The carpark_client AEA is interested in purchasing information on available car parking spaces in the same vicinity.
"},{"location":"aea-framework-documentation/car-park-skills/#discussion","title":"Discussion","text":"

The full Fetch.ai car park AEA demo is documented in its own repo here. This demo allows you to test the AEA functionality of the car park AEA demo without the detection logic.

It demonstrates how the AEAs trade car park information.

"},{"location":"aea-framework-documentation/car-park-skills/#communication","title":"Communication","text":"

This diagram shows the communication between the various entities as data is successfully sold by the car park AEA to the client.

    sequenceDiagram\n        participant Search\n        participant Car_Data_Buyer_AEA\n        participant Car_Park_AEA\n        participant Blockchain\n\n        activate Search\n        activate Car_Data_Buyer_AEA\n        activate Car_Park_AEA\n        activate Blockchain\n\n        Car_Park_AEA->>Search: register_service\n        Car_Data_Buyer_AEA->>Search: search\n        Search-->>Car_Data_Buyer_AEA: list_of_agents\n        Car_Data_Buyer_AEA->>Car_Park_AEA: call_for_proposal\n        Car_Park_AEA->>Car_Data_Buyer_AEA: propose\n        Car_Data_Buyer_AEA->>Car_Park_AEA: accept\n        Car_Park_AEA->>Car_Data_Buyer_AEA: match_accept\n        Car_Data_Buyer_AEA->>Blockchain: transfer_funds\n        Car_Data_Buyer_AEA->>Car_Park_AEA: send_transaction_hash\n        Car_Park_AEA->>Blockchain: check_transaction_status\n        Car_Park_AEA->>Car_Data_Buyer_AEA: send_data\n\n        deactivate Search\n        deactivate Car_Data_Buyer_AEA\n        deactivate Car_Park_AEA\n        deactivate Blockchain
"},{"location":"aea-framework-documentation/car-park-skills/#option-1-aea-manager-approach","title":"Option 1: AEA Manager Approach","text":"

Follow this approach when using the AEA Manager Desktop app. Otherwise, skip and follow the CLI approach below.

"},{"location":"aea-framework-documentation/car-park-skills/#preparation-instructions","title":"Preparation Instructions","text":"

Install the AEA Manager.

"},{"location":"aea-framework-documentation/car-park-skills/#demo-instructions","title":"Demo Instructions","text":"

The following steps assume you have launched the AEA Manager Desktop app.

  1. Add a new AEA called car_detector with public id fetchai/car_detector:0.32.5.

  2. Add another new AEA called car_data_buyer with public id fetchai/car_data_buyer:0.33.5.

  3. Copy the address from the car_data_buyer into your clip board. Then go to the Dorado block explorer and request some test tokens via Get Funds.

  4. Run the car_detector AEA. Navigate to its logs and copy the multiaddress displayed.

  5. Navigate to the settings of the car_data_buyer and under components > connection > fetchai/p2p_libp2p:0.22.0 update as follows (make sure to replace the placeholder with the multiaddress):

    {\n\"delegate_uri\": \"127.0.0.1:11001\",\n  \"entry_peers\": [\"REPLACE_WITH_MULTI_ADDRESS_HERE\"],\n  \"local_uri\": \"127.0.0.1:9001\",\n  \"log_file\": \"libp2p_node.log\",\n  \"public_uri\": \"127.0.0.1:9001\"\n}\n
  6. Run the car_data_buyer.

In the AEA's logs, you should see the agent trading successfully.

"},{"location":"aea-framework-documentation/car-park-skills/#option-2-cli-approach","title":"Option 2: CLI Approach","text":"

Follow this approach when using the aea CLI.

"},{"location":"aea-framework-documentation/car-park-skills/#preparation-instructions_1","title":"Preparation Instructions","text":""},{"location":"aea-framework-documentation/car-park-skills/#dependencies","title":"Dependencies","text":"

Follow the Preliminaries and Installation sections from the AEA quick start.

"},{"location":"aea-framework-documentation/car-park-skills/#demo-instructions_1","title":"Demo Instructions","text":""},{"location":"aea-framework-documentation/car-park-skills/#create-car-detector-aea","title":"Create Car Detector AEA","text":"

First, fetch the car detector AEA:

aea fetch fetchai/car_detector:0.32.5\ncd car_detector\naea install\naea build\n
Alternatively, create from scratch:

The following steps create the car detector from scratch:

aea create car_detector\ncd car_detector\naea add connection fetchai/p2p_libp2p:0.27.5\naea add connection fetchai/soef:0.27.6\naea add connection fetchai/ledger:0.21.5\naea add skill fetchai/carpark_detection:0.27.6\naea config set --type dict agent.dependencies \\\n'{\n  \"aea-ledger-fetchai\": {\"version\": \"<2.0.0,>=1.0.0\"}\n}'\naea config set agent.default_connection fetchai/p2p_libp2p:0.27.5\naea config set --type dict agent.default_routing \\\n'{\n  \"fetchai/ledger_api:1.1.7\": \"fetchai/ledger:0.21.5\",\n  \"fetchai/oef_search:1.1.7\": \"fetchai/soef:0.27.6\"\n}'\naea install\naea build\n
"},{"location":"aea-framework-documentation/car-park-skills/#create-car-data-buyer-aea","title":"Create Car Data Buyer AEA","text":"

Then, fetch the car data client AEA:

aea fetch fetchai/car_data_buyer:0.33.5\ncd car_data_buyer\naea install\naea build\n
Alternatively, create from scratch:

The following steps create the car data client from scratch:

aea create car_data_buyer\ncd car_data_buyer\naea add connection fetchai/p2p_libp2p:0.27.5\naea add connection fetchai/soef:0.27.6\naea add connection fetchai/ledger:0.21.5\naea add skill fetchai/carpark_client:0.27.6\naea config set --type dict agent.dependencies \\\n'{\n  \"aea-ledger-fetchai\": {\"version\": \"<2.0.0,>=1.0.0\"}\n}'\naea config set agent.default_connection fetchai/p2p_libp2p:0.27.5\naea config set --type dict agent.default_routing \\\n'{\n  \"fetchai/ledger_api:1.1.7\": \"fetchai/ledger:0.21.5\",\n  \"fetchai/oef_search:1.1.7\": \"fetchai/soef:0.27.6\"\n}'\naea install\naea build\n
"},{"location":"aea-framework-documentation/car-park-skills/#add-keys-for-the-car-data-seller-aea","title":"Add Keys for the Car Data Seller AEA","text":"

First, create the private key for the car data seller AEA based on the network you want to transact. To generate and add a private-public key pair for Fetch.ai Dorado use:

aea generate-key fetchai\naea add-key fetchai fetchai_private_key.txt\n

Next, create a private key used to secure the AEA's communications:

aea generate-key fetchai fetchai_connection_private_key.txt\naea add-key fetchai fetchai_connection_private_key.txt --connection\n

Finally, certify the key for use by the connections that request that:

aea issue-certificates\n
"},{"location":"aea-framework-documentation/car-park-skills/#add-keys-and-generate-wealth-for-the-car-data-buyer-aea","title":"Add Keys and Generate Wealth for the Car Data Buyer AEA","text":"

The buyer needs to have some wealth to purchase the service from the seller.

First, create the private key for the car data buyer AEA based on the network you want to transact. To generate and add a private-public key pair for Fetch.ai Dorado use:

aea generate-key fetchai\naea add-key fetchai fetchai_private_key.txt\n

Then, create some wealth for your car data buyer based on the network you want to transact with. On the Fetch.ai Dorado network:

aea generate-wealth fetchai\n

Next, create a private key used to secure the AEA's communications:

aea generate-key fetchai fetchai_connection_private_key.txt\naea add-key fetchai fetchai_connection_private_key.txt --connection\n

Finally, certify the key for use by the connections that request that:

aea issue-certificates\n
"},{"location":"aea-framework-documentation/car-park-skills/#run-the-aeas","title":"Run the AEAs","text":"

Run both AEAs from their respective terminals.

First, run the car data seller AEA:

aea run\n

Once you see a message of the form To join its network use multiaddr 'SOME_ADDRESS' take note of the address. (Alternatively, use aea get-multiaddress fetchai -c -i fetchai/p2p_libp2p:0.27.5 -u public_uri to retrieve the address.) This is the entry peer address for the local agent communication network created by the car data seller.

Then, in the car data buyer, run this command (replace SOME_ADDRESS with the correct value as described above):

aea config set --type dict vendor.fetchai.connections.p2p_libp2p.config \\\n'{\n  \"delegate_uri\": \"127.0.0.1:11001\",\n  \"entry_peers\": [\"SOME_ADDRESS\"],\n  \"local_uri\": \"127.0.0.1:9001\",\n  \"log_file\": \"libp2p_node.log\",\n  \"public_uri\": \"127.0.0.1:9001\"\n}'\n

This allows the car data buyer to connect to the same local agent communication network as the car data seller.

Then run the buyer AEA:

aea run\n

You will see that the AEAs negotiate and then transact using the Fetch.ai testnet.

"},{"location":"aea-framework-documentation/car-park-skills/#cleaning-up","title":"Cleaning up","text":"

When you're finished, delete your AEAs:

cd ..\naea delete car_detector\naea delete car_data_buyer\n
"},{"location":"aea-framework-documentation/cli-commands/","title":"CLI Commands","text":"Command Description add [package_type] [public_id] Add a package_type connection, contract, protocol, or skill, with [public_id], to the AEA. add --local to add from local packages directory. add-key [ledger_id] file [--connection] Add a private key from a file for ledger_id. build Build the agent and its components. config get [path] Reads the configuration specified in path and prints its target. config set [path] [--type TYPE] Sets a new value for the target of the path. Optionally cast to type. create [name] Create a new AEA project called name. delete [name] Delete an AEA project. See below for disabling a resource. eject [package_type] [public_id] Move a package of package_type and package_id from vendor to project working directory. fetch [public_id] Fetch an AEA project with public_id. fetch --local to fetch from local packages directory. fingerprint [package_type] [public_id] Fingerprint connection, contract, protocol, or skill, with public_id. freeze Get all the dependencies needed for the AEA project and its components. generate protocol [protocol_spec_path] Generate a protocol from the specification. generate-key [ledger_id] Generate private keys. The AEA uses a private key to derive the associated public key and address. generate-wealth [ledger_id] Generate wealth for address on test network. get-address [ledger_id] Get the address associated with the private key. get-multiaddress [ledger_id]... Get the multiaddress associated with a private key or connection. get-public-key [ledger_id]... Get the public key associated with a private key of the agent. get-wealth [ledger_id] Get the wealth associated with the private key. init Initialize your AEA configurations. (With --author to define author.) install [-r <requirements_file>] Install the dependencies. (With --install-deps to install dependencies.) interact Interact with a running AEA via the stub connection. ipfs IPFS Commands issue-certificates Issue the connection certificates. launch [path_to_agent_project]... Launch many agents at the same time. list [package_type] List the installed resources. local-registry-sync Upgrade the local package registry. login USERNAME [--password password] Login to a registry account with credentials. logout Logout from registry account. publish Publish the AEA to registry. Needs to be executed from an AEA project.publish --local to publish to local packages directory. push [package_type] [public_id] Push connection, protocol, or skill with public_id to registry. push --local to push to local packages directory. register Create a new registry account. remove [package_type] [name] Remove connection, protocol, or skill, called name, from AEA. remove-key [ledger_id] [name] Remove a private key registered with id ledger_id. reset_password EMAIL Reset the password of the registry account. run {using [connections, ...]} Run the AEA on the Fetch.ai network with default or specified connections. scaffold [package_type] [name] Scaffold a new connection, protocol, or skill called name. search [package_type] Search for components in the registry. search --local [package_type] [--query searching_query] to search in local packages directory. transfer [type] [address] [amount] Transfer wealth associated with a private key of the agent to another account. upgrade [package_type] [public_id] Upgrade the packages of the agent. -v DEBUG run Run with debugging.

Tip

You can also disable a resource without deleting it by removing the entry from the configuration but leaving the package in the skills namespace.

Tip

You can skip the consistency checks on the AEA project by using the flag --skip-consistency-check. E.g. aea --skip-consistency-check run will bypass the fingerprint checks.

"},{"location":"aea-framework-documentation/cli-vs-programmatic-aeas/","title":"CLI vs Programmatic AEAs","text":"

The AEA framework enables us to create agents either from the CLI tool or programmatically.

The following demo demonstrates an interaction between two AEAs.

The provider of weather data (managed with the CLI). The buyer of weather data (managed programmatically).

"},{"location":"aea-framework-documentation/cli-vs-programmatic-aeas/#discussion","title":"Discussion","text":"

The scope of the specific demo is to demonstrate how a CLI based AEA can interact with a programmatically managed AEA. In order to achieve this we are going to use the weather station skills. This demo does not utilize a smart contract or a ledger interaction.

"},{"location":"aea-framework-documentation/cli-vs-programmatic-aeas/#get-required-packages","title":"Get Required Packages","text":"

Copy the packages directory into your local working directory:

svn export https://github.com/fetchai/agents-aea.git/trunk/packages\n

Also, install aea-ledger-fetchai plug-in:

pip install aea-ledger-fetchai\n
"},{"location":"aea-framework-documentation/cli-vs-programmatic-aeas/#demo-instructions","title":"Demo Instructions","text":"

If you want to create the weather station AEA step by step you can follow this guide here

"},{"location":"aea-framework-documentation/cli-vs-programmatic-aeas/#create-the-weather-station-aea","title":"Create the Weather Station AEA","text":"

Fetch the weather station AEA with the following command :

aea fetch fetchai/weather_station:0.32.5\ncd weather_station\naea install\naea build\n
"},{"location":"aea-framework-documentation/cli-vs-programmatic-aeas/#update-the-aea-configurations","title":"Update the AEA Configurations","text":"

In the terminal change the configuration:

aea config set vendor.fetchai.skills.weather_station.models.strategy.args.is_ledger_tx False --type bool\n

The is_ledger_tx will prevent the AEA to communicate with a ledger.

"},{"location":"aea-framework-documentation/cli-vs-programmatic-aeas/#add-keys","title":"Add Keys","text":"

Add a private key for the weather station.

aea generate-key fetchai\naea add-key fetchai fetchai_private_key.txt\n

Next, create a private key used to secure the AEA's communications:

aea generate-key fetchai fetchai_connection_private_key.txt\naea add-key fetchai fetchai_connection_private_key.txt --connection\n

Finally, certify the key for use by the connections that request that:

aea issue-certificates\n
"},{"location":"aea-framework-documentation/cli-vs-programmatic-aeas/#run-the-weather-station-aea","title":"Run the Weather Station AEA","text":"
aea run\n

Once you see a message of the form To join its network use multiaddr: ['SOME_ADDRESS'] take note of the address.

"},{"location":"aea-framework-documentation/cli-vs-programmatic-aeas/#create-the-weather-client-aea","title":"Create the Weather Client AEA","text":"

Since we want to show the interaction between a programmatically created AEA with a CLI based AEA we are going to write some code for the client.

Create a new python file and name it weather_client.py and add the following code

Weather client full code:
import logging\nimport os\nimport sys\nfrom typing import cast\nfrom aea_ledger_fetchai import FetchAICrypto\nfrom aea.aea import AEA\nfrom aea.aea_builder import AEABuilder\nfrom aea.configurations.base import ConnectionConfig\nfrom aea.crypto.helpers import (\nPRIVATE_KEY_PATH_SCHEMA,\ncreate_private_key,\nmake_certificate,\n)\nfrom aea.crypto.wallet import Wallet\nfrom aea.helpers.base import CertRequest\nfrom aea.identity.base import Identity\nfrom aea.protocols.base import Protocol\nfrom aea.registries.resources import Resources\nfrom aea.skills.base import Skill\nimport packages.fetchai.connections.p2p_libp2p.connection\nfrom packages.fetchai.connections.ledger.connection import LedgerConnection\nfrom packages.fetchai.connections.p2p_libp2p.connection import P2PLibp2pConnection\nfrom packages.fetchai.connections.soef.connection import SOEFConnection\nfrom packages.fetchai.protocols.ledger_api.message import LedgerApiMessage\nfrom packages.fetchai.protocols.oef_search.message import OefSearchMessage\nfrom packages.fetchai.skills.weather_client.strategy import Strategy\nAPI_KEY = \"TwiCIriSl0mLahw17pyqoA\"\nSOEF_ADDR = \"s-oef.fetch.ai\"\nSOEF_PORT = 443\nENTRY_PEER_ADDRESS = (\n\"/dns4/127.0.0.1/tcp/9000/p2p/16Uiu2HAmLBCAqHL8SuFosyDhAKYsLKXBZBWXBsB9oFw2qU4Kckun\"\n)\nFETCHAI_PRIVATE_KEY_FILE = PRIVATE_KEY_PATH_SCHEMA.format(FetchAICrypto.identifier)\nFETCHAI_PRIVATE_KEY_FILE_CONNECTION = PRIVATE_KEY_PATH_SCHEMA.format(\n\"fetchai_connection\"\n)\nROOT_DIR = os.getcwd()\nlogger = logging.getLogger(\"aea\")\nlogging.basicConfig(stream=sys.stdout, level=logging.INFO)\ndef run():\n\"\"\"Run demo.\"\"\"\n# Create a private key\ncreate_private_key(FetchAICrypto.identifier, FETCHAI_PRIVATE_KEY_FILE)\ncreate_private_key(FetchAICrypto.identifier, FETCHAI_PRIVATE_KEY_FILE_CONNECTION)\n# Set up the wallet, identity and (empty) resources\nwallet = Wallet(\nprivate_key_paths={FetchAICrypto.identifier: FETCHAI_PRIVATE_KEY_FILE},\nconnection_private_key_paths={\nFetchAICrypto.identifier: FETCHAI_PRIVATE_KEY_FILE_CONNECTION\n},\n)\nidentity = Identity(\n\"my_aea\",\naddress=wallet.addresses.get(FetchAICrypto.identifier),\npublic_key=wallet.public_keys.get(FetchAICrypto.identifier),\n)\nresources = Resources()\ndata_dir = os.getcwd()\n# specify the default routing for some protocols\ndefault_routing = {\nLedgerApiMessage.protocol_id: LedgerConnection.connection_id,\nOefSearchMessage.protocol_id: SOEFConnection.connection_id,\n}\ndefault_connection = P2PLibp2pConnection.connection_id\nstate_update_protocol = Protocol.from_dir(\nos.path.join(os.getcwd(), \"packages\", \"fetchai\", \"protocols\", \"state_update\")\n)\nresources.add_protocol(state_update_protocol)\n# Add the default protocol (which is part of the AEA distribution)\ndefault_protocol = Protocol.from_dir(\nos.path.join(os.getcwd(), \"packages\", \"fetchai\", \"protocols\", \"default\")\n)\nresources.add_protocol(default_protocol)\n# Add the signing protocol (which is part of the AEA distribution)\nsigning_protocol = Protocol.from_dir(\nos.path.join(os.getcwd(), \"packages\", \"fetchai\", \"protocols\", \"signing\")\n)\nresources.add_protocol(signing_protocol)\n# Add the ledger_api protocol\nledger_api_protocol = Protocol.from_dir(\nos.path.join(\nos.getcwd(),\n\"packages\",\n\"fetchai\",\n\"protocols\",\n\"ledger_api\",\n)\n)\nresources.add_protocol(ledger_api_protocol)\n# Add the oef_search protocol\noef_protocol = Protocol.from_dir(\nos.path.join(\nos.getcwd(),\n\"packages\",\n\"fetchai\",\n\"protocols\",\n\"oef_search\",\n)\n)\nresources.add_protocol(oef_protocol)\n# Add the fipa protocol\nfipa_protocol = Protocol.from_dir(\nos.path.join(\nos.getcwd(),\n\"packages\",\n\"fetchai\",\n\"protocols\",\n\"fipa\",\n)\n)\nresources.add_protocol(fipa_protocol)\n# Add the LedgerAPI connection\nconfiguration = ConnectionConfig(connection_id=LedgerConnection.connection_id)\nledger_api_connection = LedgerConnection(\nconfiguration=configuration, data_dir=data_dir, identity=identity\n)\nresources.add_connection(ledger_api_connection)\n# Add the P2P connection\ncert_path = \".certs/conn_cert.txt\"\ncert_request = CertRequest(\nidentifier=\"acn\",\nledger_id=FetchAICrypto.identifier,\nnot_after=\"2022-01-01\",\nnot_before=\"2021-01-01\",\npublic_key=\"fetchai\",\nmessage_format=\"{public_key}\",\nsave_path=cert_path,\n)\npublic_key = wallet.connection_cryptos.public_keys.get(FetchAICrypto.identifier)\nmessage = cert_request.get_message(public_key)\nmake_certificate(\nFetchAICrypto.identifier, FETCHAI_PRIVATE_KEY_FILE, message, cert_path\n)\nconfiguration = ConnectionConfig(\nconnection_id=P2PLibp2pConnection.connection_id,\ndelegate_uri=\"127.0.0.1:11001\",\nentry_peers=[ENTRY_PEER_ADDRESS],\nlocal_uri=\"127.0.0.1:9001\",\nlog_file=\"libp2p_node.log\",\npublic_uri=\"127.0.0.1:9001\",\nbuild_directory=os.getcwd(),\nbuild_entrypoint=\"check_dependencies.py\",\ncert_requests=[cert_request],\n)\nconfiguration.directory = os.path.dirname(\npackages.fetchai.connections.p2p_libp2p.connection.__file__\n)\nAEABuilder.run_build_for_component_configuration(configuration)\np2p_connection = P2PLibp2pConnection(\nconfiguration=configuration,\ndata_dir=data_dir,\nidentity=identity,\ncrypto_store=wallet.connection_cryptos,\n)\nresources.add_connection(p2p_connection)\n# Add the SOEF connection\nconfiguration = ConnectionConfig(\napi_key=API_KEY,\nsoef_addr=SOEF_ADDR,\nsoef_port=SOEF_PORT,\nrestricted_to_protocols={OefSearchMessage.protocol_id},\nconnection_id=SOEFConnection.connection_id,\n)\nsoef_connection = SOEFConnection(\nconfiguration=configuration, data_dir=data_dir, identity=identity\n)\nresources.add_connection(soef_connection)\n# create the AEA\nmy_aea = AEA(\nidentity,\nwallet,\nresources,\ndata_dir,\ndefault_connection=default_connection,\ndefault_routing=default_routing,\n)\n# Add the error and weather_client skills\nerror_skill = Skill.from_dir(\nos.path.join(ROOT_DIR, \"packages\", \"fetchai\", \"skills\", \"error\"),\nagent_context=my_aea.context,\n)\nweather_skill = Skill.from_dir(\nos.path.join(ROOT_DIR, \"packages\", \"fetchai\", \"skills\", \"weather_client\"),\nagent_context=my_aea.context,\n)\nstrategy = cast(Strategy, weather_skill.models.get(\"strategy\"))\nstrategy._is_ledger_tx = False\nfor skill in [error_skill, weather_skill]:\nresources.add_skill(skill)\n# Run the AEA\ntry:\nlogger.info(\"STARTING AEA NOW!\")\nmy_aea.start()\nexcept KeyboardInterrupt:\nlogger.info(\"STOPPING AEA NOW!\")\nmy_aea.stop()\nif __name__ == \"__main__\":\nrun()\n

Now replace ENTRY_PEER_ADDRESS with the peer address (SOME_ADDRESS) noted above.

For more details on how to create an agent programmatically follow this guide here.

"},{"location":"aea-framework-documentation/cli-vs-programmatic-aeas/#run-the-weather-client-aea","title":"Run the Weather Client AEA","text":"

In a new terminal window, navigate to the folder that you created the script and run:

python weather_client.py\n

You should see both AEAs interacting now.

"},{"location":"aea-framework-documentation/config/","title":"Configurations","text":"

This document describes the configuration files of the different packages.

"},{"location":"aea-framework-documentation/config/#aea-configuration-yaml","title":"AEA Configuration YAML","text":"

The following provides a list of the relevant regex used:

PACKAGE_REGEX: \"[a-zA-Z_][a-zA-Z0-9_]*\"\nAUTHOR_REGEX: \"[a-zA-Z_][a-zA-Z0-9_]*\"\nPUBLIC_ID_REGEX: \"^[a-zA-Z0-9_]*/[a-zA-Z_][a-zA-Z0-9_]*:(0|[1-9]\\\\d*)\\\\.(0|[1-9]\\\\d*)\\\\.(0|[1-9]\\\\d*)(?:-((?:0|[1-9]\\\\d*|\\\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\\\.(?:0|[1-9]\\\\d*|\\\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\\\+([0-9a-zA-Z-]+(?:\\\\.[0-9a-zA-Z-]+)*))?$\"\nLEDGER_ID_REGEX: \"^[^\\\\d\\\\W]\\\\w*\\\\Z\"\n

The aea-config.yaml defines the AEA project. The compulsory components are listed below:

agent_name: my_agent                            # Name of the AEA project (must satisfy PACKAGE_REGEX)\nauthor: fetchai                                 # Author handle of the project's author (must satisfy AUTHOR_REGEX)\nversion: 0.1.0                                  # Version of the AEA project (a semantic version number, see https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string\")\ndescription: A demo project                     # Description of the AEA project\nlicense: Apache-2.0                             # License of the AEA project\naea_version: '>=1.0.0, <2.0.0'               # AEA framework version(s) compatible with the AEA project (a version number that matches PEP 440 version schemes, or a comma-separated list of PEP 440 version specifiers, see https://www.python.org/dev/peps/pep-0440/#version-specifiers)\nfingerprint: {}                                 # Fingerprint of AEA project components.\nfingerprint_ignore_patterns: []                 # Ignore pattern for the fingerprinting tool.\nconnections:                                    # The list of connection public ids the AEA project depends on (each public id must satisfy PUBLIC_ID_REGEX)\n- fetchai/stub:0.21.3\ncontracts: []                                   # The list of contract public ids the AEA project depends on (each public id must satisfy PUBLIC_ID_REGEX).\nprotocols:                                      # The list of protocol public ids the AEA project depends on (each public id must satisfy PUBLIC_ID_REGEX).\n- fetchai/default:1.1.7\nskills:                                         # The list of skill public ids the AEA project depends on (each public id must satisfy PUBLIC_ID_REGEX).\n- fetchai/error:0.18.6\ndefault_connection: fetchai/p2p_libp2p:0.27.5   # The default connection used for envelopes sent by the AEA (must satisfy PUBLIC_ID_REGEX).\ndefault_ledger: fetchai                         # The default ledger identifier the AEA project uses (must satisfy LEDGER_ID_REGEX)\nrequired_ledgers: [fetchai]                            # the list of identifiers of ledgers that the AEA project requires key pairs for (each item must satisfy LEDGER_ID_REGEX)\ndefault_routing: {}                             # The default routing scheme applied to envelopes sent by the AEA, it maps from protocol public ids to connection public ids (both keys and values must satisfy PUBLIC_ID_REGEX)\nconnection_private_key_paths:                   # The private key paths the AEA project uses for its connections (keys must satisfy LEDGER_ID_REGEX, values must be file paths)\nfetchai: fetchai_private_key.txt\nprivate_key_paths:                              # The private key paths the AEA project uses (keys must satisfy LEDGER_ID_REGEX, values must be file paths)\nfetchai: fetchai_private_key.txt\nlogging_config:                                 # The logging configurations the AEA project uses\ndisable_existing_loggers: false\nversion: 1\ndependencies: {}                                # The python dependencies the AEA relies on (e.g. plugins). They will be installed when `aea install` is run.\n

The aea-config.yaml can be extended with a number of optional fields:

period: 0.05                                    # The period to call agent's act\nexecution_timeout: 0                            # The execution time limit on each call to `react` and `act` (0 disables the feature)\ntimeout: 0.05                                   # The sleep time on each AEA loop spin (only relevant for the `sync` mode)\nmax_reactions: 20                               # The maximum number of envelopes processed per call to `react` (only relevant for the `sync` mode)\nskill_exception_policy: propagate               # The exception policy applied to skills (must be one of \"propagate\", \"just_log\", or \"stop_and_exit\")\nconnection_exception_policy: propagate          # The exception policy applied to connections (must be one of \"propagate\", \"just_log\", or \"stop_and_exit\")\nloop_mode: async                                # The agent loop mode (must be one of \"sync\" or \"async\")\nruntime_mode: threaded                          # The runtime mode (must be one of \"threaded\" or \"async\") and determines how agent loop and multiplexer are run\nerror_handler: None                             # The error handler to be used.\ndecision_maker_handler: None                    # The decision maker handler to be used.\nstorage_uri: None                               # The URI to the storage.\ndata_dir: None                                  # The path to the directory for local files. Defaults to current working directory.\n

The aea-config.yaml can further be extended with component configuration overrides.

For custom connection configurations:

public_id: some_author/some_package:0.1.0       # The public id of the connection (must satisfy PUBLIC_ID_REGEX).\ntype: connection                                # for connections, this must be \"connection\".\nconfig: ...                                     # a dictionary to overwrite the `config` field (see below)\n

For custom skill configurations:

public_id: some_author/some_package:0.1.0       # The public id of the connection (must satisfy PUBLIC_ID_REGEX).\ntype: skill                                     # for skills, this must be \"skill\".\nbehaviours:                                     # override configurations for behaviours\nbehaviour_1:                                  # override configurations for \"behaviour_1\"\nargs:                                       # arguments for a specific behaviour (see below)\nfoo: bar\nhandlers:                                       # override configurations for handlers\nhandler_1:                                    # override configurations for \"handler_1\"\nargs:                                       # arguments for a specific handler (see below)\nfoo: bar\nmodels:                                         # override configurations for models\nmodel_1:                                      # override configurations for \"model_1\"\nargs:                                       # arguments for a specific model (see below)\nfoo: bar\n
"},{"location":"aea-framework-documentation/config/#connection-configuration-yaml","title":"Connection Configuration YAML","text":"

The connection.yaml, which is present in each connection package, has the following required fields:

name: scaffold                                  # Name of the package (must satisfy PACKAGE_REGEX)\nauthor: fetchai                                 # Author handle of the package's author (must satisfy AUTHOR_REGEX)\nversion: 0.1.0                                  # Version of the package (a semantic version number, see https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string\")\ntype: connection                                # The type of the package; for connections, it must be \"connection\"\ndescription: A scaffold connection              # Description of the package\nlicense: Apache-2.0                             # License of the package\naea_version: '>=1.0.0, <2.0.0'               # AEA framework version(s) compatible with the AEA project (a version number that matches PEP 440 version schemes, or a comma-separated list of PEP 440 version specifiers, see https://www.python.org/dev/peps/pep-0440/#version-specifiers)\nfingerprint:                                    # Fingerprint of package components.\n__init__.py: QmZvYZ5ECcWwqiNGh8qNTg735wu51HqaLxTSifUxkQ4KGj\nconnection.py: QmagwVgaPgfeXqVTgcpFESA4DYsteSbojz94SLtmnHNAze\nfingerprint_ignore_patterns: []                 # Ignore pattern for the fingerprinting tool.\nconnections: []                                 # The list of connection public ids the package depends on (each public id must satisfy PUBLIC_ID_REGEX).\nprotocols: []                                   # The list of protocol public ids the package depends on (each public id must satisfy PUBLIC_ID_REGEX).\nclass_name: MyScaffoldConnection                # The class name of the class implementing the connection interface.\nconfig:                                         # A dictionary containing the kwargs for the connection instantiation.\nfoo: bar\nexcluded_protocols: []                          # The list of protocol public ids the package does not permit (each public id must satisfy PUBLIC_ID_REGEX).\nrestricted_to_protocols: []                     # The list of protocol public ids the package is limited to (each public id must satisfy PUBLIC_ID_REGEX).\ndependencies: {}                                # The python dependencies the package relies on. They will be installed when `aea install` is run.\nis_abstract: false                              # An optional boolean that if `true` makes the connection\n
"},{"location":"aea-framework-documentation/config/#contract-configuration-yaml","title":"Contract Configuration YAML","text":"

The contract.yaml, which is present in each contract package, has the following required fields:

name: scaffold                                  # Name of the package (must satisfy PACKAGE_REGEX)\nauthor: fetchai                                 # Author handle of the package's author (must satisfy AUTHOR_REGEX)\nversion: 0.1.0                                  # Version of the package (a semantic version number, see https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string\")\ntype: contract                                  # The type of the package; for contracts, it must be \"contract\"\ndescription: A scaffold contract                # Description of the package\nlicense: Apache-2.0                             # License of the package\naea_version: '>=1.0.0, <2.0.0'               # AEA framework version(s) compatible with the AEA project (a version number that matches PEP 440 version schemes, or a comma-separated list of PEP 440 version specifiers, see https://www.python.org/dev/peps/pep-0440/#version-specifiers)\nfingerprint:                                    # Fingerprint of package components.\n__init__.py: QmPBwWhEg3wcH1q9612srZYAYdANVdWLDFWKs7TviZmVj6\ncontract.py: QmXvjkD7ZVEJDJspEz5YApe5bRUxvZHNi8vfyeVHPyQD5G\nfingerprint_ignore_patterns: []                 # Ignore pattern for the fingerprinting tool.\nclass_name: MyScaffoldContract                  # The class name of the class implementing the contract interface.\ncontract_interface_paths: {}                    # The paths to the contract interfaces (one for each ledger identifier).\nconfig:                                         # A dictionary containing the kwargs for the contract instantiation.\nfoo: bar\ndependencies: {}                                # The python dependencies the package relies on. They will be installed when `aea install` is run.\n
"},{"location":"aea-framework-documentation/config/#protocol-configuration-yaml","title":"Protocol Configuration YAML","text":"

The protocol.yaml, which is present in each protocol package, has the following required fields:

name: scaffold                                  # Name of the package (must satisfy PACKAGE_REGEX)\nauthor: fetchai                                 # Author handle of the package's author (must satisfy AUTHOR_REGEX)\nversion: 0.1.0                                  # Version of the package (a semantic version number, see https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string\")\ntype: protocol                                  # The type of the package; for protocols, it must be \"protocol\" \ndescription: A scaffold protocol                # Description of the package\nlicense: Apache-2.0                             # License of the package\naea_version: '>=1.0.0, <2.0.0'               # AEA framework version(s) compatible with the AEA project (a version number that matches PEP 440 version schemes, or a comma-separated list of PEP 440 version specifiers, see https://www.python.org/dev/peps/pep-0440/#version-specifiers)\nfingerprint:                                    # Fingerprint of package components.\n__init__.py: Qmay9PmfeHqqVa3rdgiJYJnzZzTStboQEfpwXDpcgJMHTJ\nmessage.py: QmdvAdYSHNdZyUMrK3ue7quHAuSNwgZZSHqxYXyvh8Nie4\nserialization.py: QmVUzwaSMErJgNFYQZkzsDhuuT2Ht4EdbGJ443usHmPxVv\nfingerprint_ignore_patterns: []                 # Ignore pattern for the fingerprinting tool.\ndependencies: {}                                # The python dependencies the package relies on. They will be installed when `aea install` is run.\n
"},{"location":"aea-framework-documentation/config/#skill-configuration-yaml","title":"Skill Configuration YAML","text":"

The skill.yaml, which is present in each protocol package, has the following required fields:

name: scaffold                                  # Name of the package (must satisfy PACKAGE_REGEX)\nauthor: fetchai                                 # Author handle of the package's author (must satisfy AUTHOR_REGEX)\nversion: 0.1.0                                  # Version of the package (a semantic version number, see https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string\")\ntype: skill                                     # The type of the package; for skills, it must be \"skill\"\ndescription: A scaffold skill                   # Description of the package\nlicense: Apache-2.0                             # License of the package\naea_version: '>=1.0.0, <2.0.0'               # AEA framework version(s) compatible with the AEA project (a version number that matches PEP 440 version schemes, or a comma-separated list of PEP 440 version specifiers, see https://www.python.org/dev/peps/pep-0440/#version-specifiers)\nfingerprint:                                    # Fingerprint of package components.\n__init__.py: QmNkZAetyctaZCUf6ACxP5onGWsSxu2hjSNoFmJ3ta6Lta\nbehaviours.py: QmYa1rczhGTtMJBgCd1QR9uZhhkf45orm7TnGTE5Eizjpy\nhandlers.py: QmZYyTENRr6ecnxx1FeBdgjLiBhFLVn9mqarzUtFQmNUFn\nmy_model.py: QmPaZ6G37Juk63mJj88nParaEp71XyURts8AmmX1axs24V\nfingerprint_ignore_patterns: []                 # Ignore pattern for the fingerprinting tool.\ncontracts: []                                   # The list of contract public ids the package depends on (each public id must satisfy PUBLIC_ID_REGEX).\nprotocols: []                                   # The list of protocol public ids the package depends on (each public id must satisfy PUBLIC_ID_REGEX).\nskills: []                                      # The list of skill public ids the package depends on (each public id must satisfy PUBLIC_ID_REGEX).\nis_abstract: false                              # An optional boolean that if `true` makes the skill abstract, i.e. not instantiated by the framework but importable from other skills. Defaults to `false`. \nbehaviours:                                     # The dictionary describing the behaviours immplemented in the package (including their configuration)\nscaffold:                                     # Name of the behaviour under which it is made available on the skill context.\nargs:                                       # Keyword arguments provided to the skill component on instantiation.\nfoo: bar\nclass_name: MyScaffoldBehaviour             # The class name of the class implementing the behaviour interface.\nhandlers:                                       # The dictionary describing the handlers immplemented in the package (including their configuration)\nscaffold:                                     # Name of the handler under which it is made available on the skill\nargs:                                       # Keyword arguments provided to the skill component on instantiation.\nfoo: bar\nclass_name: MyScaffoldHandler               # The class name of the class implementing the handler interface.\nmodels:                                         # The dictionary describing the models immplemented in the package (including their configuration)\nscaffold:                                     # Name of the model under which it is made available on the skill\nargs:                                       # Keyword arguments provided to the skill component on instantiation.\nfoo: bar\nclass_name: MyModel                         # The class name of the class implementing the model interface.\ndependencies: {}                                # The python dependencies the package relies on. They will be installed when `aea install` is run.\n
"},{"location":"aea-framework-documentation/connect-a-frontend/","title":"Front-End Integration","text":"

This page lays out two options for connecting a front-end to an AEA. The following diagram illustrates these two options.

"},{"location":"aea-framework-documentation/connect-a-frontend/#case-1","title":"Case 1","text":"

The first option is to create a HTTP Server connection that handles incoming requests from a REST API. In this scenario, the REST API communicates with the AEA and requests are handled by the HTTP Server connection package. The REST API should send CRUD requests to the HTTP Server connection (fetchai/http_server:0.23.6) which translates these into Envelopes to be consumed by the correct skill.

"},{"location":"aea-framework-documentation/connect-a-frontend/#case-2","title":"Case 2","text":"

The second option is to create a front-end comprising a stand-alone Multiplexer with a P2P connection (fetchai/p2p_libp2p:0.27.5). In this scenario the Agent Communication Network can be used to send Envelopes from the AEA to the front-end.

"},{"location":"aea-framework-documentation/connection/","title":"Connections","text":"

A Connection provides an interface for the agent to connect with entities in the outside world. Connections wrap SDKs or APIs and provide interfaces to networks, ledgers and other services. As such, a connection is concerned with I/O bound and continuously connected operations. Where necessary, a connection is responsible for translating between the framework specific protocol (an Envelope with its contained Message) and the external service or third-party protocol (e.g. HTTP). Hence, there are two roles for connections: wrapper and transport connection. The transport connection is responsible to delivering AEA envelopes.

The messages constructed or received by a connection are eventually processed by one or several skills which deal with handling and generating messages related to a specific business objective.

An AEA can interact with multiple connections at the same time via the Multiplexer. Connections are passive in terms of multiplexer interactions (its methods are called by the Multiplexer), but they can run their own asynchronous or threaded tasks.

The Multiplexer maintains an InBox and OutBox, which are, respectively, queues for incoming and outgoing envelopes and their contained messages.

"},{"location":"aea-framework-documentation/connection/#developing-your-connection","title":"Developing your Connection","text":"

The easiest way to get started developing your own connection is by using the scaffold command:

aea scaffold connection my_new_connection\n

This will scaffold a connection package called my_new_connection with three files:

  • __init__.py
  • connection.py containing the scaffolded connection class
  • connection.yaml containing the scaffolded configuration file

As a developer you have the choice between implementing a sync or asynchronous interface. The scaffolded connection.py file contains two classes: the MyScaffoldAsyncConnection inherited from the Connection base class and the MyScaffoldSyncConnection inherited from the BaseSyncConnection. Remove the unused class.

"},{"location":"aea-framework-documentation/connection/#primary-methods-to-develop-asynchronous-connection-interface","title":"Primary Methods to Develop - Asynchronous Connection Interface","text":"

The developer needs to implement four public coroutines:

  • The connect coroutine implements the setup logic required to be performed for the connection when it is initially launched. The connect coroutine is called by the AEA framework once when the agent is being started.

  • The disconnect coroutine implements the teardown logic required to be performed for the connection when it is eventually stopped. The disconnect coroutine is called by the AEA framework once when the agent is being stopped.

  • The send coroutine is called by the AEA framework each time the Multiplexer handles an outgoing envelope specified to be handled by this connection. The send coroutine must implement the processing of the envelope leaving the agent.

  • The receive coroutine is continuously called by the AEA framework. It either returns None or an envelope. The receive coroutine must implement the logic of data being received by the agent, and if necessary, its translation into a relevant protocol.

The framework provides a demo stub connection which implements an I/O reader and writer to send and receive messages between the agent and a local file. To gain inspiration and become familiar with the structure of connection packages, you may find it useful to check out fetchai/stub:0.21.3, fetchai/http_server:0.23.6 or fetchai/http_client:0.24.6 connections. The latter two connections are for external clients to connect with an agent, and for the agent to connect with external servers, respectively.

"},{"location":"aea-framework-documentation/connection/#primary-methods-to-develop-sync-connection-interface","title":"Primary Methods to Develop - Sync Connection Interface","text":"

The BaseSyncConnection uses executors to execute synchronous code from the asynchronous context of the Multiplexer in executors/threads, which are limited by the amount of configured workers.

The asynchronous methods connect, disconnect and send are converted to callbacks which the developer implements:

  • on_connect
  • on_disconnect
  • on_send

All of these methods will be executed in the executor pool.

Every method can create a message by putting it into the thread/asynchronous friendly queue that is consumed by the Multiplexer.

The receive coroutine has no direct equivalent. Instead, the developer implements a main method which runs synchronously in the background.

"},{"location":"aea-framework-documentation/connection/#configuration","title":"Configuration","text":"

Every connection must have a configuration file in connection.yaml, containing meta-information about the connection as well as all the required configuration details. For more details, have a look here.

"},{"location":"aea-framework-documentation/connection/#configuration-options","title":"Configuration Options","text":"

The connection.yaml file contains a number of fields that must be edited by the developer of the connection:

connections: []\nprotocols: []\nclass_name: MyScaffoldConnection\nconfig:\nfoo: bar\nexcluded_protocols: []\nrestricted_to_protocols: []\ndependencies: {}\nis_abstract: false\ncert_requests: []\n
  • connections specifies the list of other connection this connection depends on
  • protocols specifies the list of protocols this connection depends on
  • class_name needs to match the name of the connection class in connection.py
  • config can contain arbitrary configuration information which is made available in the constructor of the connection as keyword arguments (**kwargs)
  • excluded_protocols lists the protocols which cannot be used in this connection
  • restricted_to_protocols lists the protocols which this connection is restricted to be used by
  • dependencies lists any Python dependencies of the connection package
  • is_abstract specifies whether this connection is only used as an abstract base class
  • cert_requests lists certification requests of the connection (see proof of representation for details)
"},{"location":"aea-framework-documentation/contract/","title":"Contracts","text":"

Contracts wrap smart contracts for Fetch.ai and third-party decentralized ledgers. In particular, they provide wrappers around the API or ABI of a smart contract and its byte code. They implement a translation between framework messages (in the fetchai/contract_api:1.0.0 protocol) and the implementation specifics of the ABI.

Contracts usually implement four types of methods:

  • a method to create a smart contract deployment transaction,
  • methods to create transactions to modify state in the deployed smart contract,
  • methods to create contract calls to execute static methods on the deployed smart contract, and
  • methods to query the state of the deployed smart contract.

Contracts can be added as packages which means they become reusable across AEA projects.

The smart contract wrapped in an AEA contract package might be a third-party smart contract or your own smart contract potentially interacting with a third-party contract on-chain.

"},{"location":"aea-framework-documentation/contract/#interacting-with-contracts-from-skills","title":"Interacting with Contracts from Skills","text":"

Interacting with contracts in almost all cases requires network access. Therefore, the framework executes contract related logic in a Connection.

In particular, the fetchai/ledger:0.21.5 connection can be used to execute contract related logic. The skills communicate with the fetchai/ledger:0.21.5 connection via the fetchai/contract_api:1.0.0 protocol. This protocol implements a request-response pattern to serve the four types of methods listed above:

  • the get_deploy_transaction message is used to request a deploy transaction for a specific contract. For instance, to request a deploy transaction for the deployment of the smart contract wrapped in the fetchai/erc1155:0.23.3 package, we send the following message to the fetchai/ledger:0.21.5:
contract_api_msg = ContractApiMessage(\nperformative=ContractApiMessage.Performative.GET_DEPLOY_TRANSACTION,\ndialogue_reference=contract_api_dialogues.new_self_initiated_dialogue_reference(),\nledger_id=strategy.ledger_id,\ncontract_id=\"fetchai/erc1155:0.23.3\",\ncallable=\"get_deploy_transaction\",\nkwargs=ContractApiMessage.Kwargs(\n{\"deployer_address\": self.context.agent_address}\n),\n)\n

Any additional arguments needed by the contract's constructor method should be added to kwargs.

This message will be handled by the fetchai/ledger:0.21.5 connection and then a raw_transaction message will be returned with the matching raw transaction. To send this transaction to the ledger for processing, we first sign the message with the decision maker and then send the signed transaction to the fetchai/ledger:0.21.5 connection using the fetchai/ledger_api:1.0.0 protocol. For details on how to implement the message handling, see the handlers in the erc1155_deploy skill.

CosmWasm based smart contract deployments

When using CosmWasm based smart contracts two types of deployment transactions exist. The first transaction stores the code on the chain. The second transaction initialises the code. This way, the same contract code can be initialised many times.

Both the store and init messages use the ContractApiMessage.Performative.GET_DEPLOY_TRANSACTION performative. The ledger API automatically detects the type of transactions based on the provided keyword arguments. In particular, an init transaction requires the keyword arguments code_id (integer), label (string), amount (integer) and init_msg (JSON).

For an example look at the fetchai/erc1155:0.23.3 package.

  • the get_raw_transaction message is used to request any transaction for a specific contract which changes state in the contract. For instance, to request a transaction for the creation of token in the deployed erc1155 smart contract wrapped in the fetchai/erc1155:0.23.3 package, we send the following message to the fetchai/ledger:0.21.5:
contract_api_msg = ContractApiMessage(\nperformative=ContractApiMessage.Performative.GET_RAW_TRANSACTION,\ndialogue_reference=contract_api_dialogues.new_self_initiated_dialogue_reference(),\nledger_id=strategy.ledger_id,\ncontract_id=\"fetchai/erc1155:0.23.3\",\ncontract_address=strategy.contract_address,\ncallable=\"get_create_batch_transaction\",\nkwargs=ContractApiMessage.Kwargs(\n{\n\"deployer_address\": self.context.agent_address,\n\"token_ids\": strategy.token_ids,\n}\n),\n)\n

This message will be handled by the fetchai/ledger:0.21.5 connection and then a raw_transaction message will be returned with the matching raw transaction. For this to be executed correctly, the fetchai/erc1155:0.23.3 contract package needs to implement the get_create_batch_transaction method with the specified key word arguments (see example in Deploy your own, below). Similar to the above, to send this transaction to the ledger for processing, we first sign the message with the decision maker and then send the signed transaction to the fetchai/ledger:0.21.5 connection using the fetchai/ledger_api:1.0.0 protocol.

  • the get_raw_message message is used to request any contract method call for a specific contract which does not change state in the contract. For instance, to request a call to get a hash from some input data in the deployed erc1155 smart contract wrapped in the fetchai/erc1155:0.23.3 package, we send the following message to the fetchai/ledger:0.21.5:
contract_api_msg = ContractApiMessage(\nperformative=ContractApiMessage.Performative.GET_RAW_MESSAGE,\ndialogue_reference=contract_api_dialogues.new_self_initiated_dialogue_reference(),\nledger_id=strategy.ledger_id,\ncontract_id=\"fetchai/erc1155:0.23.3\",\ncontract_address=strategy.contract_address,\ncallable=\"get_hash_single\",\nkwargs=ContractApiMessage.Kwargs(\n{\n\"from_address\": from_address,\n\"to_address\": to_address,\n\"token_id\": token_id,\n\"from_supply\": from_supply,\n\"to_supply\": to_supply,\n\"value\": value,\n\"trade_nonce\": trade_nonce,\n}\n),\n)\n

This message will be handled by the fetchai/ledger:0.21.5 connection and then a raw_message message will be returned with the matching raw message. For this to be executed correctly, the fetchai/erc1155:0.23.3 contract package needs to implement the get_hash_single method with the specified key word arguments. We can then send the raw message to the fetchai/ledger:0.21.5 connection using the fetchai/ledger_api:1.0.0 protocol. In this case, signing is not required.

  • the get_state message is used to request any contract method call to query state in the deployed contract. For instance, to request a call to get the balances in the deployed erc1155 smart contract wrapped in the fetchai/erc1155:0.23.3 package, we send the following message to the fetchai/ledger:0.21.5:
contract_api_msg = ContractApiMessage(\nperformative=ContractApiMessage.Performative.GET_STATE,\ndialogue_reference=contract_api_dialogues.new_self_initiated_dialogue_reference(),\nledger_id=strategy.ledger_id,\ncontract_id=\"fetchai/erc1155:0.23.3\",\ncontract_address=strategy.contract_address,\ncallable=\"get_balance\",\nkwargs=ContractApiMessage.Kwargs(\n{\"agent_address\": address, \"token_id\": token_id}\n),\n)\n

This message will be handled by the fetchai/ledger:0.21.5 connection and then a state message will be returned with the matching state. For this to be executed correctly, the fetchai/erc1155:0.23.3 contract package needs to implement the get_balance method with the specified key word arguments. We can then send the raw message to the fetchai/ledger:0.21.5 connection using the fetchai/ledger_api:1.0.0 protocol. In this case, signing is not required.

"},{"location":"aea-framework-documentation/contract/#developing-your-own","title":"Developing your own","text":"

The easiest way to get started developing your own contract is by using the scaffold command:

aea scaffold contract my_new_contract\n

This will scaffold a contract package called my_new_contract with three files:

  • __init__.py
  • contract.py, containing the scaffolded contract class
  • contract.yaml containing the scaffolded configuration file

Once your scaffold is in place, you can create a build folder in the package and copy the smart contract interface (e.g. bytes code and ABI) to it. Then, specify the path to the interfaces in the contract.yaml. For instance, if you use Ethereum, then you might specify the following:

contract_interface_paths:\nethereum: build/my_contract.json\n

where ethereum is the ledger id and my_contract.json is the file containing the byte code and ABI.

Finally, you will want to implement the part of the contract interface you need in contract.py:

from aea.contracts.base import Contract\nfrom aea.crypto.base import LedgerApi\nclass MyContract(Contract):\n\"\"\"The MyContract contract class which acts as a bridge between AEA framework and ERC1155 ABI.\"\"\"\n@classmethod\ndef get_create_batch_transaction(\ncls,\nledger_api: LedgerApi,\ncontract_address: str,\ndeployer_address: str,\ntoken_ids: List[int],\ndata: Optional[bytes] = b\"\",\ngas: int = 300000,\n) -> Dict[str, Any]:\n\"\"\"\n        Get the transaction to create a batch of tokens.\n        :param ledger_api: the ledger API\n        :param contract_address: the address of the contract\n        :param deployer_address: the address of the deployer\n        :param token_ids: the list of token ids for creation\n        :param data: the data to include in the transaction\n        :param gas: the gas to be used\n        :return: the transaction object\n        \"\"\"\n# create the transaction dict\nnonce = ledger_api.api.eth.getTransactionCount(deployer_address)\ninstance = cls.get_instance(ledger_api, contract_address)\ntx = instance.functions.createBatch(\ndeployer_address, token_ids\n).buildTransaction(\n{\n\"gas\": gas,\n\"gasPrice\": ledger_api.api.toWei(\"50\", \"gwei\"),\n\"nonce\": nonce,\n}\n)\ntx = cls._try_estimate_gas(ledger_api, tx)\nreturn tx\n

Above, we implement a method to create a transaction, in this case a transaction to create a batch of tokens. The method will be called by the framework, specifically the fetchai/ledger:0.21.5 connection once it receives a message (see bullet point 2 above). The method first gets the latest transaction nonce of the deployer_address, then constructs the contract instance, then uses the instance to build the transaction and finally updates the gas on the transaction.

It helps to look at existing contract packages, like fetchai/erc1155:0.23.3, and skills using them, like fetchai/erc1155_client:0.11.0 and fetchai/erc1155_deploy:0.31.6, for inspiration and guidance.

"},{"location":"aea-framework-documentation/core-components-1/","title":"Core Components - Part 1","text":"

The AEA framework consists of several core components, some required to run an AEA and others optional.

The following sections discuss the inner workings of the AEA framework and how it calls the code in custom packages (see inversion of control and a helpful comparison here). Whilst it is in principle possible to use parts of the framework as a library, we do not recommend it.

"},{"location":"aea-framework-documentation/core-components-1/#the-elements-each-aea-uses","title":"The Elements Each AEA Uses","text":""},{"location":"aea-framework-documentation/core-components-1/#envelope","title":"Envelope","text":"

AEA objects communicate asynchronously via Envelopes.

An Envelope is the core object with which agents communicate. It is a vehicle for Messages with five attributes:

  • to: defines the destination address.
  • sender: defines the sender address.
  • protocol_id: defines the id of the Protocol.
  • message: is a bytes field which holds the Message in serialized form.
  • Optional[context]: an optional field to specify routing information in a URI.

Messages must adhere to a Protocol.

"},{"location":"aea-framework-documentation/core-components-1/#protocol","title":"Protocol","text":"

Protocols define agent-to-agent as well as component-to-component interactions within AEAs. As such, they include:

  • Messages defining the syntax of messages;
  • Serialization defining how a Message is encoded for transport; and, optionally
  • Dialogues, which define rules over Message sequences.

The framework provides one default Protocol, called default (current version fetchai/default:1.1.7). This Protocol provides a bare-bones implementation for an AEA Protocol which includes a DefaultMessage class and associated DefaultSerializer and DefaultDialogue classes.

Additional Protocols, for new types of interactions, can be added as packages. For more details on Protocols you can read the protocol guide. To learn how you can easily automate protocol definition, head to the guide for the protocol generator.

Protocol specific Messages, wrapped in Envelopes, are sent and received to other agents, agent components and services via Connections.

"},{"location":"aea-framework-documentation/core-components-1/#connection","title":"Connection","text":"

A Connection wraps an SDK or API and provides an interface to networks, ledgers or other services. Where necessary, a Connection is responsible for translating between the framework specific Envelope with its contained Message and the external service or third-party protocol (e.g. HTTP).

The framework provides one default Connection, called stub (current version fetchai/stub:0.21.3). It implements an I/O reader and writer to send Messages to the agent from a local file.

Additional Connections can be added as packages. For more details on Connections read the Connection guide .

An AEA runs and manages Connections via a Multiplexer.

"},{"location":"aea-framework-documentation/core-components-1/#multiplexer","title":"Multiplexer","text":"

The Multiplexer is responsible for maintaining (potentially multiple) Connections.

It maintains an InBox and OutBox, which are, respectively, queues for incoming and outgoing Envelopes from the perspective of Skills.

"},{"location":"aea-framework-documentation/core-components-1/#skill","title":"Skill","text":"

Skills are the core focus of the framework's extensibility as they implement business logic to deliver economic value for the AEA. They are self-contained capabilities that AEAs can dynamically take on board, in order to expand their effectiveness in different situations.

A Skill encapsulates implementations of the three abstract base classes Handler, Behaviour, Model, and is closely related with the abstract base class Task:

  • Handler: each Skill has zero, one or more Handler objects. There is a one-to-one correspondence between Handlers and the protocols in an AEA (also known as the registered protocols). Handlers implement AEAs' reactive behaviour. If an AEA understands a Protocol referenced in a received Envelope (i.e. the protocol is registered in this AEA), this envelope is sent to the corresponding Handler which executes the AEA's reaction to this Message.
  • Behaviour: a skill can have zero, one or more Behaviours, each encapsulating actions which further the AEAs goal and are initiated by internals of the AEA rather than external events. Behaviours implement AEAs' pro-activeness. The framework provides a number of abstract base classes implementing different types of simple and composite behaviours (e.g. cyclic, one-shot, finite-state-machine, etc), and these define how often and in what order a behaviour and its sub-behaviours must be executed.
  • Model: zero, one or more Models that inherit from the Model abstract base class and are accessible via the SkillContext.
  • Task: zero, one or more Tasks encapsulate background work internal to the AEA. Task differs from the other three in that it is not a part of Skills, but Tasks are declared in or from Skills if a packaging approach for AEA creation is used.

A Skill can read (parts of) an AEA's state (as summarised in the AgentContext), and propose actions to the AEA according to its specific logic. As such, more than one Skill could exist per Protocol, competing with each other in suggesting to the AEA the best course of actions to take. In technical terms, this means Skills are horizontally arranged.

For instance, an AEA which is trading goods, could subscribe to more than one Skill, where each corresponds to a different trading strategy.

The framework places no limits on the complexity of Skills. They can implement simple (e.g. if-this-then-that) logic or be complex (e.g. a deep learning model or reinforcement learning agent).

The framework provides one default Skill, called error. Additional Skills can be added as packages. For more details on Skills head over to the Skill guide .

"},{"location":"aea-framework-documentation/core-components-1/#agent-loop","title":"Agent Loop","text":"

The AgentLoop performs a series of activities while the AEA state is not stopped.

  • it calls the act() function of all active registered Behaviours at their respective tick rate.
  • it grabs all Envelopes waiting in the InBox queue and calls the handle() function for the Handlers currently registered against the Protocol of the Envelope.
  • it dispatches the internal Messages from the decision maker (described below) to the handler in the relevant Skill.

The AgentLoop and Multiplexer are decoupled via the InBox and OutBox, and both are maintained by the Runtime.

"},{"location":"aea-framework-documentation/core-components-1/#next-steps","title":"Next Steps","text":""},{"location":"aea-framework-documentation/core-components-1/#recommended","title":"Recommended","text":"

We recommend you continue with the next step in the 'Getting Started' series:

  • AEA and web frameworks
"},{"location":"aea-framework-documentation/core-components-1/#relevant-deep-dives","title":"Relevant Deep-Dives","text":"

Most AEA development focuses on developing the Skills and Protocols necessary for an AEA to deliver against its economic objectives.

Understanding Protocols is core to developing your own agent. You can learn more about the Protocols agents use to communicate with each other and how they are created in the following section:

  • Protocols

Most of an AEA developer's time is spent on Skill development. Skills are the core business logic components of an AEA. Check out the following guide to learn more:

  • Skills

In most cases, one of the available Connection packages can be used. Occasionally, you might develop your own Connection:

  • Connections
"},{"location":"aea-framework-documentation/core-components-2/","title":"Core components - Part 2","text":"

The AEA framework consists of several core components, some required to run an AEA and others optional.

In Core Components - Part 1 we described the common components each AEA uses. In this page, we will look at more advanced components.

"},{"location":"aea-framework-documentation/core-components-2/#required-components-used-by-aeas","title":"Required Components Used by AEAs","text":""},{"location":"aea-framework-documentation/core-components-2/#decision-maker","title":"Decision Maker","text":"

The DecisionMaker can be thought of as a Wallet manager plus \"economic brain\" of the AEA. It is responsible for the AEA's crypto-economic security and goal management, and it contains the preference and ownership representation of the AEA. The decision maker is the only component with access to the Wallet's private keys.

You can learn more about the decision maker here. In its simplest form, the decision maker acts like a Wallet with Handler that reacts to the messages it receives from the skills.

"},{"location":"aea-framework-documentation/core-components-2/#wallet","title":"Wallet","text":"

The Wallet contains the private-public key pairs used by the AEA. Skills do not have access to the wallet, only the decision maker does.

The agent has two sets of private keys, as configured in the aea-config.yaml:

  • private_key_paths: This is a dictionary mapping identifiers to the file paths of private keys used in the AEA. For each identifier, e.g. fetchai, the AEA can have one private key. The private keys listed here are available in the Decision Maker and the associated public keys and addresses are available in all skills. The AEA uses these keys to sign transactions and messages. These keys usually hold the AEAs funds.
  • connection_private_key_paths: This is a dictionary mapping identifiers to the file paths of private keys used in connections. For each identifier, e.g. fetchai, the Multiplexer can have one private key. The private keys listed here are available in the connections. The connections use these keys to secure message transport, for instance.

It is the responsibility of the AEA's user to safeguard the keys used and ensure that keys are only used in a single AEA. Using the same key across different AEAs will lead to various failure modes.

Private keys can be encrypted at rest. The CLI commands used for interacting with the wallet allow specifying a password for encryption/decryption.

"},{"location":"aea-framework-documentation/core-components-2/#identity","title":"Identity","text":"

The Identity is an abstraction that represents the identity of an AEA in the Open Economic Framework, backed by public-key cryptography. It contains the AEA's addresses as well as its name.

The identity can be accessed in a Skill via the AgentContext.

"},{"location":"aea-framework-documentation/core-components-2/#optional-components-used-by-aeas","title":"Optional Components Used by AEAs","text":""},{"location":"aea-framework-documentation/core-components-2/#contracts","title":"Contracts","text":"

Contracts wrap smart contracts for third-party decentralized ledgers. In particular, they provide wrappers around the API or ABI of a smart contract. They expose an API to abstract implementation specifics of the ABI from the Skills.

Contracts usually contain the logic to create contract transactions and make contract calls.

Contracts can be added as packages. For more details on Contracts also read the Contract guide here.

"},{"location":"aea-framework-documentation/core-components-2/#putting-it-together","title":"Putting it Together","text":"

Taken together, the core components from this section and the first part provide the following simplified illustration of an AEA:

"},{"location":"aea-framework-documentation/core-components-2/#next-steps","title":"Next Steps","text":""},{"location":"aea-framework-documentation/core-components-2/#recommended","title":"Recommended","text":"

We recommend you continue with the next step in the 'Getting Started' series:

  • How AEAs talk to each other - Interaction protocols
"},{"location":"aea-framework-documentation/core-components-2/#relevant-deep-dives","title":"Relevant Deep-Dives","text":"

Understanding the decision maker is vital to developing a goal oriented and crypto-economically safe AEA. You can learn more about the DecisionMaker in the following section:

  • Decision Maker

Understanding Contracts is important when developing AEAs that make commitments or use smart contracts for other purposes. You can learn more about the Contracts agents use in the following section:

  • Contracts
"},{"location":"aea-framework-documentation/core-components/","title":"Core Components","text":"

AEAs can be made from various components, much like legos, and these components can be of differing types. Below are some of the more important types of components an agent can have.

"},{"location":"aea-framework-documentation/core-components/#skill","title":"Skill","text":"

A Skill is an isolated, self-contained, (and preferably atomic) functionality that AEAs can take on board to expand their capability. Skills contain the proactive and reactive behaviour that ultimately makes it possible for an AEA to deliver economic value to its owner.

A Skill encapsulates implementations of three base classes Handler, Behaviour, Model, and is closely related with Task:

  • Handler: Handlers implement AEAs' reactive behaviour. If an AEA understands a protocol referenced in a received Envelope, this envelope is sent to the corresponding handler which executes the AEA's reaction to this message.
  • Behaviour: Behaviours implement AEAs' proactiveness, encapsulating actions which further an AEA's goals, and are initiated by internals of the AEA rather than external events.
  • Model: Encapsulate arbitrary objects and is made available to all components of the skill.
  • Task: Tasks encapsulate background work internal to the AEA.

A skill can read (parts of) an AEA's state and propose actions to the AEA according to its specific logic. As such, more than one skill could exist per protocol, competing with each other in suggesting to the AEA the best course of actions to take.

For instance, an AEA which is trading goods, could subscribe to more than one skill, where each corresponds to a different trading strategy.

The framework places no limits on the complexity of Skills. They can implement simple (e.g. if-this-then-that) logic or be complex (e.g. a deep learning model or reinforcement learning agent).

The framework provides one default error skill. Additional Skills can be added as packages. For more details on skills, head over to the Skill guide .

"},{"location":"aea-framework-documentation/core-components/#protocol","title":"Protocol","text":"

A Protocol defines the structure and nature of an interaction that can happen between agents, or between components of an agent. You can think of a protocol as the language that two agents speak and a skill for this protocol as a particular way of speaking this language. From a game-theoretic viewpoint, a protocol defines the rules of a game and a skill for this protocol defines a particular strategy for playing this game.

Protocols define agent-to-agent as well as component-to-component interactions within AEAs. As such, they include:

  • Messages: defining the syntax of messages.
  • Serialization: defining how a message is encoded for transport.
  • Dialogues: defines rules over sequences of messages.

The framework provides one default protocol. This protocol provides a bare-bones implementation which includes a DefaultMessage class and associated DefaultSerializer and DefaultDialogue classes.

Additional protocols for new types of interactions, can be added as packages. For more details on protocols, you can read the protocol guide. To learn how you can easily automate protocol definition, head to the guide for the protocol generator.

Protocol specific messages, wrapped in Envelopes, are sent and received to other agents, agent components and services via Connections.

"},{"location":"aea-framework-documentation/core-components/#connection","title":"Connection","text":"

Connections act as interfaces between an agent and the outside world. As such, a connection allows the agent to communicate with some entity outside of it, for example, another agent, a traditional HTTP server, a database, a reinforcement learning training environment, a blockchain, etc.

Where necessary, a Connection is responsible for translating between the framework specific Envelope with its contained message and the external service or third-party protocol (e.g. HTTP).

The framework provides one default stub connection. It implements an I/O reader and writer to send messages to the agent from a local file.

Additional connections can be added as packages. For more details on Connections read the Connection guide.

"},{"location":"aea-framework-documentation/debug/","title":"Debugging","text":"

There are multiple ways in which to configure your AEA for debugging during development. We focus on the standard Python approach here.

"},{"location":"aea-framework-documentation/debug/#using-pdb-stdlib","title":"Using pdb stdlib","text":"

You can add a debugger anywhere in your code:

import pdb; pdb.set_trace()\n

Then simply run you AEA with the --skip-consistency-check mode:

aea -s run\n

For more guidance on how to use pdb check out the documentation.

"},{"location":"aea-framework-documentation/debug/#using-an-ide","title":"Using an IDE","text":"
  • For VSCode modify the launch.json to include the following information:
{\n\"version\": \"0.2.0\",\n\"configurations\": [\n{\n\"name\": \"aea run\",\n\"type\": \"python\",\n\"request\": \"launch\",\n\"program\": \"PATH_TO_VIRTUAL_ENV/bin/aea\",\n\"args\": [\"-v\",\"DEBUG\",\"--skip-consistency-check\",\"run\"],\n\"cwd\": \"CWD\",\n\"console\": \"integratedTerminal\"\n}\n]\n}\n

where PATH_TO_VIRTUAL_ENV should be replaced with the path to the virtual environment and CWD with the working directory for the agent to debug (where the aea-config.yaml file is).

"},{"location":"aea-framework-documentation/decision-maker-transaction/","title":"Create Decision-Maker Transaction","text":"

This guide can be considered as a part 2 of the the stand-alone transaction demo. The main difference is that now we are going to use the decision-maker to sign the transaction.

First, import the libraries and the set the constant values. (Get the packages directory from the AEA repository svn export https://github.com/fetchai/agents-aea.git/trunk/packages.)

import logging\nimport time\nfrom threading import Thread\nfrom typing import Optional, cast\nfrom aea_ledger_fetchai import FetchAICrypto\nfrom aea.aea_builder import AEABuilder\nfrom aea.configurations.base import PublicId, SkillConfig\nfrom aea.crypto.helpers import create_private_key\nfrom aea.crypto.ledger_apis import LedgerApis\nfrom aea.crypto.wallet import Wallet\nfrom aea.helpers.transaction.base import RawTransaction, Terms\nfrom aea.identity.base import Identity\nfrom aea.protocols.base import Address, Message\nfrom aea.protocols.dialogue.base import Dialogue\nfrom aea.skills.base import Handler, Model, Skill, SkillContext\nfrom packages.fetchai.protocols.signing.dialogues import SigningDialogue\nfrom packages.fetchai.protocols.signing.dialogues import (\nSigningDialogues as BaseSigningDialogues,\n)\nfrom packages.fetchai.protocols.signing.message import SigningMessage\nfrom tests.conftest import get_wealth_if_needed\nlogger = logging.getLogger(\"aea\")\nlogging.basicConfig(level=logging.INFO)\nFETCHAI_PRIVATE_KEY_FILE_1 = \"fetchai_private_key_1.txt\"\nFETCHAI_PRIVATE_KEY_FILE_2 = \"fetchai_private_key_2.txt\"\n
"},{"location":"aea-framework-documentation/decision-maker-transaction/#create-a-private-key-and-an-aea","title":"Create a Private Key and an AEA","text":"

To have access to the decision-maker, which is responsible for signing transactions, we need to create an AEA. We can create an AEA with the builder, providing it with a private key we generate first.

    # Create a private key\ncreate_private_key(\nFetchAICrypto.identifier, private_key_file=FETCHAI_PRIVATE_KEY_FILE_1\n)\n# Instantiate the builder and build the AEA\n# By default, the default protocol, error skill and stub connection are added\nbuilder = AEABuilder()\nbuilder.set_name(\"my_aea\")\nbuilder.add_private_key(FetchAICrypto.identifier, FETCHAI_PRIVATE_KEY_FILE_1)\n# Create our AEA\nmy_aea = builder.build()\n
"},{"location":"aea-framework-documentation/decision-maker-transaction/#add-a-simple-skill","title":"Add a Simple Skill","text":"

Add a simple skill with a signing handler and the signing dialogues.

    # add a simple skill with handler\nskill_context = SkillContext(my_aea.context)\nskill_config = SkillConfig(name=\"simple_skill\", author=\"fetchai\", version=\"0.1.0\")\nsigning_handler = SigningHandler(\nskill_context=skill_context, name=\"signing_handler\"\n)\nsigning_dialogues_model = SigningDialogues(\nskill_context=skill_context,\nname=\"signing_dialogues\",\nself_address=str(skill_config.public_id),\n)\nsimple_skill = Skill(\nskill_config,\nskill_context,\nhandlers={signing_handler.name: signing_handler},\nmodels={signing_dialogues_model.name: signing_dialogues_model},\n)\nmy_aea.resources.add_skill(simple_skill)\n
"},{"location":"aea-framework-documentation/decision-maker-transaction/#create-a-second-identity","title":"Create a Second Identity","text":"
    # create a second identity\ncreate_private_key(\nFetchAICrypto.identifier, private_key_file=FETCHAI_PRIVATE_KEY_FILE_2\n)\ncounterparty_wallet = Wallet({FetchAICrypto.identifier: FETCHAI_PRIVATE_KEY_FILE_2})\nget_wealth_if_needed(counterparty_wallet.addresses[\"fetchai\"])\ncounterparty_identity = Identity(\nname=\"counterparty_aea\",\naddresses=counterparty_wallet.addresses,\npublic_keys=counterparty_wallet.public_keys,\ndefault_address_key=FetchAICrypto.identifier,\n)\n
"},{"location":"aea-framework-documentation/decision-maker-transaction/#create-the-signing-message","title":"Create the Signing Message","text":"

Next, we are creating the signing message and sending it to the decision-maker.

    # create signing message for decision maker to sign\nterms = Terms(\nledger_id=FetchAICrypto.identifier,\nsender_address=my_aea.identity.address,\ncounterparty_address=counterparty_identity.address,\namount_by_currency_id={\"FET\": -1},\nquantities_by_good_id={\"some_service\": 1},\nnonce=\"some_nonce\",\nfee_by_currency_id={\"FET\": 0},\n)\nget_wealth_if_needed(terms.sender_address)\nsigning_dialogues = cast(SigningDialogues, skill_context.signing_dialogues)\nstub_transaction = LedgerApis.get_transfer_transaction(\nterms.ledger_id,\nterms.sender_address,\nterms.counterparty_address,\nterms.sender_payable_amount,\nterms.sender_fee,\nterms.nonce,\n)\nsigning_msg = SigningMessage(\nperformative=SigningMessage.Performative.SIGN_TRANSACTION,\ndialogue_reference=signing_dialogues.new_self_initiated_dialogue_reference(),\nraw_transaction=RawTransaction(FetchAICrypto.identifier, stub_transaction),\nterms=terms,\n)\nsigning_dialogue = cast(\nOptional[SigningDialogue],\nsigning_dialogues.create_with_message(\"decision_maker\", signing_msg),\n)\nassert signing_dialogue is not None\nmy_aea.context.decision_maker_message_queue.put_nowait(signing_msg)\n
"},{"location":"aea-framework-documentation/decision-maker-transaction/#run-the-agent","title":"Run the Agent","text":"

Finally, we are running the agent and expect the signed transaction to be printed in the terminal.

    # Set the AEA running in a different thread\ntry:\nlogger.info(\"STARTING AEA NOW!\")\nt = Thread(target=my_aea.start)\nt.start()\n# Let it run long enough to interact with the decision maker\ntime.sleep(1)\nfinally:\n# Shut down the AEA\nlogger.info(\"STOPPING AEA NOW!\")\nmy_aea.stop()\nt.join()\n

After the completion of the signing, we get the signed transaction.

"},{"location":"aea-framework-documentation/decision-maker-transaction/#more-details","title":"More Details","text":"

To be able to register a handler that reads the internal messages, we have to create a class at the end of the file which processes the signing messages.

class SigningDialogues(Model, BaseSigningDialogues):\n\"\"\"Signing dialogues model.\"\"\"\ndef __init__(self, self_address: Address, **kwargs) -> None:\n\"\"\"\n        Initialize dialogues.\n        :return: None\n        \"\"\"\nModel.__init__(self, **kwargs)\ndef role_from_first_message(  # pylint: disable=unused-argument\nmessage: Message, receiver_address: Address\n) -> Dialogue.Role:\n\"\"\"Infer the role of the agent from an incoming/outgoing first message\n            :param message: an incoming/outgoing first message\n            :param receiver_address: the address of the receiving agent\n            :return: The role of the agent\n            \"\"\"\nreturn SigningDialogue.Role.SKILL\nBaseSigningDialogues.__init__(\nself,\nself_address=self_address,\nrole_from_first_message=role_from_first_message,\n)\nclass SigningHandler(Handler):\n\"\"\"Implement the signing handler.\"\"\"\nSUPPORTED_PROTOCOL = SigningMessage.protocol_id  # type: Optional[PublicId]\ndef setup(self) -> None:\n\"\"\"Implement the setup for the handler.\"\"\"\ndef handle(self, message: Message) -> None:\n\"\"\"\n        Implement the reaction to a message.\n        :param message: the message\n        :return: None\n        \"\"\"\nsigning_msg = cast(SigningMessage, message)\n# recover dialogue\nsigning_dialogues = cast(SigningDialogues, self.context.signing_dialogues)\nsigning_dialogue = cast(\nOptional[SigningDialogue], signing_dialogues.update(signing_msg)\n)\nif signing_dialogue is None:\nself._handle_unidentified_dialogue(signing_msg)\nreturn\n# handle message\nif signing_msg.performative is SigningMessage.Performative.SIGNED_TRANSACTION:\nself._handle_signed_transaction(signing_msg, signing_dialogue)\nelif signing_msg.performative is SigningMessage.Performative.ERROR:\nself._handle_error(signing_msg, signing_dialogue)\nelse:\nself._handle_invalid(signing_msg, signing_dialogue)\ndef teardown(self) -> None:\n\"\"\"\n        Implement the handler teardown.\n        :return: None\n        \"\"\"\ndef _handle_unidentified_dialogue(self, signing_msg: SigningMessage) -> None:\n\"\"\"\n        Handle an unidentified dialogue.\n        :param msg: the message\n        \"\"\"\nself.context.logger.info(\n\"received invalid signing message={}, unidentified dialogue.\".format(\nsigning_msg\n)\n)\ndef _handle_signed_transaction(\nself, signing_msg: SigningMessage, signing_dialogue: SigningDialogue\n) -> None:\n\"\"\"\n        Handle a signing message.\n        :param signing_msg: the signing message\n        :param signing_dialogue: the dialogue\n        :return: None\n        \"\"\"\nself.context.logger.info(\"transaction signing was successful.\")\nlogger.info(signing_msg.signed_transaction)\ndef _handle_error(\nself, signing_msg: SigningMessage, signing_dialogue: SigningDialogue\n) -> None:\n\"\"\"\n        Handle an oef search message.\n        :param signing_msg: the signing message\n        :param signing_dialogue: the dialogue\n        :return: None\n        \"\"\"\nself.context.logger.info(\n\"transaction signing was not successful. Error_code={} in dialogue={}\".format(\nsigning_msg.error_code, signing_dialogue\n)\n)\ndef _handle_invalid(\nself, signing_msg: SigningMessage, signing_dialogue: SigningDialogue\n) -> None:\n\"\"\"\n        Handle an oef search message.\n        :param signing_msg: the signing message\n        :param signing_dialogue: the dialogue\n        :return: None\n        \"\"\"\nself.context.logger.warning(\n\"cannot handle signing message of performative={} in dialogue={}.\".format(\nsigning_msg.performative, signing_dialogue\n)\n)\n

You can find the full code for this example below:

Transaction via decision-maker full code:
import logging\nimport time\nfrom threading import Thread\nfrom typing import Optional, cast\nfrom aea_ledger_fetchai import FetchAICrypto\nfrom aea.aea_builder import AEABuilder\nfrom aea.configurations.base import PublicId, SkillConfig\nfrom aea.crypto.helpers import create_private_key\nfrom aea.crypto.ledger_apis import LedgerApis\nfrom aea.crypto.wallet import Wallet\nfrom aea.helpers.transaction.base import RawTransaction, Terms\nfrom aea.identity.base import Identity\nfrom aea.protocols.base import Address, Message\nfrom aea.protocols.dialogue.base import Dialogue\nfrom aea.skills.base import Handler, Model, Skill, SkillContext\nfrom packages.fetchai.protocols.signing.dialogues import SigningDialogue\nfrom packages.fetchai.protocols.signing.dialogues import (\nSigningDialogues as BaseSigningDialogues,\n)\nfrom packages.fetchai.protocols.signing.message import SigningMessage\nfrom tests.conftest import get_wealth_if_needed\nlogger = logging.getLogger(\"aea\")\nlogging.basicConfig(level=logging.INFO)\nFETCHAI_PRIVATE_KEY_FILE_1 = \"fetchai_private_key_1.txt\"\nFETCHAI_PRIVATE_KEY_FILE_2 = \"fetchai_private_key_2.txt\"\ndef run():\n\"\"\"Run demo.\"\"\"\n# Create a private key\ncreate_private_key(\nFetchAICrypto.identifier, private_key_file=FETCHAI_PRIVATE_KEY_FILE_1\n)\n# Instantiate the builder and build the AEA\n# By default, the default protocol, error skill and stub connection are added\nbuilder = AEABuilder()\nbuilder.set_name(\"my_aea\")\nbuilder.add_private_key(FetchAICrypto.identifier, FETCHAI_PRIVATE_KEY_FILE_1)\n# Create our AEA\nmy_aea = builder.build()\n# add a simple skill with handler\nskill_context = SkillContext(my_aea.context)\nskill_config = SkillConfig(name=\"simple_skill\", author=\"fetchai\", version=\"0.1.0\")\nsigning_handler = SigningHandler(\nskill_context=skill_context, name=\"signing_handler\"\n)\nsigning_dialogues_model = SigningDialogues(\nskill_context=skill_context,\nname=\"signing_dialogues\",\nself_address=str(skill_config.public_id),\n)\nsimple_skill = Skill(\nskill_config,\nskill_context,\nhandlers={signing_handler.name: signing_handler},\nmodels={signing_dialogues_model.name: signing_dialogues_model},\n)\nmy_aea.resources.add_skill(simple_skill)\n# create a second identity\ncreate_private_key(\nFetchAICrypto.identifier, private_key_file=FETCHAI_PRIVATE_KEY_FILE_2\n)\ncounterparty_wallet = Wallet({FetchAICrypto.identifier: FETCHAI_PRIVATE_KEY_FILE_2})\nget_wealth_if_needed(counterparty_wallet.addresses[\"fetchai\"])\ncounterparty_identity = Identity(\nname=\"counterparty_aea\",\naddresses=counterparty_wallet.addresses,\npublic_keys=counterparty_wallet.public_keys,\ndefault_address_key=FetchAICrypto.identifier,\n)\n# create signing message for decision maker to sign\nterms = Terms(\nledger_id=FetchAICrypto.identifier,\nsender_address=my_aea.identity.address,\ncounterparty_address=counterparty_identity.address,\namount_by_currency_id={\"FET\": -1},\nquantities_by_good_id={\"some_service\": 1},\nnonce=\"some_nonce\",\nfee_by_currency_id={\"FET\": 0},\n)\nget_wealth_if_needed(terms.sender_address)\nsigning_dialogues = cast(SigningDialogues, skill_context.signing_dialogues)\nstub_transaction = LedgerApis.get_transfer_transaction(\nterms.ledger_id,\nterms.sender_address,\nterms.counterparty_address,\nterms.sender_payable_amount,\nterms.sender_fee,\nterms.nonce,\n)\nsigning_msg = SigningMessage(\nperformative=SigningMessage.Performative.SIGN_TRANSACTION,\ndialogue_reference=signing_dialogues.new_self_initiated_dialogue_reference(),\nraw_transaction=RawTransaction(FetchAICrypto.identifier, stub_transaction),\nterms=terms,\n)\nsigning_dialogue = cast(\nOptional[SigningDialogue],\nsigning_dialogues.create_with_message(\"decision_maker\", signing_msg),\n)\nassert signing_dialogue is not None\nmy_aea.context.decision_maker_message_queue.put_nowait(signing_msg)\n# Set the AEA running in a different thread\ntry:\nlogger.info(\"STARTING AEA NOW!\")\nt = Thread(target=my_aea.start)\nt.start()\n# Let it run long enough to interact with the decision maker\ntime.sleep(1)\nfinally:\n# Shut down the AEA\nlogger.info(\"STOPPING AEA NOW!\")\nmy_aea.stop()\nt.join()\nclass SigningDialogues(Model, BaseSigningDialogues):\n\"\"\"Signing dialogues model.\"\"\"\ndef __init__(self, self_address: Address, **kwargs) -> None:\n\"\"\"\n        Initialize dialogues.\n        :return: None\n        \"\"\"\nModel.__init__(self, **kwargs)\ndef role_from_first_message(  # pylint: disable=unused-argument\nmessage: Message, receiver_address: Address\n) -> Dialogue.Role:\n\"\"\"Infer the role of the agent from an incoming/outgoing first message\n            :param message: an incoming/outgoing first message\n            :param receiver_address: the address of the receiving agent\n            :return: The role of the agent\n            \"\"\"\nreturn SigningDialogue.Role.SKILL\nBaseSigningDialogues.__init__(\nself,\nself_address=self_address,\nrole_from_first_message=role_from_first_message,\n)\nclass SigningHandler(Handler):\n\"\"\"Implement the signing handler.\"\"\"\nSUPPORTED_PROTOCOL = SigningMessage.protocol_id  # type: Optional[PublicId]\ndef setup(self) -> None:\n\"\"\"Implement the setup for the handler.\"\"\"\ndef handle(self, message: Message) -> None:\n\"\"\"\n        Implement the reaction to a message.\n        :param message: the message\n        :return: None\n        \"\"\"\nsigning_msg = cast(SigningMessage, message)\n# recover dialogue\nsigning_dialogues = cast(SigningDialogues, self.context.signing_dialogues)\nsigning_dialogue = cast(\nOptional[SigningDialogue], signing_dialogues.update(signing_msg)\n)\nif signing_dialogue is None:\nself._handle_unidentified_dialogue(signing_msg)\nreturn\n# handle message\nif signing_msg.performative is SigningMessage.Performative.SIGNED_TRANSACTION:\nself._handle_signed_transaction(signing_msg, signing_dialogue)\nelif signing_msg.performative is SigningMessage.Performative.ERROR:\nself._handle_error(signing_msg, signing_dialogue)\nelse:\nself._handle_invalid(signing_msg, signing_dialogue)\ndef teardown(self) -> None:\n\"\"\"\n        Implement the handler teardown.\n        :return: None\n        \"\"\"\ndef _handle_unidentified_dialogue(self, signing_msg: SigningMessage) -> None:\n\"\"\"\n        Handle an unidentified dialogue.\n        :param msg: the message\n        \"\"\"\nself.context.logger.info(\n\"received invalid signing message={}, unidentified dialogue.\".format(\nsigning_msg\n)\n)\ndef _handle_signed_transaction(\nself, signing_msg: SigningMessage, signing_dialogue: SigningDialogue\n) -> None:\n\"\"\"\n        Handle a signing message.\n        :param signing_msg: the signing message\n        :param signing_dialogue: the dialogue\n        :return: None\n        \"\"\"\nself.context.logger.info(\"transaction signing was successful.\")\nlogger.info(signing_msg.signed_transaction)\ndef _handle_error(\nself, signing_msg: SigningMessage, signing_dialogue: SigningDialogue\n) -> None:\n\"\"\"\n        Handle an oef search message.\n        :param signing_msg: the signing message\n        :param signing_dialogue: the dialogue\n        :return: None\n        \"\"\"\nself.context.logger.info(\n\"transaction signing was not successful. Error_code={} in dialogue={}\".format(\nsigning_msg.error_code, signing_dialogue\n)\n)\ndef _handle_invalid(\nself, signing_msg: SigningMessage, signing_dialogue: SigningDialogue\n) -> None:\n\"\"\"\n        Handle an oef search message.\n        :param signing_msg: the signing message\n        :param signing_dialogue: the dialogue\n        :return: None\n        \"\"\"\nself.context.logger.warning(\n\"cannot handle signing message of performative={} in dialogue={}.\".format(\nsigning_msg.performative, signing_dialogue\n)\n)\nif __name__ == \"__main__\":\nrun()\n
"},{"location":"aea-framework-documentation/decision-maker/","title":"Decision Maker","text":"

The DecisionMaker can be thought of like a wallet manager plus \"economic brain\" of the AEA. It is responsible for the AEA's crypto-economic security and goal management, and it contains the preference and ownership representation of the AEA. The decision maker is the only component which has access to the wallet's private keys.

"},{"location":"aea-framework-documentation/decision-maker/#interaction-with-skills","title":"Interaction with Skills","text":"

Skills communicate with the decision maker via Messages. At present, the decision maker processes messages of two protocols:

  • SigningMessage: it is used by skills to propose a transaction to the decision-maker for signing.

  • StateUpdateMessage: it is used to initialize the decision maker with preferences and ownership states. It can also be used to update the ownership states in the decision maker if the settlement of transaction takes place.

A message, say msg, is sent to the decision maker like so from any skill:

self.context.decision_maker_message_queue.put_nowait(msg)\n

The decision maker processes messages and can accept or reject them.

To process Messages from the decision maker in a given skill you need to create a Handler, in particular a SigningHandler like so:

class SigningHandler(Handler):\nprotocol_id = SigningMessage.protocol_id\ndef handle(self, message: Message):\n\"\"\"\n        Handle a signing message.\n        :param message: the signing message from the decision maker.\n        \"\"\"\n# code to handle the message\n
"},{"location":"aea-framework-documentation/decision-maker/#custom-decisionmaker","title":"Custom DecisionMaker","text":"

The framework implements a default DecisionMakerHandler and an advanced DecisionMakerHandler. You can also implement your own and mount it.

No further configuration is needed to use the default. To use the advanced decision maker handler, add the following configuration to the aea-config.yaml of your AEA (on page 1):

decision_maker_handler:\nconfig: {}\ndotted_path: \"aea.decision_maker.gop:DecisionMakerHandler\"\nfile_path: null\n

The easiest way to add a custom decision maker handler is to run the following command to scaffold a custom DecisionMakerHandler:

aea scaffold decision-maker-handler\n

You can then implement your own custom logic to process messages and interact with the Wallet.

Note

For examples how to use these concepts have a look at the tac_ skills. These functionalities are experimental and subject to change.

"},{"location":"aea-framework-documentation/defining-data-models/","title":"Defining Data Models","text":"

In this section, we explain how to define data models, an important component of the OEF Search & Discovery. It allows agents to describe themselves and to discover the services/resources they are interested in.

In a sentence, a DataModel is a set of attributes, and a Description of a service/resource is an assignment of those attributes.

All you need to specify data models and descriptions (that is, instances of the data model) can be found in the aea.helpers.search module.

"},{"location":"aea-framework-documentation/defining-data-models/#attributes","title":"Attributes","text":"

At the lowest level of our data model language, we have the Attribute. An attribute is an abstract definition of a property.

It is identified by a name, that must be unique in a given data model (that is, we can't have two attributes that share the same name).

Every attribute has a type, that specifies the domain of the property, that is, the possible values that the attribute can assume. At the moment, we support five types of attributes:

  • strings
  • integers
  • booleans
  • floats
  • locations, i.e. instances of Location (pairs of (latitude, longitude))

An attribute can be optional, in the sense that instantiation of the attribute is not mandatory by the instances of the data model.

Finally, every attribute might have a description that explains the purpose of the attribute.

Example: suppose we have a bookshop, and we want to describe the books we sell. Presumably, we would like to include: the following properties of our books:

  • The title
  • The author
  • The genre (e.g. science fiction, horror)
  • The year of publication
  • The average rating (average of the ratings between 0 and 5)
  • The ISBN code
  • If it can be sold as an e-book.

For each of these fields, we can define an attribute by using Attribute:

from aea.helpers.search.models import Attribute, Location\nattr_title   = Attribute(\"title\", str, True, \"The title of the book.\")\nattr_author  = Attribute(\"author\", str, True, \"The author of the book.\")\nattr_genre   = Attribute(\"genre\", str, True, \"The genre of the book.\")\nattr_year    = Attribute(\"year\", int, True, \"The year of publication of the book.\")\nattr_avg_rat = Attribute(\"average_rating\",  float,    False, \"The average rating of the book.\")\nattr_isbn    = Attribute(\"ISBN\", str, True, \"The ISBN.\")\nattr_ebook   = Attribute(\"ebook_available\", bool, False, \"If the book can be sold as an e-book.\")\nattr_bookshop = Attribute(\"bookshop_pos\", Location, False, \"The location of the bookshop where you can find the book\")\n

Let's focus on the parameters of the Attribute constructor:

  1. the first one is the name of the attribute. It is needed to instantiate a data model and to define queries over it.
  2. the second one is the type of the attribute. It specifies the domain of the possible values the attribute can assume. E.g. the attribute year can only be an integer, whereas the average_rating can only be a floating-point number. The supported types are: str, int, bool, float and Location.
  3. the third one is a boolean that specifies whether the attribute is always required or it can be omitted. For example, we might not be able to specify the ebook_available attribute, maybe because it's not applicable to some kind of books.
  4. the fourth parameter is the description, that is a short description of the purpose of the attribute.
"},{"location":"aea-framework-documentation/defining-data-models/#data-models","title":"Data Models","text":"

A data model is just a set of attributes. The class that implements the data model is DataModel.

Example: let's continue with the example of the bookshop. Once we've defined the attributes, we'd like to group them in the same structure. We can do it in the following way:

from aea.helpers.search.models import DataModel\nbook_model = DataModel(\"book\", [\nattr_title,\nattr_author,\nattr_genre,\nattr_year,\nattr_avg_rat,\nattr_isbn,\nattr_ebook,\nattr_bookshop\n], \"A data model to describe books.\")\n

A DataModel requires:

  1. a name (in the example the name is \"book\") used to refer to the data model.
  2. a list of attributes, that constitutes the abstract data model.
  3. an (optional) description about the purpose of the data model.
"},{"location":"aea-framework-documentation/defining-data-models/#description","title":"Description","text":"

A Description is just an instantiation of a data model. That is, we specify a value to every attribute belonging to the data model we are interested in.

The class that implements the description is Description.

Example: now we have all we need to create a little catalogue about our books:

from aea.helpers.search.models import Description\nIt = Description({\n\"title\" :           \"It\",\n\"author\":           \"Stephen King\",\n\"genre\":            \"horror\",\n\"year\":             1986,\n\"average_rating\":   4.5,\n\"ISBN\":             \"0-670-81302-8\",\n\"ebook_available\":  True,\n\"bookshop_pos\":     Location(52.2057092, 0.1183431)\n}, book_model)\n_1984 = Description({\n\"title\" :           \"1984\",\n\"author\":           \"George Orwell\",\n\"genre\":            \"novel\",\n\"year\":             1949,\n\"ISBN\":             \"978-0451524935\",\n\"ebook_available\":  False\n}, book_model)\n

We defined the descriptions for two books, namely It and _1984, that refers to a data model.

The attributes are instantiated with a dictionary that has:

  • as keys, the name of the attributes.
  • as values, the values associated with the attributes.

Notice that in the latter book we omitted the average_rating field. We are allowed to do that because of the average_rating attribute is not mandatory.

"},{"location":"aea-framework-documentation/demos/","title":"Demos","text":"

We provide demo guides for multiple use-cases, each one involving several AEAs interacting in a different scenario.

These demos serve to highlight the concept of AEAs as well as provide inspiration for developers. Demos should not be taken as production ready software, although every care is taken to fix bugs when reported.

Demos are alphabetically sorted, we recommend you start with the weather skills demo.

"},{"location":"aea-framework-documentation/deployment/","title":"Deployment","text":"

The easiest way to run an AEA is using your development environment.

If you would like to run an AEA from a browser you can use Google Colab. This gist can be opened in Colab and implements the quick start.

For deployment, we recommend you use Docker.

"},{"location":"aea-framework-documentation/deployment/#deployment-using-a-docker-image","title":"Deployment using a Docker Image","text":"

First, we fetch a directory containing a Dockerfile and some dependencies:

svn export https://github.com/fetchai/agents-aea/branches/main/deploy-image\ncd deploy-image\n

Then follow the README.md contained in the folder.

"},{"location":"aea-framework-documentation/deployment/#deployment-using-kubernetes","title":"Deployment using Kubernetes","text":"

For an example of how to use Kubernetes navigate to our TAC deployment example.

"},{"location":"aea-framework-documentation/design-principles/","title":"Design Principles","text":"

The AEA framework development is guided by the following 8 principles:

  • Accessibility: ease of use.
  • Modularity: encourages module creation, sharing and reuse.
  • Openness: easily extensible with third-party libraries.
  • Conciseness: conceptually simple.
  • Value-driven: drives immediate value.
  • Low entry barriers: leverages existing programming languages and web protocols.
  • Safety: safe for the user (economically speaking).
  • Goal-alignment: seamless facilitation of users' preferences and goals.
"},{"location":"aea-framework-documentation/development-setup/","title":"Development Setup","text":"

An AEA consists of packages . When developing, it helps to be able to save packages in a local package registry, rather than pushing them to remote registry. This guide helps you set up a local package registry and configure the working directory for development.

There are two ways to write code for an AEA:

  1. independent of a concrete AEA project, write individual packages

  2. from within an AEA project, write packages for that AEA

"},{"location":"aea-framework-documentation/development-setup/#approach-1","title":"Approach 1","text":"

To prepare a directory (henceforth working directory) for development with the AEA framework you can take a few steps:

  • Either, manually:
    • Ensure you start with an empty working directory to avoid any unnecessary side effects.
    • In your working directory, create an empty folder called packages. This folder will act as the local registry for packages.
    • In your working directory, create a .env file with the constant PYTHONPATH=$PYTHONPATH:path_to_packages_dir where path_to_packages_dir is the path to the packages folder in your working directory.
  • Or, automated:
    • Fork our template repo for AEA development. Then clone it to your machine.
  • Depending on your editor, you might take further steps:
    • VS Code: The Python Extension in VS Code can be configured to include additional paths in the Python path. The extension has a setting for python.envFile which specifies the path to a file containing environment variable definitions. The default is set to \"python.envFile\": \"${workspaceFolder}/.env\". Provide the path to the .env file in the above settings. In the .env file, add the PYTHONPATH constant defined above. Then close VS Code and re-open it for the settings to take effect.

After developing a package, you can add it to an AEA project in the working directory (e.g. aea create AGENT_NAME && cd AGENT_NAME && aea add --local PACKAGE_TYPE PUBLIC_ID will create a new AEA project AGENT_NAME and add the package of type PACKAGE_TYPE with public id PUBLIC_ID to it.)

"},{"location":"aea-framework-documentation/development-setup/#approach-2","title":"Approach 2","text":"

It is also possible to develop directly in an AEA project:

  • Prepare a directory (henceforth working directory) for development.
  • Create a new project aea create AGENT_NAME && cd AGENT_NAME
  • Scaffold a new package aea scaffold --with-symlinks PACKAGE_TYPE PACKAGE_NAME. This will create the package scaffold under the directory {PACKAGE_TYPE}s and create symlinks to ensure package import paths line up with the folder structure. The symlinks are not needed to run the AEA. They are purely for your IDE.
  • In your working directory, create a .env file with the constant PYTHONPATH=$PYTHONPATH:path_to_project_dir where path_to_project_dir is the path to the AEA project contained in your working directory.
  • Depending on your editor, you might take further steps:
    • VS Code: The Python Extension in VS Code can be configured to include additional paths in the Python path. The extension has a setting for python.envFile which specifies the path to a file containing environment variable definitions. The default is set to \"python.envFile\": \"${workspaceFolder}/.env\". Provide the path to the .env file in the above settings. In the .env file, add the PYTHONPATH constant defined above. Then close VS Code and re-open it for the settings to take effect.
"},{"location":"aea-framework-documentation/development-setup/#general-advice","title":"General Advice","text":"

This advice partially overlaps with the previous two sections:

  • When developing a specific AEA, it might be helpful to publish/push or fetch/add from local registry. From your working directory/AEA project, simply execute the usual AEA CLI commands. The CLI will first search in the packages directory, then in the remote AEA registry. You can explicitly point to local registry by providing flag --local or --remote to only point to remote registry (see here for more details on CLI commands).
  • When working on an AEA, it may help to provide a symbolic link to the packages directory, so that the import paths are detected by your editor. Simply create an empty file with touch packages in your AEA project, then create a symbolic link to the packages directory with ln -s ../packages packages.
  • Alternatively, it can help to provide symbolic links within an AEA to align import paths with folder structure. Simply create an empty file with touch packages in your AEA project, then create a symbolic link to ln -s vendor packages.
"},{"location":"aea-framework-documentation/diagram/","title":"Architectural Diagram","text":"

The framework has two distinctive parts.

  • A core that is developed by the Fetch.ai team as well as external contributors.
  • Extensions (also known as packages) developed by any developer.

Currently, the framework supports four types of packages which can be added to the core as modules:

  • Skills encapsulate logic that deliver economic value to the AEA. Skills are the main focus of the framework's extensibility.
  • Protocols define the structure of agent-to-agent and component-to-component interactions (messages and dialogues) for agents.
  • Connections provide interfaces for the agent to connect with the outside world. They wrap SDKs or APIs and provide interfaces to networks, ledgers and other services.
  • Contracts wrap smart contracts for Fetch.ai and third-party decentralized ledgers.

The following figure illustrates the framework's architecture:

The execution is broken down in more detail below:

The agent operation breaks down into three parts:

  • Setup: calls the setup() method of all registered resources
  • Operation:
    • Agent loop (Thread 1 - Asynchronous agent loop):
      • react(): this function grabs all Envelopes waiting in the InBox queue and calls the handle() method on the Handler(s) responsible for them.
      • act(): this function calls the act() method of all registered Behaviours.
      • update(): this function enqueues scheduled tasks for execution with the TaskManager and executes the decision maker.
    • Task loop (Thread 2- Synchronous): executes available tasks
    • Decision maker loop (Thread 3- Synchronous): processes internal messages
    • Multiplexer (Thread 4 - Asynchronous event loop): processes incoming and outgoing messages across several connections asynchronously.
  • Teardown: calls the teardown() method of all registered resources

To prevent a developer from blocking the main loop with custom skill code, an execution time limit is applied to every Behaviour.act and Handler.handle call.

By default, the execution limit is set to 0 seconds, which disables the feature. You can set the limit to a strictly positive value (e.g. 0.1 seconds) to test your AEA for production readiness. If the act or handle time exceed this limit, the call will be terminated.

An appropriate message is added to the logs in the case of some code execution being terminated.

"},{"location":"aea-framework-documentation/ecosystem/","title":"Agent Ecosystem","text":"

AEAs are situated within a larger ecosystem comprised of various other systems and technology layers.

"},{"location":"aea-framework-documentation/ecosystem/#agent-communication-network-acn","title":"Agent Communication Network (ACN)","text":"

ACN is a peer-to-peer communication network for agents. It allows AEAs to send and receive envelopes between each other.

The implementation builds on the open-source libp2p library. A distributed hash table is used by all participating peers to maintain a mapping between agents' cryptographic addresses and their network addresses.

Agents can receive messages from other agents if they are both connected to the ACN (see here for an example).

"},{"location":"aea-framework-documentation/ecosystem/#search-and-discovery","title":"Search and Discovery","text":"

An sOEF node allows agents to discover each other. In particular, agents can register themselves and the services they offer, and can search for agents who offer specific services.

For two agents to be able to find each other, at least one must register itself on the sOEF and the other must query the sOEF node for it. Detailed documentation is provided here.

"},{"location":"aea-framework-documentation/ecosystem/#ledgers","title":"Ledgers","text":"

Ledgers enable AEAs to store transactions, for example involving the transfer of funds to each other, or the execution of smart contracts. They optionally ensure the truth and integrity of agent to agent interactions.

Although a ledger can, in principle, be used to store structured data (e.g. training data in a machine learning model), in most cases the resulting costs and privacy implications do not make this sustainable. Instead, usually only references to structured data - often in the form of hashes - are stored on a ledger, and the actual data is stored off-chain.

The Python implementation of the AEA Framework currently integrates with three ledgers:

  • Fetch.ai ledger
  • Ethereum ledger
  • Cosmos ledger

Furthermore, the framework makes it straightforward for any developer to create a ledger plugin, adding support for another ledger.

"},{"location":"aea-framework-documentation/ecosystem/#aeas-as-second-layer-technology","title":"AEAs as Second Layer Technology","text":"

The following presentation discusses how AEAs can be seen as second layer technology to ledgers.

"},{"location":"aea-framework-documentation/erc1155-skills/","title":"Contract Deploy and Interact","text":"

The AEA erc1155_deploy and erc1155_client skills demonstrate an interaction between two AEAs which use a smart contract.

  • The erc1155_deploy skill deploys the smart contract, creates and mints items.
  • The erc1155_client skill signs a transaction to complete a trustless trade with its counterparty.
"},{"location":"aea-framework-documentation/erc1155-skills/#preparation-instructions","title":"Preparation Instructions","text":""},{"location":"aea-framework-documentation/erc1155-skills/#dependencies","title":"Dependencies","text":"

Follow the Preliminaries and Installation sections from the AEA quick start.

"},{"location":"aea-framework-documentation/erc1155-skills/#discussion","title":"Discussion","text":"

The scope of this guide is demonstrating how you can deploy a smart contract and interact with it using AEAs. In this specific demo, you create two AEAs. One deploys and creates tokens inside a smart contract. The other signs a transaction to complete an atomic swap. The smart contract used is ERC1155 with a one-step atomic swap functionality. This means the trade between the two AEAs can be trustless.

Note

This is only for demonstrative purposes since the AEA deploying the contract also has the ability to mint tokens. In reality, the transfer of tokens from the AEA signing the transaction is worthless.

"},{"location":"aea-framework-documentation/erc1155-skills/#demo","title":"Demo","text":""},{"location":"aea-framework-documentation/erc1155-skills/#create-the-deployer-aea","title":"Create the Deployer AEA","text":"

Fetch the AEA that will deploy the contract:

aea fetch fetchai/erc1155_deployer:0.34.5\ncd erc1155_deployer\naea install\naea build\n
Alternatively, create from scratch:

Create the AEA that will deploy the contract.

aea create erc1155_deployer\ncd erc1155_deployer\naea add connection fetchai/p2p_libp2p:0.27.5\naea add connection fetchai/soef:0.27.6\naea add connection fetchai/ledger:0.21.5\naea add skill fetchai/erc1155_deploy:0.31.6\naea config set --type dict agent.dependencies \\\n'{\n  \"aea-ledger-fetchai\": {\"version\": \"<2.0.0,>=1.0.0\"},\n  \"aea-ledger-ethereum\": {\"version\": \"<2.0.0,>=1.0.0\"},\n  \"aea-ledger-cosmos\": {\"version\": \"<2.0.0,>=1.0.0\"}\n}'\naea config set agent.default_connection fetchai/p2p_libp2p:0.27.5\naea config set --type dict agent.default_routing \\\n'{\n  \"fetchai/contract_api:1.1.7\": \"fetchai/ledger:0.21.5\",\n  \"fetchai/ledger_api:1.1.7\": \"fetchai/ledger:0.21.5\",\n  \"fetchai/oef_search:1.1.7\": \"fetchai/soef:0.27.6\"\n}'\naea config set --type list vendor.fetchai.connections.p2p_libp2p.cert_requests \\\n'[{\"identifier\": \"acn\", \"ledger_id\": \"ethereum\", \"not_after\": \"2023-01-01\", \"not_before\": \"2022-01-01\", \"public_key\": \"fetchai\", \"save_path\": \".certs/conn_cert.txt\"}]'\naea install\naea build\n

And change the default ledger:

aea config set agent.default_ledger ethereum\n

Create a private key for the deployer AEA and add it for Ethereum use:

aea generate-key ethereum\naea add-key ethereum ethereum_private_key.txt\n

Create a private key for the P2P connection:

aea generate-key fetchai fetchai_connection_private_key.txt\naea add-key fetchai fetchai_connection_private_key.txt --connection\n

Finally, certify the key for use by the connections that request that:

aea issue-certificates\n
"},{"location":"aea-framework-documentation/erc1155-skills/#create-the-client-aea","title":"Create the Client AEA","text":"

In another terminal, fetch the client AEA which will receive some tokens from the deployer.

aea fetch fetchai/erc1155_client:0.34.5\ncd erc1155_client\naea install\naea build\n
Alternatively, create from scratch:

Create the AEA that will get some tokens from the deployer.

aea create erc1155_client\ncd erc1155_client\naea add connection fetchai/p2p_libp2p:0.27.5\naea add connection fetchai/soef:0.27.6\naea add connection fetchai/ledger:0.21.5\naea add skill fetchai/erc1155_client:0.29.6\naea config set --type dict agent.dependencies \\\n'{\n  \"aea-ledger-fetchai\": {\"version\": \"<2.0.0,>=1.0.0\"},\n  \"aea-ledger-ethereum\": {\"version\": \"<2.0.0,>=1.0.0\"},\n  \"aea-ledger-cosmos\": {\"version\": \"<2.0.0,>=1.0.0\"}\n}'\naea config set agent.default_connection fetchai/p2p_libp2p:0.27.5\naea config set --type dict agent.default_routing \\\n'{\n  \"fetchai/contract_api:1.1.7\": \"fetchai/ledger:0.21.5\",\n  \"fetchai/ledger_api:1.1.7\": \"fetchai/ledger:0.21.5\",\n  \"fetchai/oef_search:1.1.7\": \"fetchai/soef:0.27.6\"\n}'\naea config set --type list vendor.fetchai.connections.p2p_libp2p.cert_requests \\\n'[{\"identifier\": \"acn\", \"ledger_id\": \"ethereum\", \"not_after\": \"2023-01-01\", \"not_before\": \"2022-01-01\", \"public_key\": \"fetchai\", \"save_path\": \".certs/conn_cert.txt\"}]'\naea install\naea build\n

And change the default ledger:

aea config set agent.default_ledger ethereum\n

Create a private key for the client AEA and add it for Ethereum use:

aea generate-key ethereum\naea add-key ethereum ethereum_private_key.txt\n

Create a private key for the P2P connection:

aea generate-key fetchai fetchai_connection_private_key.txt\naea add-key fetchai fetchai_connection_private_key.txt --connection\n

Finally, certify the key for use by the connections that request that:

aea issue-certificates\n
"},{"location":"aea-framework-documentation/erc1155-skills/#run-ganache","title":"Run Ganache","text":"

Execute the following command to run Ganache:

docker run -p 8545:8545 trufflesuite/ganache-cli:latest --verbose --gasPrice=0 --gasLimit=0x1fffffffffffff --account=\"$(cat erc1155_deployer/ethereum_private_key.txt),1000000000000000000000\" --account=\"$(cat erc1155_client/ethereum_private_key.txt),1000000000000000000000\"\n

Wait some time for the wealth creation to be mined on Ropsten.

Check your wealth:

aea get-wealth ethereum\n

You should get 1000000000000000000000.

Note

If no wealth appears after a while, then try funding the private key directly using a web faucet.

"},{"location":"aea-framework-documentation/erc1155-skills/#update-soef-configurations-for-both-aeas","title":"Update SOEF Configurations for both AEAs","text":"

Update the SOEF configuration in both AEA projects:

aea config set vendor.fetchai.connections.soef.config.chain_identifier ethereum\n
"},{"location":"aea-framework-documentation/erc1155-skills/#run-the-aeas","title":"Run the AEAs","text":"

First, run the deployer AEA:

aea run\n

Once you see a message of the form To join its network use multiaddr 'SOME_ADDRESS' take note of this address.

Alternatively, use aea get-multiaddress fetchai -c -i fetchai/p2p_libp2p:0.27.5 -u public_uri to retrieve the address. The output will be something like /dns4/127.0.0.1/tcp/9000/p2p/16Uiu2HAm2JPsUX1Su59YVDXJQizYkNSe8JCusqRpLeeTbvY76fE5.

This is the entry peer address for the local agent communication network created by the deployer.

This AEA then performs the following steps:

  • deploys the smart contract
  • creates a batch of items in the smart contract
  • mints a batch of items in the smart contract

At some point you should see the log output:

registering service on SOEF.\n

At this point, configure the client AEA to connect to the same local ACN created by the deployer by running the following command in the client's terminal, replacing SOME_ADDRESS with the value you noted above:

aea config set --type dict vendor.fetchai.connections.p2p_libp2p.config \\\n'{\n  \"delegate_uri\": \"127.0.0.1:11001\",\n  \"entry_peers\": [\"SOME_ADDRESS\"],\n  \"local_uri\": \"127.0.0.1:9001\",\n  \"log_file\": \"libp2p_node.log\",\n  \"public_uri\": \"127.0.0.1:9001\"\n}'\n

Then, run the client AEA:

aea run\n

You will see that after discovery, the two AEAs exchange information about the transaction and the client at the end signs and sends the signature to the deployer AEA to send it to the network.

Note

Transactions on Ropsten can take a significant amount of time! If you run the example a second time, and the previous transaction is still pending, it can lead to a failure. The warning message Cannot verify whether transaction improves utility. Assuming it does! can be ignored.

"},{"location":"aea-framework-documentation/erc1155-skills/#delete-the-aeas","title":"Delete the AEAs","text":"

When you're done, stop the agents (CTRL+C), go up a level and delete the AEAs.

cd ..\naea delete erc1155_deployer\naea delete erc1155_client\n
"},{"location":"aea-framework-documentation/erc1155-skills/#communication","title":"Communication","text":"

This diagram shows the communication between the various entities in this interaction:

    sequenceDiagram\n        participant Search\n        participant Erc1155_contract\n        participant Client_AEA\n        participant Deployer_AEA\n        participant Blockchain\n\n        activate Search\n        activate Erc1155_contract\n        activate Client_AEA\n        activate Deployer_AEA\n        activate Blockchain\n\n        Deployer_AEA->>Blockchain: deployes smart contract\n        Deployer_AEA->>ERC1155_contract: creates tokens\n        Deployer_AEA->>ERC1155_contract: mint tokens       \n        Deployer_AEA->>Search: register_service\n        Client_AEA->>Search: search\n        Search-->>Client_AEA: list_of_agents\n        Client_AEA->>Deployer_AEA: call_for_proposal\n        Deployer_AEA->>Client_AEA: inform_message\n        Client_AEA->>Deployer_AEA: signature\n        Deployer_AEA->>Blockchain: send_transaction\n        Client_AEA->>ERC1155_contract: asks_balance\n\n        deactivate Search\n        deactivate Erc1155_contract\n        deactivate Client_AEA\n        deactivate Deployer_AEA\n        deactivate Blockchain
"},{"location":"aea-framework-documentation/generic-skills-step-by-step/","title":"Trade between Two AEAs","text":"

This guide is a step-by-step introduction to building AEAs that advertise their static and dynamic data, find other AEAs with required data, negotiate terms of trade, and carry out trades via ledger transactions.

If you simply want to run the resulting AEAs go here.

"},{"location":"aea-framework-documentation/generic-skills-step-by-step/#dependencies-required","title":"Dependencies (Required)","text":"

Follow the Preliminaries and Installation sections from the AEA quick start.

"},{"location":"aea-framework-documentation/generic-skills-step-by-step/#reference-code-optional","title":"Reference Code (Optional)","text":"

This step-by-step guide goes through the creation of two AEAs which are already developed by Fetch.ai. You can get the finished AEAs, and compare your code against them, by following the next steps:

aea fetch fetchai/generic_seller:0.29.5\ncd generic_seller\naea eject skill fetchai/generic_seller:0.28.6\ncd ..\n
aea fetch fetchai/generic_buyer:0.30.5\ncd generic_buyer\naea eject skill fetchai/generic_buyer:0.27.6\ncd ..\n
"},{"location":"aea-framework-documentation/generic-skills-step-by-step/#simplification-step","title":"Simplification Step","text":"

To keep file paths consistent with the reference code, we suggest you initialize your local author as fetchai for the purpose of this demo only:

aea init --reset --author fetchai\n
"},{"location":"aea-framework-documentation/generic-skills-step-by-step/#generic-seller-aea","title":"Generic Seller AEA","text":""},{"location":"aea-framework-documentation/generic-skills-step-by-step/#step-1-create-the-aea","title":"Step 1: Create the AEA","text":"

Create a new AEA by typing the following command in the terminal:

aea create my_generic_seller\ncd my_generic_seller\naea install\n

Our newly created AEA is inside the current working directory. Let\u2019s create our new skill that will handle the sale of data. Type the following command:

aea scaffold skill generic_seller\n

The scaffold skill command creates the correct structure for a new skill inside our AEA project. You can locate the newly created skill under the \"skills\" folder (my_generic_seller/skills/generic_seller/), and it must contain the following files:

  • __init__.py
  • behaviours.py
  • handlers.py
  • my_model.py
  • skills.yaml
"},{"location":"aea-framework-documentation/generic-skills-step-by-step/#step-2-create-the-behaviour","title":"Step 2: Create the Behaviour","text":"

A Behaviour class contains the business logic specific to actions initiated by the AEA, rather than reactions to other events.

Open the behaviours.py file (my_generic_seller/skills/generic_seller/behaviours.py) and replace the stub code with the following:

from typing import Any, Optional, cast\nfrom aea.helpers.search.models import Description\nfrom aea.skills.behaviours import TickerBehaviour\nfrom packages.fetchai.connections.ledger.base import (\nCONNECTION_ID as LEDGER_CONNECTION_PUBLIC_ID,\n)\nfrom packages.fetchai.protocols.ledger_api.message import LedgerApiMessage\nfrom packages.fetchai.protocols.oef_search.message import OefSearchMessage\nfrom packages.fetchai.skills.generic_seller.dialogues import (\nLedgerApiDialogues,\nOefSearchDialogues,\n)\nfrom packages.fetchai.skills.generic_seller.strategy import GenericStrategy\nDEFAULT_SERVICES_INTERVAL = 60.0\nDEFAULT_MAX_SOEF_REGISTRATION_RETRIES = 5\nLEDGER_API_ADDRESS = str(LEDGER_CONNECTION_PUBLIC_ID)\nclass GenericServiceRegistrationBehaviour(TickerBehaviour):\n\"\"\"This class implements a behaviour.\"\"\"\ndef __init__(self, **kwargs: Any):\n\"\"\"Initialise the behaviour.\"\"\"\nservices_interval = kwargs.pop(\n\"services_interval\", DEFAULT_SERVICES_INTERVAL\n)  # type: int\nself._max_soef_registration_retries = kwargs.pop(\n\"max_soef_registration_retries\", DEFAULT_MAX_SOEF_REGISTRATION_RETRIES\n)  # type: int\nsuper().__init__(tick_interval=services_interval, **kwargs)\nself.failed_registration_msg = None  # type: Optional[OefSearchMessage]\nself._nb_retries = 0\ndef setup(self) -> None:\n\"\"\"Implement the setup.\"\"\"\nstrategy = cast(GenericStrategy, self.context.strategy)\nif strategy.is_ledger_tx:\nledger_api_dialogues = cast(\nLedgerApiDialogues, self.context.ledger_api_dialogues\n)\nledger_api_msg, _ = ledger_api_dialogues.create(\ncounterparty=LEDGER_API_ADDRESS,\nperformative=LedgerApiMessage.Performative.GET_BALANCE,\nledger_id=strategy.ledger_id,\naddress=cast(str, self.context.agent_addresses.get(strategy.ledger_id)),\n)\nself.context.outbox.put_message(message=ledger_api_msg)\nself._register_agent()\ndef act(self) -> None:\n\"\"\"Implement the act.\"\"\"\nself._retry_failed_registration()\ndef teardown(self) -> None:\n\"\"\"Implement the task teardown.\"\"\"\nself._unregister_service()\nself._unregister_agent()\ndef _retry_failed_registration(self) -> None:\n\"\"\"Retry a failed registration.\"\"\"\nif self.failed_registration_msg is not None:\nself._nb_retries += 1\nif self._nb_retries > self._max_soef_registration_retries:\nself.context.is_active = False\nreturn\noef_search_dialogues = cast(\nOefSearchDialogues, self.context.oef_search_dialogues\n)\noef_search_msg, _ = oef_search_dialogues.create(\ncounterparty=self.failed_registration_msg.to,\nperformative=self.failed_registration_msg.performative,\nservice_description=self.failed_registration_msg.service_description,\n)\nself.context.outbox.put_message(message=oef_search_msg)\nself.context.logger.info(\nf\"Retrying registration on SOEF. Retry {self._nb_retries} out of {self._max_soef_registration_retries}.\"\n)\nself.failed_registration_msg = None\ndef _register(self, description: Description, logger_msg: str) -> None:\n\"\"\"\n        Register something on the SOEF.\n        :param description: the description of what is being registered\n        :param logger_msg: the logger message to print after the registration\n        \"\"\"\noef_search_dialogues = cast(\nOefSearchDialogues, self.context.oef_search_dialogues\n)\noef_search_msg, _ = oef_search_dialogues.create(\ncounterparty=self.context.search_service_address,\nperformative=OefSearchMessage.Performative.REGISTER_SERVICE,\nservice_description=description,\n)\nself.context.outbox.put_message(message=oef_search_msg)\nself.context.logger.info(logger_msg)\ndef _register_agent(self) -> None:\n\"\"\"Register the agent's location.\"\"\"\nstrategy = cast(GenericStrategy, self.context.strategy)\ndescription = strategy.get_location_description()\nself._register(description, \"registering agent on SOEF.\")\ndef register_service(self) -> None:\n\"\"\"Register the agent's service.\"\"\"\nstrategy = cast(GenericStrategy, self.context.strategy)\ndescription = strategy.get_register_service_description()\nself._register(description, \"registering agent's service on the SOEF.\")\ndef register_genus(self) -> None:\n\"\"\"Register the agent's personality genus.\"\"\"\nstrategy = cast(GenericStrategy, self.context.strategy)\ndescription = strategy.get_register_personality_description()\nself._register(\ndescription, \"registering agent's personality genus on the SOEF.\"\n)\ndef register_classification(self) -> None:\n\"\"\"Register the agent's personality classification.\"\"\"\nstrategy = cast(GenericStrategy, self.context.strategy)\ndescription = strategy.get_register_classification_description()\nself._register(\ndescription, \"registering agent's personality classification on the SOEF.\"\n)\ndef _unregister_service(self) -> None:\n\"\"\"Unregister service from the SOEF.\"\"\"\nstrategy = cast(GenericStrategy, self.context.strategy)\ndescription = strategy.get_unregister_service_description()\noef_search_dialogues = cast(\nOefSearchDialogues, self.context.oef_search_dialogues\n)\noef_search_msg, _ = oef_search_dialogues.create(\ncounterparty=self.context.search_service_address,\nperformative=OefSearchMessage.Performative.UNREGISTER_SERVICE,\nservice_description=description,\n)\nself.context.outbox.put_message(message=oef_search_msg)\nself.context.logger.info(\"unregistering service from SOEF.\")\ndef _unregister_agent(self) -> None:\n\"\"\"Unregister agent from the SOEF.\"\"\"\nstrategy = cast(GenericStrategy, self.context.strategy)\ndescription = strategy.get_location_description()\noef_search_dialogues = cast(\nOefSearchDialogues, self.context.oef_search_dialogues\n)\noef_search_msg, _ = oef_search_dialogues.create(\ncounterparty=self.context.search_service_address,\nperformative=OefSearchMessage.Performative.UNREGISTER_SERVICE,\nservice_description=description,\n)\nself.context.outbox.put_message(message=oef_search_msg)\nself.context.logger.info(\"unregistering agent from SOEF.\")\n

This TickerBehaviour registers (see setup method) and de-registers (see teardown method) our AEA\u2019s service on the SOEF search node at regular tick intervals (here 60 seconds). By registering, the AEA becomes discoverable to other AEAs.

In setup, prior to registrations, we send a message to the ledger connection to check the account balance for the AEA's address on the configured ledger.

"},{"location":"aea-framework-documentation/generic-skills-step-by-step/#step-3-create-the-handler","title":"Step 3: Create the Handler","text":"

So far, we have tasked the AEA with sending register/unregister requests to the SOEF search node. However at present, the AEA has no way of handling the responses it receives from the search node, or in fact messages sent by any other AEA.

We have to specify the logic to negotiate with another AEA based on the strategy we want our AEA to follow. The following diagram illustrates the negotiation flow that we want this AEA to use, as well as interactions with a search node and the blockchain between a seller_AEA and a buyer_AEA.

    sequenceDiagram\n        participant Search\n        participant Buyer_AEA\n        participant Seller_AEA\n        participant Blockchain\n\n        activate Buyer_AEA\n        activate Search\n        activate Seller_AEA\n        activate Blockchain\n\n        Seller_AEA->>Search: register_service\n        Buyer_AEA->>Search: search\n        Search-->>Buyer_AEA: list_of_agents\n        Buyer_AEA->>Seller_AEA: call_for_proposal\n        Seller_AEA->>Buyer_AEA: propose\n        Buyer_AEA->>Seller_AEA: accept\n        Seller_AEA->>Buyer_AEA: match_accept\n        loop Once with LedgerConnection\n            Buyer_AEA->>Buyer_AEA: Get raw transaction from ledger api\n        end\n        loop Once with DecisionMaker\n            Buyer_AEA->>Buyer_AEA: Get signed transaction from decision maker\n        end\n        loop Once with LedgerConnection\n            Buyer_AEA->>Buyer_AEA: Send transaction and get digest from ledger api\n            Buyer_AEA->>Blockchain: transfer_funds\n        end\n        Buyer_AEA->>Seller_AEA: send_transaction_digest\n        Seller_AEA->>Blockchain: check_transaction_status\n        Seller_AEA->>Buyer_AEA: send_data\n\n        deactivate Buyer_AEA\n        deactivate Search\n        deactivate Seller_AEA\n        deactivate Blockchain

In our case, my_generic_seller is the Seller_AEA in the above figure.

Let us now implement a Handler to deal with incoming messages. Open the handlers.py file (my_generic_seller/skills/generic_seller/handlers.py) and replace the stub code with the following:

from typing import Optional, cast\nfrom aea.configurations.base import PublicId\nfrom aea.crypto.ledger_apis import LedgerApis\nfrom aea.helpers.transaction.base import TransactionDigest\nfrom aea.protocols.base import Message\nfrom aea.skills.base import Handler\nfrom packages.fetchai.connections.ledger.base import (\nCONNECTION_ID as LEDGER_CONNECTION_PUBLIC_ID,\n)\nfrom packages.fetchai.protocols.default.message import DefaultMessage\nfrom packages.fetchai.protocols.fipa.message import FipaMessage\nfrom packages.fetchai.protocols.ledger_api.message import LedgerApiMessage\nfrom packages.fetchai.protocols.oef_search.message import OefSearchMessage\nfrom packages.fetchai.skills.generic_seller.behaviours import (\nGenericServiceRegistrationBehaviour,\n)\nfrom packages.fetchai.skills.generic_seller.dialogues import (\nDefaultDialogues,\nFipaDialogue,\nFipaDialogues,\nLedgerApiDialogue,\nLedgerApiDialogues,\nOefSearchDialogue,\nOefSearchDialogues,\n)\nfrom packages.fetchai.skills.generic_seller.strategy import GenericStrategy\nLEDGER_API_ADDRESS = str(LEDGER_CONNECTION_PUBLIC_ID)\nclass GenericFipaHandler(Handler):\n\"\"\"This class implements a FIPA handler.\"\"\"\nSUPPORTED_PROTOCOL = FipaMessage.protocol_id  # type: Optional[PublicId]\ndef setup(self) -> None:\n\"\"\"Implement the setup for the handler.\"\"\"\ndef handle(self, message: Message) -> None:\n\"\"\"\n        Implement the reaction to a message.\n        :param message: the message\n        \"\"\"\nfipa_msg = cast(FipaMessage, message)\n# recover dialogue\nfipa_dialogues = cast(FipaDialogues, self.context.fipa_dialogues)\nfipa_dialogue = cast(FipaDialogue, fipa_dialogues.update(fipa_msg))\nif fipa_dialogue is None:\nself._handle_unidentified_dialogue(fipa_msg)\nreturn\n# handle message\nif fipa_msg.performative == FipaMessage.Performative.CFP:\nself._handle_cfp(fipa_msg, fipa_dialogue)\nelif fipa_msg.performative == FipaMessage.Performative.DECLINE:\nself._handle_decline(fipa_msg, fipa_dialogue, fipa_dialogues)\nelif fipa_msg.performative == FipaMessage.Performative.ACCEPT:\nself._handle_accept(fipa_msg, fipa_dialogue)\nelif fipa_msg.performative == FipaMessage.Performative.INFORM:\nself._handle_inform(fipa_msg, fipa_dialogue)\nelse:\nself._handle_invalid(fipa_msg, fipa_dialogue)\ndef teardown(self) -> None:\n\"\"\"Implement the handler teardown.\"\"\"\n

The code above contains the logic for handling FipaMessages received by the my_generic_seller AEA. We use FipaDialogues (more on this below) to keep track of the progress of the negotiation dialogue between the my_generic_seller AEA and the my_generic_buyer AEA.

In the above handle method, we first check if a received message belongs to an existing dialogue or if we have to create a new dialogue (the recover dialogue part). Once this is done, we break down the AEA's response to each type of negotiation message, as indicated by the message's performative (the handle message part). Therefore, we implement the AEA's response to each negotiation message type in a different handler function.

Below the unused teardown function, we continue by adding the following function:

    def _handle_unidentified_dialogue(self, fipa_msg: FipaMessage) -> None:\n\"\"\"\n        Handle an unidentified dialogue.\n        :param fipa_msg: the message\n        \"\"\"\nself.context.logger.info(\n\"received invalid fipa message={}, unidentified dialogue.\".format(fipa_msg)\n)\ndefault_dialogues = cast(DefaultDialogues, self.context.default_dialogues)\ndefault_msg, _ = default_dialogues.create(\ncounterparty=fipa_msg.sender,\nperformative=DefaultMessage.Performative.ERROR,\nerror_code=DefaultMessage.ErrorCode.INVALID_DIALOGUE,\nerror_msg=\"Invalid dialogue.\",\nerror_data={\"fipa_message\": fipa_msg.encode()},\n)\nself.context.outbox.put_message(message=default_msg)\n

The above code handles an unidentified dialogue by responding to the sender with a DefaultMessage containing the appropriate error information.

The next code block handles CFP (call-for-proposal) negotiation messages. Paste the following code below the _handle_unidentified_dialogue function:

    def _handle_cfp(self, fipa_msg: FipaMessage, fipa_dialogue: FipaDialogue) -> None:\n\"\"\"\n        Handle the CFP.\n        If the CFP matches the supplied services then send a PROPOSE, otherwise send a DECLINE.\n        :param fipa_msg: the message\n        :param fipa_dialogue: the dialogue object\n        \"\"\"\nself.context.logger.info(\n\"received CFP from sender={}\".format(fipa_msg.sender[-5:])\n)\nstrategy = cast(GenericStrategy, self.context.strategy)\nif strategy.is_matching_supply(fipa_msg.query):\nproposal, terms, data_for_sale = strategy.generate_proposal_terms_and_data(\nfipa_msg.query, fipa_msg.sender\n)\nfipa_dialogue.data_for_sale = data_for_sale\nfipa_dialogue.terms = terms\nself.context.logger.info(\n\"sending a PROPOSE with proposal={} to sender={}\".format(\nproposal.values, fipa_msg.sender[-5:]\n)\n)\nproposal_msg = fipa_dialogue.reply(\nperformative=FipaMessage.Performative.PROPOSE,\ntarget_message=fipa_msg,\nproposal=proposal,\n)\nself.context.outbox.put_message(message=proposal_msg)\nelse:\nself.context.logger.info(\n\"declined the CFP from sender={}\".format(fipa_msg.sender[-5:])\n)\ndecline_msg = fipa_dialogue.reply(\nperformative=FipaMessage.Performative.DECLINE,\ntarget_message=fipa_msg,\n)\nself.context.outbox.put_message(message=decline_msg)\n

The above code sends a PROPOSE message back to the buyer as a response to its CFP if the requested services match our seller agent's supplied services, otherwise it will respond with a DECLINE message.

The next code-block handles the decline message we receive from the buyer. Add the following code below the _handle_cfpfunction:

    def _handle_decline(\nself,\nfipa_msg: FipaMessage,\nfipa_dialogue: FipaDialogue,\nfipa_dialogues: FipaDialogues,\n) -> None:\n\"\"\"\n        Handle the DECLINE.\n        Close the dialogue.\n        :param fipa_msg: the message\n        :param fipa_dialogue: the dialogue object\n        :param fipa_dialogues: the dialogues object\n        \"\"\"\nself.context.logger.info(\n\"received DECLINE from sender={}\".format(fipa_msg.sender[-5:])\n)\nfipa_dialogues.dialogue_stats.add_dialogue_endstate(\nFipaDialogue.EndState.DECLINED_PROPOSE, fipa_dialogue.is_self_initiated\n)\n

If we receive a decline message from the buyer we close the dialogue and terminate this conversation with my_generic_buyer.

Alternatively, we might receive an ACCEPT message. In order to handle this option add the following code below the _handle_decline function:

    def _handle_accept(\nself, fipa_msg: FipaMessage, fipa_dialogue: FipaDialogue\n) -> None:\n\"\"\"\n        Handle the ACCEPT.\n        Respond with a MATCH_ACCEPT_W_INFORM which contains the address to send the funds to.\n        :param fipa_msg: the message\n        :param fipa_dialogue: the dialogue object\n        \"\"\"\nself.context.logger.info(\n\"received ACCEPT from sender={}\".format(fipa_msg.sender[-5:])\n)\ninfo = {\"address\": fipa_dialogue.terms.sender_address}\nmatch_accept_msg = fipa_dialogue.reply(\nperformative=FipaMessage.Performative.MATCH_ACCEPT_W_INFORM,\ntarget_message=fipa_msg,\ninfo=info,\n)\nself.context.logger.info(\n\"sending MATCH_ACCEPT_W_INFORM to sender={} with info={}\".format(\nfipa_msg.sender[-5:],\ninfo,\n)\n)\nself.context.outbox.put_message(message=match_accept_msg)\n

When my_generic_buyer accepts the Proposal we send it and sends an ACCEPT message, we have to respond with another message (MATCH_ACCEPT_W_INFORM) to match the acceptance of the terms of trade and to inform the buyer of the address we would like it to send the funds to.

Lastly, we must handle an INFORM message, which the buyer uses to inform us that it has indeed sent the funds to the provided address. Add the following code at the end of the file:

    def _handle_inform(\nself, fipa_msg: FipaMessage, fipa_dialogue: FipaDialogue\n) -> None:\n\"\"\"\n        Handle the INFORM.\n        If the INFORM message contains the transaction_digest then verify that it is settled, otherwise do nothing.\n        If the transaction is settled, send the data, otherwise do nothing.\n        :param fipa_msg: the message\n        :param fipa_dialogue: the dialogue object\n        \"\"\"\nself.context.logger.info(\n\"received INFORM from sender={}\".format(fipa_msg.sender[-5:])\n)\nstrategy = cast(GenericStrategy, self.context.strategy)\nif strategy.is_ledger_tx and \"transaction_digest\" in fipa_msg.info.keys():\nself.context.logger.info(\n\"checking whether transaction={} has been received ...\".format(\nfipa_msg.info[\"transaction_digest\"]\n)\n)\nledger_api_dialogues = cast(\nLedgerApiDialogues, self.context.ledger_api_dialogues\n)\nledger_api_msg, ledger_api_dialogue = ledger_api_dialogues.create(\ncounterparty=LEDGER_API_ADDRESS,\nperformative=LedgerApiMessage.Performative.GET_TRANSACTION_RECEIPT,\ntransaction_digest=TransactionDigest(\nfipa_dialogue.terms.ledger_id, fipa_msg.info[\"transaction_digest\"]\n),\n)\nledger_api_dialogue = cast(LedgerApiDialogue, ledger_api_dialogue)\nledger_api_dialogue.associated_fipa_dialogue = fipa_dialogue\nself.context.outbox.put_message(message=ledger_api_msg)\nelif strategy.is_ledger_tx:\nself.context.logger.warning(\n\"did not receive transaction digest from sender={}.\".format(\nfipa_msg.sender[-5:]\n)\n)\nelif not strategy.is_ledger_tx and \"Done\" in fipa_msg.info.keys():\ninform_msg = fipa_dialogue.reply(\nperformative=FipaMessage.Performative.INFORM,\ntarget_message=fipa_msg,\ninfo=fipa_dialogue.data_for_sale,\n)\nself.context.outbox.put_message(message=inform_msg)\nfipa_dialogues = cast(FipaDialogues, self.context.fipa_dialogues)\nfipa_dialogues.dialogue_stats.add_dialogue_endstate(\nFipaDialogue.EndState.SUCCESSFUL, fipa_dialogue.is_self_initiated\n)\nself.context.logger.info(\n\"transaction confirmed, sending data={} to buyer={}.\".format(\nfipa_dialogue.data_for_sale,\nfipa_msg.sender[-5:],\n)\n)\nelse:\nself.context.logger.warning(\n\"did not receive transaction confirmation from sender={}.\".format(\nfipa_msg.sender[-5:]\n)\n)\n

In the above code, we check the INFORM message. If it contains a transaction digest, then we verify that the transaction matches the proposal the buyer accepted. If the transaction is valid and we received the funds, then we send the data to the buyer. Otherwise, we do not send the data.

The remaining handlers are as follows:

    def _handle_invalid(\nself, fipa_msg: FipaMessage, fipa_dialogue: FipaDialogue\n) -> None:\n\"\"\"\n        Handle a fipa message of invalid performative.\n        :param fipa_msg: the message\n        :param fipa_dialogue: the dialogue object\n        \"\"\"\nself.context.logger.warning(\n\"cannot handle fipa message of performative={} in dialogue={}.\".format(\nfipa_msg.performative, fipa_dialogue\n)\n)\nclass GenericLedgerApiHandler(Handler):\n\"\"\"Implement the ledger handler.\"\"\"\nSUPPORTED_PROTOCOL = LedgerApiMessage.protocol_id  # type: Optional[PublicId]\ndef setup(self) -> None:\n\"\"\"Implement the setup for the handler.\"\"\"\ndef handle(self, message: Message) -> None:\n\"\"\"\n        Implement the reaction to a message.\n        :param message: the message\n        \"\"\"\nledger_api_msg = cast(LedgerApiMessage, message)\n# recover dialogue\nledger_api_dialogues = cast(\nLedgerApiDialogues, self.context.ledger_api_dialogues\n)\nledger_api_dialogue = cast(\nOptional[LedgerApiDialogue], ledger_api_dialogues.update(ledger_api_msg)\n)\nif ledger_api_dialogue is None:\nself._handle_unidentified_dialogue(ledger_api_msg)\nreturn\n# handle message\nif ledger_api_msg.performative is LedgerApiMessage.Performative.BALANCE:\nself._handle_balance(ledger_api_msg)\nelif (\nledger_api_msg.performative\nis LedgerApiMessage.Performative.TRANSACTION_RECEIPT\n):\nself._handle_transaction_receipt(ledger_api_msg, ledger_api_dialogue)\nelif ledger_api_msg.performative == LedgerApiMessage.Performative.ERROR:\nself._handle_error(ledger_api_msg, ledger_api_dialogue)\nelse:\nself._handle_invalid(ledger_api_msg, ledger_api_dialogue)\ndef teardown(self) -> None:\n\"\"\"Implement the handler teardown.\"\"\"\ndef _handle_unidentified_dialogue(self, ledger_api_msg: LedgerApiMessage) -> None:\n\"\"\"\n        Handle an unidentified dialogue.\n        :param ledger_api_msg: the message\n        \"\"\"\nself.context.logger.info(\n\"received invalid ledger_api message={}, unidentified dialogue.\".format(\nledger_api_msg\n)\n)\ndef _handle_balance(self, ledger_api_msg: LedgerApiMessage) -> None:\n\"\"\"\n        Handle a message of balance performative.\n        :param ledger_api_msg: the ledger api message\n        \"\"\"\nself.context.logger.info(\n\"starting balance on {} ledger={}.\".format(\nledger_api_msg.ledger_id,\nledger_api_msg.balance,\n)\n)\ndef _handle_transaction_receipt(\nself, ledger_api_msg: LedgerApiMessage, ledger_api_dialogue: LedgerApiDialogue\n) -> None:\n\"\"\"\n        Handle a message of balance performative.\n        :param ledger_api_msg: the ledger api message\n        :param ledger_api_dialogue: the ledger api dialogue\n        \"\"\"\nfipa_dialogue = ledger_api_dialogue.associated_fipa_dialogue\nis_settled = LedgerApis.is_transaction_settled(\nfipa_dialogue.terms.ledger_id, ledger_api_msg.transaction_receipt.receipt\n)\nis_valid = LedgerApis.is_transaction_valid(\nfipa_dialogue.terms.ledger_id,\nledger_api_msg.transaction_receipt.transaction,\nfipa_dialogue.terms.sender_address,\nfipa_dialogue.terms.counterparty_address,\nfipa_dialogue.terms.nonce,\nfipa_dialogue.terms.counterparty_payable_amount,\n)\nif is_settled and is_valid:\nlast_message = cast(\nOptional[FipaMessage], fipa_dialogue.last_incoming_message\n)\nif last_message is None:\nraise ValueError(\"Cannot retrieve last fipa message.\")\ninform_msg = fipa_dialogue.reply(\nperformative=FipaMessage.Performative.INFORM,\ntarget_message=last_message,\ninfo=fipa_dialogue.data_for_sale,\n)\nself.context.outbox.put_message(message=inform_msg)\nfipa_dialogues = cast(FipaDialogues, self.context.fipa_dialogues)\nfipa_dialogues.dialogue_stats.add_dialogue_endstate(\nFipaDialogue.EndState.SUCCESSFUL, fipa_dialogue.is_self_initiated\n)\nself.context.logger.info(\n\"transaction confirmed, sending data={} to buyer={}.\".format(\nfipa_dialogue.data_for_sale,\nlast_message.sender[-5:],\n)\n)\nelse:\nself.context.logger.info(\n\"transaction_receipt={} not settled or not valid, aborting\".format(\nledger_api_msg.transaction_receipt\n)\n)\ndef _handle_error(\nself, ledger_api_msg: LedgerApiMessage, ledger_api_dialogue: LedgerApiDialogue\n) -> None:\n\"\"\"\n        Handle a message of error performative.\n        :param ledger_api_msg: the ledger api message\n        :param ledger_api_dialogue: the ledger api dialogue\n        \"\"\"\nself.context.logger.info(\n\"received ledger_api error message={} in dialogue={}.\".format(\nledger_api_msg, ledger_api_dialogue\n)\n)\ndef _handle_invalid(\nself, ledger_api_msg: LedgerApiMessage, ledger_api_dialogue: LedgerApiDialogue\n) -> None:\n\"\"\"\n        Handle a message of invalid performative.\n        :param ledger_api_msg: the ledger api message\n        :param ledger_api_dialogue: the ledger api dialogue\n        \"\"\"\nself.context.logger.warning(\n\"cannot handle ledger_api message of performative={} in dialogue={}.\".format(\nledger_api_msg.performative,\nledger_api_dialogue,\n)\n)\nclass GenericOefSearchHandler(Handler):\n\"\"\"This class implements an OEF search handler.\"\"\"\nSUPPORTED_PROTOCOL = OefSearchMessage.protocol_id  # type: Optional[PublicId]\ndef setup(self) -> None:\n\"\"\"Call to setup the handler.\"\"\"\ndef handle(self, message: Message) -> None:\n\"\"\"\n        Implement the reaction to a message.\n        :param message: the message\n        \"\"\"\noef_search_msg = cast(OefSearchMessage, message)\n# recover dialogue\noef_search_dialogues = cast(\nOefSearchDialogues, self.context.oef_search_dialogues\n)\noef_search_dialogue = cast(\nOptional[OefSearchDialogue], oef_search_dialogues.update(oef_search_msg)\n)\nif oef_search_dialogue is None:\nself._handle_unidentified_dialogue(oef_search_msg)\nreturn\n# handle message\nif oef_search_msg.performative == OefSearchMessage.Performative.SUCCESS:\nself._handle_success(oef_search_msg, oef_search_dialogue)\nelif oef_search_msg.performative == OefSearchMessage.Performative.OEF_ERROR:\nself._handle_error(oef_search_msg, oef_search_dialogue)\nelse:\nself._handle_invalid(oef_search_msg, oef_search_dialogue)\ndef teardown(self) -> None:\n\"\"\"Implement the handler teardown.\"\"\"\ndef _handle_unidentified_dialogue(self, oef_search_msg: OefSearchMessage) -> None:\n\"\"\"\n        Handle an unidentified dialogue.\n        :param oef_search_msg: the message\n        \"\"\"\nself.context.logger.info(\n\"received invalid oef_search message={}, unidentified dialogue.\".format(\noef_search_msg\n)\n)\ndef _handle_success(\nself,\noef_search_success_msg: OefSearchMessage,\noef_search_dialogue: OefSearchDialogue,\n) -> None:\n\"\"\"\n        Handle an oef search message.\n        :param oef_search_success_msg: the oef search message\n        :param oef_search_dialogue: the dialogue\n        \"\"\"\nself.context.logger.info(\n\"received oef_search success message={} in dialogue={}.\".format(\noef_search_success_msg, oef_search_dialogue\n)\n)\ntarget_message = cast(\nOefSearchMessage,\noef_search_dialogue.get_message_by_id(oef_search_success_msg.target),\n)\nif (\ntarget_message.performative\n== OefSearchMessage.Performative.REGISTER_SERVICE\n):\ndescription = target_message.service_description\ndata_model_name = description.data_model.name\nregistration_behaviour = cast(\nGenericServiceRegistrationBehaviour,\nself.context.behaviours.service_registration,\n)\nif \"location_agent\" in data_model_name:\nregistration_behaviour.register_service()\nelif \"set_service_key\" in data_model_name:\nregistration_behaviour.register_genus()\nelif (\n\"personality_agent\" in data_model_name\nand description.values[\"piece\"] == \"genus\"\n):\nregistration_behaviour.register_classification()\nelif (\n\"personality_agent\" in data_model_name\nand description.values[\"piece\"] == \"classification\"\n):\nself.context.logger.info(\n\"the agent, with its genus and classification, and its service are successfully registered on the SOEF.\"\n)\nelse:\nself.context.logger.warning(\nf\"received soef SUCCESS message as a reply to the following unexpected message: {target_message}\"\n)\ndef _handle_error(\nself,\noef_search_error_msg: OefSearchMessage,\noef_search_dialogue: OefSearchDialogue,\n) -> None:\n\"\"\"\n        Handle an oef search message.\n        :param oef_search_error_msg: the oef search message\n        :param oef_search_dialogue: the dialogue\n        \"\"\"\nself.context.logger.info(\n\"received oef_search error message={} in dialogue={}.\".format(\noef_search_error_msg, oef_search_dialogue\n)\n)\ntarget_message = cast(\nOefSearchMessage,\noef_search_dialogue.get_message_by_id(oef_search_error_msg.target),\n)\nif (\ntarget_message.performative\n== OefSearchMessage.Performative.REGISTER_SERVICE\n):\nregistration_behaviour = cast(\nGenericServiceRegistrationBehaviour,\nself.context.behaviours.service_registration,\n)\nregistration_behaviour.failed_registration_msg = target_message\ndef _handle_invalid(\nself, oef_search_msg: OefSearchMessage, oef_search_dialogue: OefSearchDialogue\n) -> None:\n\"\"\"\n        Handle an oef search message.\n        :param oef_search_msg: the oef search message\n        :param oef_search_dialogue: the dialogue\n        \"\"\"\nself.context.logger.warning(\n\"cannot handle oef_search message of performative={} in dialogue={}.\".format(\noef_search_msg.performative,\noef_search_dialogue,\n)\n)\n

The GenericLedgerApiHandler deals with LedgerApiMessages from the ledger connection and the GenericOefSearchHandler handles OefSearchMessages from the SOEF connection.

"},{"location":"aea-framework-documentation/generic-skills-step-by-step/#step-4-create-the-strategy","title":"Step 4: Create the Strategy","text":"

Next, we are going to create the strategy that we want our my_generic_seller AEA to follow. Rename the my_model.py file (my_generic_seller/skills/generic_seller/my_model.py) to strategy.py and replace the stub code with the following:

import uuid\nfrom typing import Any, Dict, Optional, Tuple\nfrom aea.common import Address\nfrom aea.crypto.ledger_apis import LedgerApis\nfrom aea.exceptions import enforce\nfrom aea.helpers.search.generic import (\nAGENT_LOCATION_MODEL,\nAGENT_PERSONALITY_MODEL,\nAGENT_REMOVE_SERVICE_MODEL,\nAGENT_SET_SERVICE_MODEL,\nSIMPLE_SERVICE_MODEL,\n)\nfrom aea.helpers.search.models import Description, Location, Query\nfrom aea.helpers.transaction.base import Terms\nfrom aea.skills.base import Model\nDEFAULT_IS_LEDGER_TX = True\nDEFAULT_UNIT_PRICE = 4\nDEFAULT_SERVICE_ID = \"generic_service\"\nDEFAULT_LOCATION = {\"longitude\": 0.1270, \"latitude\": 51.5194}\nDEFAULT_SERVICE_DATA = {\"key\": \"seller_service\", \"value\": \"generic_service\"}\nDEFAULT_PERSONALITY_DATA = {\"piece\": \"genus\", \"value\": \"data\"}\nDEFAULT_CLASSIFICATION = {\"piece\": \"classification\", \"value\": \"seller\"}\nDEFAULT_HAS_DATA_SOURCE = False\nDEFAULT_DATA_FOR_SALE = {\n\"some_generic_data_key\": \"some_generic_data_value\"\n}  # type: Optional[Dict[str, Any]]\nclass GenericStrategy(Model):\n\"\"\"This class defines a strategy for the agent.\"\"\"\ndef __init__(self, **kwargs: Any) -> None:\n\"\"\"\n        Initialize the strategy of the agent.\n        :param kwargs: keyword arguments\n        \"\"\"\nledger_id = kwargs.pop(\"ledger_id\", None)\ncurrency_id = kwargs.pop(\"currency_id\", None)\nself._is_ledger_tx = kwargs.pop(\"is_ledger_tx\", DEFAULT_IS_LEDGER_TX)\nself._unit_price = kwargs.pop(\"unit_price\", DEFAULT_UNIT_PRICE)\nself._service_id = kwargs.pop(\"service_id\", DEFAULT_SERVICE_ID)\nlocation = kwargs.pop(\"location\", DEFAULT_LOCATION)\nself._agent_location = {\n\"location\": Location(\nlatitude=location[\"latitude\"], longitude=location[\"longitude\"]\n)\n}\nself._set_personality_data = kwargs.pop(\n\"personality_data\", DEFAULT_PERSONALITY_DATA\n)\nenforce(\nlen(self._set_personality_data) == 2\nand \"piece\" in self._set_personality_data\nand \"value\" in self._set_personality_data,\n\"personality_data must contain keys `key` and `value`\",\n)\nself._set_classification = kwargs.pop(\"classification\", DEFAULT_CLASSIFICATION)\nenforce(\nlen(self._set_classification) == 2\nand \"piece\" in self._set_classification\nand \"value\" in self._set_classification,\n\"classification must contain keys `key` and `value`\",\n)\nself._set_service_data = kwargs.pop(\"service_data\", DEFAULT_SERVICE_DATA)\nenforce(\nlen(self._set_service_data) == 2\nand \"key\" in self._set_service_data\nand \"value\" in self._set_service_data,\n\"service_data must contain keys `key` and `value`\",\n)\nself._remove_service_data = {\"key\": self._set_service_data[\"key\"]}\nself._simple_service_data = {\nself._set_service_data[\"key\"]: self._set_service_data[\"value\"]\n}\nself._has_data_source = kwargs.pop(\"has_data_source\", DEFAULT_HAS_DATA_SOURCE)\ndata_for_sale_ordered = kwargs.pop(\"data_for_sale\", DEFAULT_DATA_FOR_SALE)\ndata_for_sale = {\nstr(key): str(value) for key, value in data_for_sale_ordered.items()\n}\nsuper().__init__(**kwargs)\nself._ledger_id = (\nledger_id if ledger_id is not None else self.context.default_ledger_id\n)\nif currency_id is None:\ncurrency_id = self.context.currency_denominations.get(self._ledger_id, None)\nenforce(\ncurrency_id is not None,\nf\"Currency denomination for ledger_id={self._ledger_id} not specified.\",\n)\nself._currency_id = currency_id\nenforce(\nself.context.agent_addresses.get(self._ledger_id, None) is not None,\n\"Wallet does not contain cryptos for provided ledger id.\",\n)\nself._data_for_sale = data_for_sale\n

In the above code snippet, we initialise the strategy class by trying to read the variables specific to the strategy from a YAML configuration file. If any variable is not provided, some default values will be used.

The following properties and methods deal with different aspects of the strategy. They should be relatively self-descriptive. Add them under the initialization of the strategy class:

    @property\ndef data_for_sale(self) -> Dict[str, str]:\n\"\"\"Get the data for sale.\"\"\"\nif self._has_data_source:\nreturn self.collect_from_data_source()  # pragma: nocover\nreturn self._data_for_sale\n@property\ndef ledger_id(self) -> str:\n\"\"\"Get the ledger id.\"\"\"\nreturn self._ledger_id\n@property\ndef is_ledger_tx(self) -> bool:\n\"\"\"Check whether or not tx are settled on a ledger.\"\"\"\nreturn self._is_ledger_tx\ndef get_location_description(self) -> Description:\n\"\"\"\n        Get the location description.\n        :return: a description of the agent's location\n        \"\"\"\ndescription = Description(\nself._agent_location,\ndata_model=AGENT_LOCATION_MODEL,\n)\nreturn description\ndef get_register_service_description(self) -> Description:\n\"\"\"\n        Get the register service description.\n        :return: a description of the offered services\n        \"\"\"\ndescription = Description(\nself._set_service_data,\ndata_model=AGENT_SET_SERVICE_MODEL,\n)\nreturn description\ndef get_register_personality_description(self) -> Description:\n\"\"\"\n        Get the register personality description.\n        :return: a description of the personality\n        \"\"\"\ndescription = Description(\nself._set_personality_data,\ndata_model=AGENT_PERSONALITY_MODEL,\n)\nreturn description\ndef get_register_classification_description(self) -> Description:\n\"\"\"\n        Get the register classification description.\n        :return: a description of the classification\n        \"\"\"\ndescription = Description(\nself._set_classification,\ndata_model=AGENT_PERSONALITY_MODEL,\n)\nreturn description\ndef get_service_description(self) -> Description:\n\"\"\"\n        Get the simple service description.\n        :return: a description of the offered services\n        \"\"\"\ndescription = Description(\nself._simple_service_data,\ndata_model=SIMPLE_SERVICE_MODEL,\n)\nreturn description\ndef get_unregister_service_description(self) -> Description:\n\"\"\"\n        Get the unregister service description.\n        :return: a description of the to be removed service\n        \"\"\"\ndescription = Description(\nself._remove_service_data,\ndata_model=AGENT_REMOVE_SERVICE_MODEL,\n)\nreturn description\ndef is_matching_supply(self, query: Query) -> bool:\n\"\"\"\n        Check if the query matches the supply.\n        :param query: the query\n        :return: bool indicating whether matches or not\n        \"\"\"\nreturn query.check(self.get_service_description())\ndef generate_proposal_terms_and_data(  # pylint: disable=unused-argument\nself, query: Query, counterparty_address: Address\n) -> Tuple[Description, Terms, Dict[str, str]]:\n\"\"\"\n        Generate a proposal matching the query.\n        :param query: the query\n        :param counterparty_address: the counterparty of the proposal.\n        :return: a tuple of proposal, terms and the weather data\n        \"\"\"\ndata_for_sale = self.data_for_sale\nsale_quantity = len(data_for_sale)\nseller_address = self.context.agent_addresses[self.ledger_id]\ntotal_price = sale_quantity * self._unit_price\nif self.is_ledger_tx:\ntx_nonce = LedgerApis.generate_tx_nonce(\nidentifier=self.ledger_id,\nseller=seller_address,\nclient=counterparty_address,\n)\nelse:\ntx_nonce = uuid.uuid4().hex  # pragma: nocover\nproposal = Description(\n{\n\"ledger_id\": self.ledger_id,\n\"price\": total_price,\n\"currency_id\": self._currency_id,\n\"service_id\": self._service_id,\n\"quantity\": sale_quantity,\n\"tx_nonce\": tx_nonce,\n}\n)\nterms = Terms(\nledger_id=self.ledger_id,\nsender_address=seller_address,\ncounterparty_address=counterparty_address,\namount_by_currency_id={self._currency_id: total_price},\nquantities_by_good_id={self._service_id: -sale_quantity},\nis_sender_payable_tx_fee=False,\nnonce=tx_nonce,\nfee_by_currency_id={self._currency_id: 0},\n)\nreturn proposal, terms, data_for_sale\ndef collect_from_data_source(self) -> Dict[str, str]:\n\"\"\"Implement the logic to communicate with the sensor.\"\"\"\nraise NotImplementedError\n

The helper private function collect_from_data_source is where we read data from a sensor or if there are no sensor we use some default data provided (see the data_for_sale property).

"},{"location":"aea-framework-documentation/generic-skills-step-by-step/#step-5-create-the-dialogues","title":"Step 5: Create the Dialogues","text":"

To keep track of the structure and progress of interactions, including negotiations with a buyer AEA and interactions with search nodes and ledgers, we use dialogues. Create a new file in the skill folder (my_generic_seller/skills/generic_seller/) and name it dialogues.py. Inside this file add the following code:

from typing import Any, Dict, Optional, Type\nfrom aea.common import Address\nfrom aea.exceptions import AEAEnforceError, enforce\nfrom aea.helpers.transaction.base import Terms\nfrom aea.protocols.base import Message\nfrom aea.protocols.dialogue.base import Dialogue as BaseDialogue\nfrom aea.protocols.dialogue.base import DialogueLabel as BaseDialogueLabel\nfrom aea.skills.base import Model\nfrom packages.fetchai.protocols.default.dialogues import (\nDefaultDialogue as BaseDefaultDialogue,\n)\nfrom packages.fetchai.protocols.default.dialogues import (\nDefaultDialogues as BaseDefaultDialogues,\n)\nfrom packages.fetchai.protocols.fipa.dialogues import FipaDialogue as BaseFipaDialogue\nfrom packages.fetchai.protocols.fipa.dialogues import FipaDialogues as BaseFipaDialogues\nfrom packages.fetchai.protocols.fipa.message import FipaMessage\nfrom packages.fetchai.protocols.ledger_api.dialogues import (\nLedgerApiDialogue as BaseLedgerApiDialogue,\n)\nfrom packages.fetchai.protocols.ledger_api.dialogues import (\nLedgerApiDialogues as BaseLedgerApiDialogues,\n)\nfrom packages.fetchai.protocols.ledger_api.message import LedgerApiMessage\nfrom packages.fetchai.protocols.oef_search.dialogues import (\nOefSearchDialogue as BaseOefSearchDialogue,\n)\nfrom packages.fetchai.protocols.oef_search.dialogues import (\nOefSearchDialogues as BaseOefSearchDialogues,\n)\nDefaultDialogue = BaseDefaultDialogue\nclass DefaultDialogues(Model, BaseDefaultDialogues):\n\"\"\"The dialogues class keeps track of all dialogues.\"\"\"\ndef __init__(self, **kwargs: Any) -> None:\n\"\"\"\n        Initialize dialogues.\n        :param kwargs: keyword arguments\n        \"\"\"\nModel.__init__(self, **kwargs)\ndef role_from_first_message(  # pylint: disable=unused-argument\nmessage: Message, receiver_address: Address\n) -> BaseDialogue.Role:\n\"\"\"Infer the role of the agent from an incoming/outgoing first message\n            :param message: an incoming/outgoing first message\n            :param receiver_address: the address of the receiving agent\n            :return: The role of the agent\n            \"\"\"\nreturn DefaultDialogue.Role.AGENT\nBaseDefaultDialogues.__init__(\nself,\nself_address=self.context.agent_address,\nrole_from_first_message=role_from_first_message,\n)\nclass FipaDialogue(BaseFipaDialogue):\n\"\"\"The dialogue class maintains state of a dialogue and manages it.\"\"\"\n__slots__ = (\"data_for_sale\", \"_terms\")\ndef __init__(\nself,\ndialogue_label: BaseDialogueLabel,\nself_address: Address,\nrole: BaseDialogue.Role,\nmessage_class: Type[FipaMessage] = FipaMessage,\n) -> None:\n\"\"\"\n        Initialize a dialogue.\n        :param dialogue_label: the identifier of the dialogue\n        :param self_address: the address of the entity for whom this dialogue is maintained\n        :param role: the role of the agent this dialogue is maintained for\n        :param message_class: the message class\n        \"\"\"\nBaseFipaDialogue.__init__(\nself,\ndialogue_label=dialogue_label,\nself_address=self_address,\nrole=role,\nmessage_class=message_class,\n)\nself.data_for_sale = None  # type: Optional[Dict[str, str]]\nself._terms = None  # type: Optional[Terms]\n@property\ndef terms(self) -> Terms:\n\"\"\"Get terms.\"\"\"\nif self._terms is None:\nraise AEAEnforceError(\"Terms not set!\")\nreturn self._terms\n@terms.setter\ndef terms(self, terms: Terms) -> None:\n\"\"\"Set terms.\"\"\"\nenforce(self._terms is None, \"Terms already set!\")\nself._terms = terms\nclass FipaDialogues(Model, BaseFipaDialogues):\n\"\"\"The dialogues class keeps track of all dialogues.\"\"\"\ndef __init__(self, **kwargs: Any) -> None:\n\"\"\"\n        Initialize dialogues.\n        :param kwargs: keyword arguments\n        \"\"\"\nModel.__init__(self, **kwargs)\ndef role_from_first_message(  # pylint: disable=unused-argument\nmessage: Message, receiver_address: Address\n) -> BaseDialogue.Role:\n\"\"\"Infer the role of the agent from an incoming/outgoing first message\n            :param message: an incoming/outgoing first message\n            :param receiver_address: the address of the receiving agent\n            :return: The role of the agent\n            \"\"\"\nreturn FipaDialogue.Role.SELLER\nBaseFipaDialogues.__init__(\nself,\nself_address=self.context.agent_address,\nrole_from_first_message=role_from_first_message,\ndialogue_class=FipaDialogue,\n)\nclass LedgerApiDialogue(BaseLedgerApiDialogue):\n\"\"\"The dialogue class maintains state of a dialogue and manages it.\"\"\"\n__slots__ = (\"_associated_fipa_dialogue\",)\ndef __init__(\nself,\ndialogue_label: BaseDialogueLabel,\nself_address: Address,\nrole: BaseDialogue.Role,\nmessage_class: Type[LedgerApiMessage] = LedgerApiMessage,\n) -> None:\n\"\"\"\n        Initialize a dialogue.\n        :param dialogue_label: the identifier of the dialogue\n        :param self_address: the address of the entity for whom this dialogue is maintained\n        :param role: the role of the agent this dialogue is maintained for\n        :param message_class: the message class\n        \"\"\"\nBaseLedgerApiDialogue.__init__(\nself,\ndialogue_label=dialogue_label,\nself_address=self_address,\nrole=role,\nmessage_class=message_class,\n)\nself._associated_fipa_dialogue = None  # type: Optional[FipaDialogue]\n@property\ndef associated_fipa_dialogue(self) -> FipaDialogue:\n\"\"\"Get associated_fipa_dialogue.\"\"\"\nif self._associated_fipa_dialogue is None:\nraise AEAEnforceError(\"FipaDialogue not set!\")\nreturn self._associated_fipa_dialogue\n@associated_fipa_dialogue.setter\ndef associated_fipa_dialogue(self, fipa_dialogue: FipaDialogue) -> None:\n\"\"\"Set associated_fipa_dialogue\"\"\"\nenforce(self._associated_fipa_dialogue is None, \"FipaDialogue already set!\")\nself._associated_fipa_dialogue = fipa_dialogue\nclass LedgerApiDialogues(Model, BaseLedgerApiDialogues):\n\"\"\"The dialogues class keeps track of all dialogues.\"\"\"\ndef __init__(self, **kwargs: Any) -> None:\n\"\"\"\n        Initialize dialogues.\n        :param kwargs: keyword arguments\n        \"\"\"\nModel.__init__(self, **kwargs)\ndef role_from_first_message(  # pylint: disable=unused-argument\nmessage: Message, receiver_address: Address\n) -> BaseDialogue.Role:\n\"\"\"Infer the role of the agent from an incoming/outgoing first message\n            :param message: an incoming/outgoing first message\n            :param receiver_address: the address of the receiving agent\n            :return: The role of the agent\n            \"\"\"\nreturn BaseLedgerApiDialogue.Role.AGENT\nBaseLedgerApiDialogues.__init__(\nself,\nself_address=str(self.skill_id),\nrole_from_first_message=role_from_first_message,\ndialogue_class=LedgerApiDialogue,\n)\nOefSearchDialogue = BaseOefSearchDialogue\nclass OefSearchDialogues(Model, BaseOefSearchDialogues):\n\"\"\"This class keeps track of all oef_search dialogues.\"\"\"\ndef __init__(self, **kwargs: Any) -> None:\n\"\"\"\n        Initialize dialogues.\n        :param kwargs: keyword arguments\n        \"\"\"\nModel.__init__(self, **kwargs)\ndef role_from_first_message(  # pylint: disable=unused-argument\nmessage: Message, receiver_address: Address\n) -> BaseDialogue.Role:\n\"\"\"Infer the role of the agent from an incoming/outgoing first message\n            :param message: an incoming/outgoing first message\n            :param receiver_address: the address of the receiving agent\n            :return: The role of the agent\n            \"\"\"\nreturn BaseOefSearchDialogue.Role.AGENT\nBaseOefSearchDialogues.__init__(\nself,\nself_address=str(self.skill_id),\nrole_from_first_message=role_from_first_message,\n)\n

The FipaDialogues class contains negotiation dialogues with each my_generic_buyer AEA (and other AEAs) and exposes a number of helpful methods to manage them. This helps us match messages to the dialogues they belong to, access previous messages and enable us to identify possible communications problems between the my_generic_seller AEA and the my_generic_buyer AEA. It also keeps track of the data that we offer for sale during the proposal phase.

The FipaDialogues class extends BaseFipaDialogues, which itself derives from the base Dialogues class. Similarly, the FipaDialogue class extends BaseFipaDialogue which itself derives from the base Dialogue class. To learn more about dialogues have a look here.

"},{"location":"aea-framework-documentation/generic-skills-step-by-step/#step-6-update-the-yaml-files","title":"Step 6: Update the YAML Files","text":"

Since we made so many changes to our AEA we have to update the skill.yaml (at my_generic_seller/skills/generic_seller/skill.yaml). Make sure you update your skill.yaml with the following configuration:

name: generic_seller\nauthor: fetchai\nversion: 0.1.0\ntype: skill\ndescription: The weather station skill implements the functionality to sell weather\ndata.\nlicense: Apache-2.0\naea_version: '>=1.0.0, <2.0.0'\nfingerprint:\nREADME.md: QmPb5kHYZyhUN87EKmuahyGqDGgqVdGPyfC1KpGC3xfmcP\n__init__.py: QmTSEedzQySy2nzRCY3F66CBSX52f8s3pWHZTejX4hKC9h\nbehaviours.py: QmS9sPCv2yBnhWsmHeaCptpApMtYZipbR39TXixeGK64Ks\ndialogues.py: QmdTW8q1xQ7ajFVsWmuV62ypoT5J2b6Hkyz52LFaWuMEtd\nhandlers.py: QmQnQhSaHPUYaJut8bMe2LHEqiZqokMSVfCthVaqrvPbdi\nstrategy.py: QmYTUsfv64eRQDevCfMUDQPx2GCtiMLFdacN4sS1E4Fdfx\nfingerprint_ignore_patterns: []\nconnections:\n- fetchai/ledger:0.21.5\ncontracts: []\nprotocols:\n- fetchai/default:1.1.7\n- fetchai/fipa:1.1.7\n- fetchai/ledger_api:1.1.7\n- fetchai/oef_search:1.1.7\nskills: []\nbehaviours:\nservice_registration:\nargs:\nservices_interval: 20\nclass_name: GenericServiceRegistrationBehaviour\nhandlers:\nfipa:\nargs: {}\nclass_name: GenericFipaHandler\nledger_api:\nargs: {}\nclass_name: GenericLedgerApiHandler\noef_search:\nargs: {}\nclass_name: GenericOefSearchHandler\nmodels:\ndefault_dialogues:\nargs: {}\nclass_name: DefaultDialogues\nfipa_dialogues:\nargs: {}\nclass_name: FipaDialogues\nledger_api_dialogues:\nargs: {}\nclass_name: LedgerApiDialogues\noef_search_dialogues:\nargs: {}\nclass_name: OefSearchDialogues\nstrategy:\nargs:\ndata_for_sale:\ngeneric: data\nhas_data_source: false\nis_ledger_tx: true\nlocation:\nlatitude: 51.5194\nlongitude: 0.127\nservice_data:\nkey: seller_service\nvalue: generic_service\nservice_id: generic_service\nunit_price: 10\nclass_name: GenericStrategy\nis_abstract: false\ndependencies: {}\n

We must pay attention to the models and in particular the strategy\u2019s variables. Here we can change the price we would like to sell each data reading for, or the currency we would like to transact with. Lastly, the dependencies are the third party packages we need to install in order to get readings from the sensor.

Finally, we fingerprint our new skill:

aea fingerprint skill fetchai/generic_seller:0.1.0\n

This will hash each file and save the hash in the fingerprint. This way, in the future we can easily track if any of the files have changed.

"},{"location":"aea-framework-documentation/generic-skills-step-by-step/#generic-buyer-aea","title":"Generic Buyer AEA","text":""},{"location":"aea-framework-documentation/generic-skills-step-by-step/#step-1-create-the-aea_1","title":"Step 1: Create the AEA","text":"

In a new terminal, create a new AEA by typing the following command in the terminal:

aea create my_generic_buyer\ncd my_generic_buyer\naea install\n

Our newly created AEA is inside the current working directory. Let\u2019s create a new skill for purchasing data. Type the following command:

aea scaffold skill generic_buyer\n

This command creates the correct structure for a new skill inside our AEA project. You can locate the newly created skill under the skills folder (my_generic_buyer/skills/generic_buyer/) and it must contain the following files:

  • __init__.py
  • behaviours.py
  • handlers.py
  • my_model.py
  • skills.yaml
"},{"location":"aea-framework-documentation/generic-skills-step-by-step/#step-2-create-the-behaviour_1","title":"Step 2: Create the Behaviour","text":"

Open the behaviours.py file (my_generic_buyer/skills/generic_buyer/behaviours.py) and replace the stub code with the following:

from typing import Any, List, Optional, Set, cast\nfrom aea.protocols.dialogue.base import DialogueLabel\nfrom aea.skills.behaviours import TickerBehaviour\nfrom packages.fetchai.connections.ledger.base import (\nCONNECTION_ID as LEDGER_CONNECTION_PUBLIC_ID,\n)\nfrom packages.fetchai.protocols.ledger_api.message import LedgerApiMessage\nfrom packages.fetchai.protocols.oef_search.message import OefSearchMessage\nfrom packages.fetchai.skills.generic_buyer.dialogues import (\nFipaDialogue,\nLedgerApiDialogue,\nLedgerApiDialogues,\nOefSearchDialogues,\n)\nfrom packages.fetchai.skills.generic_buyer.strategy import GenericStrategy\nDEFAULT_MAX_PROCESSING = 120\nDEFAULT_TX_INTERVAL = 2.0\nDEFAULT_SEARCH_INTERVAL = 5.0\nLEDGER_API_ADDRESS = str(LEDGER_CONNECTION_PUBLIC_ID)\nclass GenericSearchBehaviour(TickerBehaviour):\n\"\"\"This class implements a search behaviour.\"\"\"\ndef __init__(self, **kwargs: Any):\n\"\"\"Initialize the search behaviour.\"\"\"\nsearch_interval = cast(\nfloat, kwargs.pop(\"search_interval\", DEFAULT_SEARCH_INTERVAL)\n)\nsuper().__init__(tick_interval=search_interval, **kwargs)\ndef setup(self) -> None:\n\"\"\"Implement the setup for the behaviour.\"\"\"\nstrategy = cast(GenericStrategy, self.context.strategy)\nif strategy.is_ledger_tx:\nledger_api_dialogues = cast(\nLedgerApiDialogues, self.context.ledger_api_dialogues\n)\nledger_api_msg, _ = ledger_api_dialogues.create(\ncounterparty=LEDGER_API_ADDRESS,\nperformative=LedgerApiMessage.Performative.GET_BALANCE,\nledger_id=strategy.ledger_id,\naddress=cast(str, self.context.agent_addresses.get(strategy.ledger_id)),\n)\nself.context.outbox.put_message(message=ledger_api_msg)\nelse:\nstrategy.is_searching = True\ndef act(self) -> None:\n\"\"\"Implement the act.\"\"\"\nstrategy = cast(GenericStrategy, self.context.strategy)\nif not strategy.is_searching:\nreturn\ntransaction_behaviour = cast(\nGenericTransactionBehaviour, self.context.behaviours.transaction\n)\nremaining_transactions_count = len(transaction_behaviour.waiting)\nif remaining_transactions_count > 0:\nself.context.logger.info(\nf\"Transaction behaviour has {remaining_transactions_count} transactions remaining. Skipping search!\"\n)\nreturn\nstrategy.update_search_query_params()\nquery = strategy.get_location_and_service_query()\noef_search_dialogues = cast(\nOefSearchDialogues, self.context.oef_search_dialogues\n)\noef_search_msg, _ = oef_search_dialogues.create(\ncounterparty=self.context.search_service_address,\nperformative=OefSearchMessage.Performative.SEARCH_SERVICES,\nquery=query,\n)\nself.context.outbox.put_message(message=oef_search_msg)\ndef teardown(self) -> None:\n\"\"\"Implement the task teardown.\"\"\"\nclass GenericTransactionBehaviour(TickerBehaviour):\n\"\"\"A behaviour to sequentially submit transactions to the blockchain.\"\"\"\ndef __init__(self, **kwargs: Any):\n\"\"\"Initialize the transaction behaviour.\"\"\"\ntx_interval = cast(\nfloat, kwargs.pop(\"transaction_interval\", DEFAULT_TX_INTERVAL)\n)\nself.max_processing = cast(\nfloat, kwargs.pop(\"max_processing\", DEFAULT_MAX_PROCESSING)\n)\nself.processing_time = 0.0\nself.waiting: List[FipaDialogue] = []\nself.processing: Optional[LedgerApiDialogue] = None\nself.timedout: Set[DialogueLabel] = set()\nsuper().__init__(tick_interval=tx_interval, **kwargs)\ndef setup(self) -> None:\n\"\"\"Setup behaviour.\"\"\"\ndef act(self) -> None:\n\"\"\"Implement the act.\"\"\"\nif self.processing is not None:\nif self.processing_time <= self.max_processing:\n# already processing\nself.processing_time += self.tick_interval\nreturn\nself._timeout_processing()\nif len(self.waiting) == 0:\n# nothing to process\nreturn\nself._start_processing()\ndef _start_processing(self) -> None:\n\"\"\"Process the next transaction.\"\"\"\nfipa_dialogue = self.waiting.pop(0)\nself.context.logger.info(\nf\"Processing transaction, {len(self.waiting)} transactions remaining\"\n)\nledger_api_dialogues = cast(\nLedgerApiDialogues, self.context.ledger_api_dialogues\n)\nledger_api_msg, ledger_api_dialogue = ledger_api_dialogues.create(\ncounterparty=LEDGER_API_ADDRESS,\nperformative=LedgerApiMessage.Performative.GET_RAW_TRANSACTION,\nterms=fipa_dialogue.terms,\n)\nledger_api_dialogue = cast(LedgerApiDialogue, ledger_api_dialogue)\nledger_api_dialogue.associated_fipa_dialogue = fipa_dialogue\nself.processing_time = 0.0\nself.processing = ledger_api_dialogue\nself.context.logger.info(\nf\"requesting transfer transaction from ledger api for message={ledger_api_msg}...\"\n)\nself.context.outbox.put_message(message=ledger_api_msg)\ndef teardown(self) -> None:\n\"\"\"Teardown behaviour.\"\"\"\ndef _timeout_processing(self) -> None:\n\"\"\"Timeout processing.\"\"\"\nif self.processing is None:\nreturn\nself.timedout.add(self.processing.dialogue_label)\nself.waiting.append(self.processing.associated_fipa_dialogue)\nself.processing_time = 0.0\nself.processing = None\ndef finish_processing(self, ledger_api_dialogue: LedgerApiDialogue) -> None:\n\"\"\"\n        Finish processing.\n        :param ledger_api_dialogue: the ledger api dialogue\n        \"\"\"\nif self.processing == ledger_api_dialogue:\nself.processing_time = 0.0\nself.processing = None\nreturn\nif ledger_api_dialogue.dialogue_label not in self.timedout:\nraise ValueError(\nf\"Non-matching dialogues in transaction behaviour: {self.processing} and {ledger_api_dialogue}\"\n)\nself.timedout.remove(ledger_api_dialogue.dialogue_label)\nself.context.logger.debug(\nf\"Timeout dialogue in transaction processing: {ledger_api_dialogue}\"\n)\n# don't reset, as another might be processing\ndef failed_processing(self, ledger_api_dialogue: LedgerApiDialogue) -> None:\n\"\"\"\n        Failed processing.\n        Currently, we retry processing indefinitely.\n        :param ledger_api_dialogue: the ledger api dialogue\n        \"\"\"\nself.finish_processing(ledger_api_dialogue)\nself.waiting.append(ledger_api_dialogue.associated_fipa_dialogue)\n

This TickerBehaviour will send a search query to the SOEF search node at regular tick intervals.

"},{"location":"aea-framework-documentation/generic-skills-step-by-step/#step-3-create-the-handler_1","title":"Step 3: Create the Handler","text":"

So far, the AEA is tasked with sending search queries to the SOEF search node. However, currently the AEA has no way of handling the responses it receives from the SOEF or messages from other agents.

Let us now implement Handlers to deal with the expected incoming messages. Open the handlers.py file (my_generic_buyer/skills/generic_buyer/handlers.py) and add the following code (replacing the stub code already present in the file):

import pprint\nfrom typing import Optional, cast\nfrom aea.configurations.base import PublicId\nfrom aea.crypto.ledger_apis import LedgerApis\nfrom aea.protocols.base import Message\nfrom aea.skills.base import Handler\nfrom packages.fetchai.connections.ledger.base import (\nCONNECTION_ID as LEDGER_CONNECTION_PUBLIC_ID,\n)\nfrom packages.fetchai.protocols.default.message import DefaultMessage\nfrom packages.fetchai.protocols.fipa.message import FipaMessage\nfrom packages.fetchai.protocols.ledger_api.message import LedgerApiMessage\nfrom packages.fetchai.protocols.oef_search.message import OefSearchMessage\nfrom packages.fetchai.protocols.signing.message import SigningMessage\nfrom packages.fetchai.skills.generic_buyer.behaviours import GenericTransactionBehaviour\nfrom packages.fetchai.skills.generic_buyer.dialogues import (\nDefaultDialogues,\nFipaDialogue,\nFipaDialogues,\nLedgerApiDialogue,\nLedgerApiDialogues,\nOefSearchDialogue,\nOefSearchDialogues,\nSigningDialogue,\nSigningDialogues,\n)\nfrom packages.fetchai.skills.generic_buyer.strategy import GenericStrategy\nLEDGER_API_ADDRESS = str(LEDGER_CONNECTION_PUBLIC_ID)\nclass GenericFipaHandler(Handler):\n\"\"\"This class implements a FIPA handler.\"\"\"\nSUPPORTED_PROTOCOL = FipaMessage.protocol_id  # type: Optional[PublicId]\ndef setup(self) -> None:\n\"\"\"Implement the setup.\"\"\"\ndef handle(self, message: Message) -> None:\n\"\"\"\n        Implement the reaction to a message.\n        :param message: the message\n        \"\"\"\nfipa_msg = cast(FipaMessage, message)\n# recover dialogue\nfipa_dialogues = cast(FipaDialogues, self.context.fipa_dialogues)\nfipa_dialogue = cast(FipaDialogue, fipa_dialogues.update(fipa_msg))\nif fipa_dialogue is None:\nself._handle_unidentified_dialogue(fipa_msg)\nreturn\n# handle message\nif fipa_msg.performative == FipaMessage.Performative.PROPOSE:\nself._handle_propose(fipa_msg, fipa_dialogue)\nelif fipa_msg.performative == FipaMessage.Performative.DECLINE:\nself._handle_decline(fipa_msg, fipa_dialogue, fipa_dialogues)\nelif fipa_msg.performative == FipaMessage.Performative.MATCH_ACCEPT_W_INFORM:\nself._handle_match_accept(fipa_msg, fipa_dialogue)\nelif fipa_msg.performative == FipaMessage.Performative.INFORM:\nself._handle_inform(fipa_msg, fipa_dialogue, fipa_dialogues)\nelse:\nself._handle_invalid(fipa_msg, fipa_dialogue)\ndef teardown(self) -> None:\n\"\"\"Implement the handler teardown.\"\"\"\n

You will see that we are following similar logic to the generic_seller when we develop the generic_buyer\u2019s side of the negotiation. First, we create a new dialogue and store it in the dialogues class. Then we are checking what kind of message we received by checking its performative. So lets start creating our handlers:

    def _handle_unidentified_dialogue(self, fipa_msg: FipaMessage) -> None:\n\"\"\"\n        Handle an unidentified dialogue.\n        :param fipa_msg: the message\n        \"\"\"\nself.context.logger.info(\n\"received invalid fipa message={}, unidentified dialogue.\".format(fipa_msg)\n)\ndefault_dialogues = cast(DefaultDialogues, self.context.default_dialogues)\ndefault_msg, _ = default_dialogues.create(\ncounterparty=fipa_msg.sender,\nperformative=DefaultMessage.Performative.ERROR,\nerror_code=DefaultMessage.ErrorCode.INVALID_DIALOGUE,\nerror_msg=\"Invalid dialogue.\",\nerror_data={\"fipa_message\": fipa_msg.encode()},\n)\nself.context.outbox.put_message(message=default_msg)\n

The above code handles messages referencing unidentified dialogues and responds with an error message to the sender. Next we will handle the PROPOSE message received from the my_generic_seller AEA:

    def _handle_propose(\nself, fipa_msg: FipaMessage, fipa_dialogue: FipaDialogue\n) -> None:\n\"\"\"\n        Handle the propose.\n        :param fipa_msg: the message\n        :param fipa_dialogue: the dialogue object\n        \"\"\"\nself.context.logger.info(\n\"received proposal={} from sender={}\".format(\nfipa_msg.proposal.values,\nfipa_msg.sender[-5:],\n)\n)\nstrategy = cast(GenericStrategy, self.context.strategy)\nacceptable = strategy.is_acceptable_proposal(fipa_msg.proposal)\naffordable = strategy.is_affordable_proposal(fipa_msg.proposal)\nif acceptable and affordable:\nself.context.logger.info(\n\"accepting the proposal from sender={}\".format(fipa_msg.sender[-5:])\n)\nterms = strategy.terms_from_proposal(fipa_msg.proposal, fipa_msg.sender)\nfipa_dialogue.terms = terms\naccept_msg = fipa_dialogue.reply(\nperformative=FipaMessage.Performative.ACCEPT,\ntarget_message=fipa_msg,\n)\nself.context.outbox.put_message(message=accept_msg)\nelse:\nself.context.logger.info(\n\"declining the proposal from sender={}\".format(fipa_msg.sender[-5:])\n)\ndecline_msg = fipa_dialogue.reply(\nperformative=FipaMessage.Performative.DECLINE,\ntarget_message=fipa_msg,\n)\nself.context.outbox.put_message(message=decline_msg)\n

When we receive a proposal, we have to check if we have the funds to complete the transaction and if the proposal is acceptable based on our strategy. If the proposal is not affordable or acceptable, we respond with a DECLINE message. Otherwise, we send an ACCEPT message to the seller.

The next code-block handles the DECLINE message that we may receive from the seller as a response to our CFP or ACCEPT messages:

    def _handle_decline(\nself,\nfipa_msg: FipaMessage,\nfipa_dialogue: FipaDialogue,\nfipa_dialogues: FipaDialogues,\n) -> None:\n\"\"\"\n        Handle the decline.\n        :param fipa_msg: the message\n        :param fipa_dialogue: the fipa dialogue\n        :param fipa_dialogues: the fipa dialogues\n        \"\"\"\nself.context.logger.info(\n\"received DECLINE from sender={}\".format(fipa_msg.sender[-5:])\n)\ntarget_message = fipa_dialogue.get_message_by_id(fipa_msg.target)\nif not target_message:\nraise ValueError(\"Can not find target message!\")  # pragma: nocover\ndeclined_performative = target_message.performative\nif declined_performative == FipaMessage.Performative.CFP:\nfipa_dialogues.dialogue_stats.add_dialogue_endstate(\nFipaDialogue.EndState.DECLINED_CFP, fipa_dialogue.is_self_initiated\n)\nif declined_performative == FipaMessage.Performative.ACCEPT:\nfipa_dialogues.dialogue_stats.add_dialogue_endstate(\nFipaDialogue.EndState.DECLINED_ACCEPT, fipa_dialogue.is_self_initiated\n)\n

The above code terminates each dialogue with the specific AEA and stores the state of the terminated dialogue (whether it was terminated after a CFP or an ACCEPT).

If my_generic_seller AEA wants to move on with the sale, it will send a MATCH_ACCEPT message. In order to handle this we add the following code:

def _handle_match_accept(\nself, fipa_msg: FipaMessage, fipa_dialogue: FipaDialogue\n) -> None:\n\"\"\"\n        Handle the match accept.\n        :param fipa_msg: the message\n        :param fipa_dialogue: the dialogue object\n        \"\"\"\nself.context.logger.info(\n\"received MATCH_ACCEPT_W_INFORM from sender={} with info={}\".format(\nfipa_msg.sender[-5:], fipa_msg.info\n)\n)\nstrategy = cast(GenericStrategy, self.context.strategy)\nif strategy.is_ledger_tx:\ntransfer_address = fipa_msg.info.get(\"address\", None)\nif transfer_address is not None and isinstance(transfer_address, str):\nfipa_dialogue.terms.counterparty_address = (  # pragma: nocover\ntransfer_address\n)\ntx_behaviour = cast(\nGenericTransactionBehaviour, self.context.behaviours.transaction\n)\ntx_behaviour.waiting.append(fipa_dialogue)\nelse:\ninform_msg = fipa_dialogue.reply(\nperformative=FipaMessage.Performative.INFORM,\ntarget_message=fipa_msg,\ninfo={\"Done\": \"Sending payment via bank transfer\"},\n)\nself.context.outbox.put_message(message=inform_msg)\nself.context.logger.info(\n\"informing counterparty={} of payment.\".format(fipa_msg.sender[-5:])\n)\n

The first thing we are checking is if we enabled our AEA to transact with a ledger. If so, we add this negotiation to the queue of transactions to be processed. If not, we simulate non-ledger payment by sending an inform to the seller that the payment is done (say via bank transfer).

Lastly, we need to handle INFORM messages. This is the message that will have our data:

    def _handle_inform(\nself,\nfipa_msg: FipaMessage,\nfipa_dialogue: FipaDialogue,\nfipa_dialogues: FipaDialogues,\n) -> None:\n\"\"\"\n        Handle the match inform.\n        :param fipa_msg: the message\n        :param fipa_dialogue: the fipa dialogue\n        :param fipa_dialogues: the fipa dialogues\n        \"\"\"\nself.context.logger.info(\n\"received INFORM from sender={}\".format(fipa_msg.sender[-5:])\n)\nif len(fipa_msg.info.keys()) >= 1:\ndata = fipa_msg.info\ndata_string = pprint.pformat(data)[:1000]\nself.context.logger.info(f\"received the following data={data_string}\")\nfipa_dialogues.dialogue_stats.add_dialogue_endstate(\nFipaDialogue.EndState.SUCCESSFUL, fipa_dialogue.is_self_initiated\n)\nstrategy = cast(GenericStrategy, self.context.strategy)\nstrategy.successful_trade_with_counterparty(fipa_msg.sender, data)\nelse:\nself.context.logger.info(\n\"received no data from sender={}\".format(fipa_msg.sender[-5:])\n)\ndef _handle_invalid(\nself, fipa_msg: FipaMessage, fipa_dialogue: FipaDialogue\n) -> None:\n\"\"\"\n        Handle a fipa message of invalid performative.\n        :param fipa_msg: the message\n        :param fipa_dialogue: the fipa dialogue\n        \"\"\"\nself.context.logger.warning(\n\"cannot handle fipa message of performative={} in dialogue={}.\".format(\nfipa_msg.performative, fipa_dialogue\n)\n)\n

We now need to add handlers for messages received from the DecisionMaker and the SOEF search node. We need one handler for each type of protocol we use.

To handle the messages in the oef_search protocol used by the SOEF search node we add the following code in the same file (my_generic_buyer/skills/generic_buyer/handlers.py):

class GenericOefSearchHandler(Handler):\n\"\"\"This class implements an OEF search handler.\"\"\"\nSUPPORTED_PROTOCOL = OefSearchMessage.protocol_id  # type: Optional[PublicId]\ndef setup(self) -> None:\n\"\"\"Call to setup the handler.\"\"\"\ndef handle(self, message: Message) -> None:\n\"\"\"\n        Implement the reaction to a message.\n        :param message: the message\n        \"\"\"\noef_search_msg = cast(OefSearchMessage, message)\n# recover dialogue\noef_search_dialogues = cast(\nOefSearchDialogues, self.context.oef_search_dialogues\n)\noef_search_dialogue = cast(\nOptional[OefSearchDialogue], oef_search_dialogues.update(oef_search_msg)\n)\nif oef_search_dialogue is None:\nself._handle_unidentified_dialogue(oef_search_msg)\nreturn\n# handle message\nif oef_search_msg.performative is OefSearchMessage.Performative.OEF_ERROR:\nself._handle_error(oef_search_msg, oef_search_dialogue)\nelif oef_search_msg.performative is OefSearchMessage.Performative.SEARCH_RESULT:\nself._handle_search(oef_search_msg, oef_search_dialogue)\nelse:\nself._handle_invalid(oef_search_msg, oef_search_dialogue)\ndef teardown(self) -> None:\n\"\"\"Implement the handler teardown.\"\"\"\ndef _handle_unidentified_dialogue(self, oef_search_msg: OefSearchMessage) -> None:\n\"\"\"\n        Handle an unidentified dialogue.\n        :param oef_search_msg: the message\n        \"\"\"\nself.context.logger.info(\n\"received invalid oef_search message={}, unidentified dialogue.\".format(\noef_search_msg\n)\n)\ndef _handle_error(\nself, oef_search_msg: OefSearchMessage, oef_search_dialogue: OefSearchDialogue\n) -> None:\n\"\"\"\n        Handle an oef search message.\n        :param oef_search_msg: the oef search message\n        :param oef_search_dialogue: the dialogue\n        \"\"\"\nself.context.logger.info(\n\"received oef_search error message={} in dialogue={}.\".format(\noef_search_msg, oef_search_dialogue\n)\n)\ndef _handle_search(\nself, oef_search_msg: OefSearchMessage, oef_search_dialogue: OefSearchDialogue\n) -> None:\n\"\"\"\n        Handle the search response.\n        :param oef_search_msg: the oef search message\n        :param oef_search_dialogue: the dialogue\n        \"\"\"\nif len(oef_search_msg.agents) == 0:\nself.context.logger.info(\nf\"found no agents in dialogue={oef_search_dialogue}, continue searching.\"\n)\nreturn\nstrategy = cast(GenericStrategy, self.context.strategy)\nif strategy.is_stop_searching_on_result:\nself.context.logger.info(\n\"found agents={}, stopping search.\".format(\nlist(map(lambda x: x[-5:], oef_search_msg.agents)),\n)\n)\nstrategy.is_searching = False  # stopping search\nelse:\nself.context.logger.info(\n\"found agents={}.\".format(\nlist(map(lambda x: x[-5:], oef_search_msg.agents)),\n)\n)\nquery = strategy.get_service_query()\nfipa_dialogues = cast(FipaDialogues, self.context.fipa_dialogues)\ncounterparties = strategy.get_acceptable_counterparties(oef_search_msg.agents)\nfor counterparty in counterparties:\ncfp_msg, _ = fipa_dialogues.create(\ncounterparty=counterparty,\nperformative=FipaMessage.Performative.CFP,\nquery=query,\n)\nself.context.outbox.put_message(message=cfp_msg)\nself.context.logger.info(\n\"sending CFP to agent={}\".format(counterparty[-5:])\n)\ndef _handle_invalid(\nself, oef_search_msg: OefSearchMessage, oef_search_dialogue: OefSearchDialogue\n) -> None:\n\"\"\"\n        Handle an oef search message.\n        :param oef_search_msg: the oef search message\n        :param oef_search_dialogue: the dialogue\n        \"\"\"\nself.context.logger.warning(\n\"cannot handle oef_search message of performative={} in dialogue={}.\".format(\noef_search_msg.performative,\noef_search_dialogue,\n)\n)\n

When we receive a message from the SOEF search node of a type OefSearchMessage.Performative.SEARCH_RESULT, we are passing the details to the relevant handler method. In the _handle_search function, we are checking that the response contains some agents, and we stop the search if it does. We pick our first agent and send a CFP message.

The last handlers we need are the GenericSigningHandler and the GenericLedgerApiHandler. These handlers are responsible for SigningMessages that we receive from the DecisionMaker, and LedgerApiMessages that we receive from the ledger connection, respectively.

class GenericSigningHandler(Handler):\n\"\"\"Implement the signing handler.\"\"\"\nSUPPORTED_PROTOCOL = SigningMessage.protocol_id  # type: Optional[PublicId]\ndef setup(self) -> None:\n\"\"\"Implement the setup for the handler.\"\"\"\ndef handle(self, message: Message) -> None:\n\"\"\"\n        Implement the reaction to a message.\n        :param message: the message\n        \"\"\"\nsigning_msg = cast(SigningMessage, message)\n# recover dialogue\nsigning_dialogues = cast(SigningDialogues, self.context.signing_dialogues)\nsigning_dialogue = cast(\nOptional[SigningDialogue], signing_dialogues.update(signing_msg)\n)\nif signing_dialogue is None:\nself._handle_unidentified_dialogue(signing_msg)\nreturn\n# handle message\nif signing_msg.performative is SigningMessage.Performative.SIGNED_TRANSACTION:\nself._handle_signed_transaction(signing_msg, signing_dialogue)\nelif signing_msg.performative is SigningMessage.Performative.ERROR:\nself._handle_error(signing_msg, signing_dialogue)\nelse:\nself._handle_invalid(signing_msg, signing_dialogue)\ndef teardown(self) -> None:\n\"\"\"Implement the handler teardown.\"\"\"\ndef _handle_unidentified_dialogue(self, signing_msg: SigningMessage) -> None:\n\"\"\"\n        Handle an unidentified dialogue.\n        :param signing_msg: the message\n        \"\"\"\nself.context.logger.info(\n\"received invalid signing message={}, unidentified dialogue.\".format(\nsigning_msg\n)\n)\ndef _handle_signed_transaction(\nself, signing_msg: SigningMessage, signing_dialogue: SigningDialogue\n) -> None:\n\"\"\"\n        Handle an oef search message.\n        :param signing_msg: the signing message\n        :param signing_dialogue: the dialogue\n        \"\"\"\nself.context.logger.info(\"transaction signing was successful.\")\nledger_api_dialogue = signing_dialogue.associated_ledger_api_dialogue\nlast_ledger_api_msg = ledger_api_dialogue.last_incoming_message\nif last_ledger_api_msg is None:\nraise ValueError(\"Could not retrieve last message in ledger api dialogue\")\nledger_api_msg = ledger_api_dialogue.reply(\nperformative=LedgerApiMessage.Performative.SEND_SIGNED_TRANSACTION,\ntarget_message=last_ledger_api_msg,\nsigned_transaction=signing_msg.signed_transaction,\n)\nself.context.outbox.put_message(message=ledger_api_msg)\nself.context.logger.info(\"sending transaction to ledger.\")\ndef _handle_error(\nself, signing_msg: SigningMessage, signing_dialogue: SigningDialogue\n) -> None:\n\"\"\"\n        Handle an oef search message.\n        :param signing_msg: the signing message\n        :param signing_dialogue: the dialogue\n        \"\"\"\nself.context.logger.info(\n\"transaction signing was not successful. Error_code={} in dialogue={}\".format(\nsigning_msg.error_code, signing_dialogue\n)\n)\nsigning_msg_ = cast(\nOptional[SigningMessage], signing_dialogue.last_outgoing_message\n)\nif (\nsigning_msg_ is not None\nand signing_msg_.performative\n== SigningMessage.Performative.SIGN_TRANSACTION\n):\ntx_behaviour = cast(\nGenericTransactionBehaviour, self.context.behaviours.transaction\n)\nledger_api_dialogue = signing_dialogue.associated_ledger_api_dialogue\ntx_behaviour.failed_processing(ledger_api_dialogue)\ndef _handle_invalid(\nself, signing_msg: SigningMessage, signing_dialogue: SigningDialogue\n) -> None:\n\"\"\"\n        Handle an oef search message.\n        :param signing_msg: the signing message\n        :param signing_dialogue: the dialogue\n        \"\"\"\nself.context.logger.warning(\n\"cannot handle signing message of performative={} in dialogue={}.\".format(\nsigning_msg.performative, signing_dialogue\n)\n)\nclass GenericLedgerApiHandler(Handler):\n\"\"\"Implement the ledger handler.\"\"\"\nSUPPORTED_PROTOCOL = LedgerApiMessage.protocol_id  # type: Optional[PublicId]\ndef setup(self) -> None:\n\"\"\"Implement the setup for the handler.\"\"\"\ndef handle(self, message: Message) -> None:\n\"\"\"\n        Implement the reaction to a message.\n        :param message: the message\n        \"\"\"\nledger_api_msg = cast(LedgerApiMessage, message)\n# recover dialogue\nledger_api_dialogues = cast(\nLedgerApiDialogues, self.context.ledger_api_dialogues\n)\nledger_api_dialogue = cast(\nOptional[LedgerApiDialogue], ledger_api_dialogues.update(ledger_api_msg)\n)\nif ledger_api_dialogue is None:\nself._handle_unidentified_dialogue(ledger_api_msg)\nreturn\n# handle message\nif ledger_api_msg.performative is LedgerApiMessage.Performative.BALANCE:\nself._handle_balance(ledger_api_msg)\nelif (\nledger_api_msg.performative is LedgerApiMessage.Performative.RAW_TRANSACTION\n):\nself._handle_raw_transaction(ledger_api_msg, ledger_api_dialogue)\nelif (\nledger_api_msg.performative\n== LedgerApiMessage.Performative.TRANSACTION_DIGEST\n):\nself._handle_transaction_digest(ledger_api_msg, ledger_api_dialogue)\nelif (\nledger_api_msg.performative\n== LedgerApiMessage.Performative.TRANSACTION_RECEIPT\n):\nself._handle_transaction_receipt(ledger_api_msg, ledger_api_dialogue)\nelif ledger_api_msg.performative == LedgerApiMessage.Performative.ERROR:\nself._handle_error(ledger_api_msg, ledger_api_dialogue)\nelse:\nself._handle_invalid(ledger_api_msg, ledger_api_dialogue)\ndef teardown(self) -> None:\n\"\"\"Implement the handler teardown.\"\"\"\ndef _handle_unidentified_dialogue(self, ledger_api_msg: LedgerApiMessage) -> None:\n\"\"\"\n        Handle an unidentified dialogue.\n        :param ledger_api_msg: the message\n        \"\"\"\nself.context.logger.info(\n\"received invalid ledger_api message={}, unidentified dialogue.\".format(\nledger_api_msg\n)\n)\ndef _handle_balance(self, ledger_api_msg: LedgerApiMessage) -> None:\n\"\"\"\n        Handle a message of balance performative.\n        :param ledger_api_msg: the ledger api message\n        \"\"\"\nstrategy = cast(GenericStrategy, self.context.strategy)\nif ledger_api_msg.balance > 0:\nself.context.logger.info(\n\"starting balance on {} ledger={}.\".format(\nstrategy.ledger_id,\nledger_api_msg.balance,\n)\n)\nstrategy.balance = ledger_api_msg.balance\nstrategy.is_searching = True\nelse:\nself.context.logger.warning(\nf\"you have no starting balance on {strategy.ledger_id} ledger! Stopping skill {self.skill_id}.\"\n)\nself.context.is_active = False\ndef _handle_raw_transaction(\nself, ledger_api_msg: LedgerApiMessage, ledger_api_dialogue: LedgerApiDialogue\n) -> None:\n\"\"\"\n        Handle a message of raw_transaction performative.\n        :param ledger_api_msg: the ledger api message\n        :param ledger_api_dialogue: the ledger api dialogue\n        \"\"\"\nself.context.logger.info(\"received raw transaction={}\".format(ledger_api_msg))\nsigning_dialogues = cast(SigningDialogues, self.context.signing_dialogues)\nsigning_msg, signing_dialogue = signing_dialogues.create(\ncounterparty=self.context.decision_maker_address,\nperformative=SigningMessage.Performative.SIGN_TRANSACTION,\nraw_transaction=ledger_api_msg.raw_transaction,\nterms=ledger_api_dialogue.associated_fipa_dialogue.terms,\n)\nsigning_dialogue = cast(SigningDialogue, signing_dialogue)\nsigning_dialogue.associated_ledger_api_dialogue = ledger_api_dialogue\nself.context.decision_maker_message_queue.put_nowait(signing_msg)\nself.context.logger.info(\n\"proposing the transaction to the decision maker. Waiting for confirmation ...\"\n)\ndef _handle_transaction_digest(\nself, ledger_api_msg: LedgerApiMessage, ledger_api_dialogue: LedgerApiDialogue\n) -> None:\n\"\"\"\n        Handle a message of transaction_digest performative.\n        :param ledger_api_msg: the ledger api message\n        :param ledger_api_dialogue: the ledger api dialogue\n        \"\"\"\nself.context.logger.info(\n\"transaction was successfully submitted. Transaction digest={}\".format(\nledger_api_msg.transaction_digest\n)\n)\nledger_api_msg_ = ledger_api_dialogue.reply(\nperformative=LedgerApiMessage.Performative.GET_TRANSACTION_RECEIPT,\ntarget_message=ledger_api_msg,\ntransaction_digest=ledger_api_msg.transaction_digest,\n)\nself.context.logger.info(\"checking transaction is settled.\")\nself.context.outbox.put_message(message=ledger_api_msg_)\ndef _handle_transaction_receipt(\nself, ledger_api_msg: LedgerApiMessage, ledger_api_dialogue: LedgerApiDialogue\n) -> None:\n\"\"\"\n        Handle a message of balance performative.\n        :param ledger_api_msg: the ledger api message\n        :param ledger_api_dialogue: the ledger api dialogue\n        \"\"\"\nfipa_dialogue = ledger_api_dialogue.associated_fipa_dialogue\nis_settled = LedgerApis.is_transaction_settled(\nfipa_dialogue.terms.ledger_id, ledger_api_msg.transaction_receipt.receipt\n)\ntx_behaviour = cast(\nGenericTransactionBehaviour, self.context.behaviours.transaction\n)\nif is_settled:\ntx_behaviour.finish_processing(ledger_api_dialogue)\nledger_api_msg_ = cast(\nOptional[LedgerApiMessage], ledger_api_dialogue.last_outgoing_message\n)\nif ledger_api_msg_ is None:\nraise ValueError(  # pragma: nocover\n\"Could not retrieve last ledger_api message\"\n)\nfipa_msg = cast(Optional[FipaMessage], fipa_dialogue.last_incoming_message)\nif fipa_msg is None:\nraise ValueError(\"Could not retrieve last fipa message\")\ninform_msg = fipa_dialogue.reply(\nperformative=FipaMessage.Performative.INFORM,\ntarget_message=fipa_msg,\ninfo={\"transaction_digest\": ledger_api_msg_.transaction_digest.body},\n)\nself.context.outbox.put_message(message=inform_msg)\nself.context.logger.info(\n\"transaction confirmed, informing counterparty={} of transaction digest.\".format(\nfipa_dialogue.dialogue_label.dialogue_opponent_addr[-5:],\n)\n)\nelse:\ntx_behaviour.failed_processing(ledger_api_dialogue)\nself.context.logger.info(\n\"transaction_receipt={} not settled or not valid, aborting\".format(\nledger_api_msg.transaction_receipt\n)\n)\ndef _handle_error(\nself, ledger_api_msg: LedgerApiMessage, ledger_api_dialogue: LedgerApiDialogue\n) -> None:\n\"\"\"\n        Handle a message of error performative.\n        :param ledger_api_msg: the ledger api message\n        :param ledger_api_dialogue: the ledger api dialogue\n        \"\"\"\nself.context.logger.info(\n\"received ledger_api error message={} in dialogue={}.\".format(\nledger_api_msg, ledger_api_dialogue\n)\n)\nledger_api_msg_ = cast(\nOptional[LedgerApiMessage], ledger_api_dialogue.last_outgoing_message\n)\nif (\nledger_api_msg_ is not None\nand ledger_api_msg_.performative\n!= LedgerApiMessage.Performative.GET_BALANCE\n):\ntx_behaviour = cast(\nGenericTransactionBehaviour, self.context.behaviours.transaction\n)\ntx_behaviour.failed_processing(ledger_api_dialogue)\ndef _handle_invalid(\nself, ledger_api_msg: LedgerApiMessage, ledger_api_dialogue: LedgerApiDialogue\n) -> None:\n\"\"\"\n        Handle a message of invalid performative.\n        :param ledger_api_msg: the ledger api message\n        :param ledger_api_dialogue: the ledger api dialogue\n        \"\"\"\nself.context.logger.warning(\n\"cannot handle ledger_api message of performative={} in dialogue={}.\".format(\nledger_api_msg.performative,\nledger_api_dialogue,\n)\n)\n
"},{"location":"aea-framework-documentation/generic-skills-step-by-step/#step-4-create-the-strategy_1","title":"Step 4: Create the Strategy","text":"

We are going to create the strategy that we want our AEA to follow. Rename the my_model.py file (in my_generic_buyer/skills/generic_buyer/) to strategy.py and replace the stub code with the following:

from typing import Any, Dict, List, Tuple\nfrom aea.common import Address\nfrom aea.exceptions import enforce\nfrom aea.helpers.search.generic import SIMPLE_SERVICE_MODEL\nfrom aea.helpers.search.models import (\nConstraint,\nConstraintType,\nDescription,\nLocation,\nQuery,\n)\nfrom aea.helpers.transaction.base import Terms\nfrom aea.skills.base import Model\nDEFAULT_IS_LEDGER_TX = True\nDEFAULT_MAX_UNIT_PRICE = 5\nDEFAULT_MAX_TX_FEE = 2\nDEFAULT_SERVICE_ID = \"generic_service\"\nDEFAULT_MIN_QUANTITY = 1\nDEFAULT_MAX_QUANTITY = 100\nDEFAULT_LOCATION = {\"longitude\": 0.1270, \"latitude\": 51.5194}\nDEFAULT_SEARCH_QUERY = {\n\"search_key\": \"seller_service\",\n\"search_value\": \"generic_service\",\n\"constraint_type\": \"==\",\n}\nDEFAULT_SEARCH_RADIUS = 5.0\nDEFAULT_MAX_NEGOTIATIONS = 2\nclass GenericStrategy(Model):\n\"\"\"This class defines a strategy for the agent.\"\"\"\ndef __init__(self, **kwargs: Any) -> None:\n\"\"\"\n        Initialize the strategy of the agent.\n        :param kwargs: keyword arguments\n        \"\"\"\nledger_id = kwargs.pop(\"ledger_id\", None)\ncurrency_id = kwargs.pop(\"currency_id\", None)\nself._is_ledger_tx = kwargs.pop(\"is_ledger_tx\", DEFAULT_IS_LEDGER_TX)\nself._max_unit_price = kwargs.pop(\"max_unit_price\", DEFAULT_MAX_UNIT_PRICE)\nself._min_quantity = kwargs.pop(\"min_quantity\", DEFAULT_MIN_QUANTITY)\nself._max_quantity = kwargs.pop(\"max_quantity\", DEFAULT_MAX_QUANTITY)\nself._max_tx_fee = kwargs.pop(\"max_tx_fee\", DEFAULT_MAX_TX_FEE)\nself._service_id = kwargs.pop(\"service_id\", DEFAULT_SERVICE_ID)\nself._search_query = kwargs.pop(\"search_query\", DEFAULT_SEARCH_QUERY)\nlocation = kwargs.pop(\"location\", DEFAULT_LOCATION)\nself._agent_location = Location(\nlatitude=location[\"latitude\"], longitude=location[\"longitude\"]\n)\nself._radius = kwargs.pop(\"search_radius\", DEFAULT_SEARCH_RADIUS)\nself._max_negotiations = kwargs.pop(\n\"max_negotiations\", DEFAULT_MAX_NEGOTIATIONS\n)\nself._is_stop_searching_on_result = kwargs.pop(\"stop_searching_on_result\", True)\nsuper().__init__(**kwargs)\nself._ledger_id = (\nledger_id if ledger_id is not None else self.context.default_ledger_id\n)\nif currency_id is None:\ncurrency_id = self.context.currency_denominations.get(self._ledger_id, None)\nenforce(\ncurrency_id is not None,\nf\"Currency denomination for ledger_id={self._ledger_id} not specified.\",\n)\nself._currency_id = currency_id\nself._is_searching = False\nself._balance = 0\n

Similar to the seller AEA, we initialize the strategy class by trying to read the strategy variables from the YAML file, and if not possible, use some default values. In the following snippet, the two methods after the properties are related to the OEF search service. Add this snippet under the initialization of the strategy class:

    @property\ndef ledger_id(self) -> str:\n\"\"\"Get the ledger id.\"\"\"\nreturn self._ledger_id\n@property\ndef is_ledger_tx(self) -> bool:\n\"\"\"Check whether or not tx are settled on a ledger.\"\"\"\nreturn self._is_ledger_tx\n@property\ndef is_stop_searching_on_result(self) -> bool:\n\"\"\"Check if search is stopped on result.\"\"\"\nreturn self._is_stop_searching_on_result\n@property\ndef is_searching(self) -> bool:\n\"\"\"Check if the agent is searching.\"\"\"\nreturn self._is_searching\n@is_searching.setter\ndef is_searching(self, is_searching: bool) -> None:\n\"\"\"Check if the agent is searching.\"\"\"\nenforce(isinstance(is_searching, bool), \"Can only set bool on is_searching!\")\nself._is_searching = is_searching\n@property\ndef balance(self) -> int:\n\"\"\"Get the balance.\"\"\"\nreturn self._balance\n@balance.setter\ndef balance(self, balance: int) -> None:\n\"\"\"Set the balance.\"\"\"\nself._balance = balance\n@property\ndef max_negotiations(self) -> int:\n\"\"\"Get the maximum number of negotiations the agent can start.\"\"\"\nreturn self._max_negotiations\ndef get_location_and_service_query(self) -> Query:\n\"\"\"\n        Get the location and service query of the agent.\n        :return: the query\n        \"\"\"\nclose_to_my_service = Constraint(\n\"location\", ConstraintType(\"distance\", (self._agent_location, self._radius))\n)\nservice_key_filter = Constraint(\nself._search_query[\"search_key\"],\nConstraintType(\nself._search_query[\"constraint_type\"],\nself._search_query[\"search_value\"],\n),\n)\nquery = Query(\n[close_to_my_service, service_key_filter],\n)\nreturn query\ndef get_service_query(self) -> Query:\n\"\"\"\n        Get the service query of the agent.\n        :return: the query\n        \"\"\"\nservice_key_filter = Constraint(\nself._search_query[\"search_key\"],\nConstraintType(\nself._search_query[\"constraint_type\"],\nself._search_query[\"search_value\"],\n),\n)\nquery = Query([service_key_filter], model=SIMPLE_SERVICE_MODEL)\nreturn query\n

The following code block checks if the proposal that we received is acceptable according to a strategy:

    def is_acceptable_proposal(self, proposal: Description) -> bool:\n\"\"\"\n        Check whether it is an acceptable proposal.\n        :param proposal: a description\n        :return: whether it is acceptable\n        \"\"\"\nresult = (\nall(\nkey in proposal.values\nfor key in [\n\"ledger_id\",\n\"currency_id\",\n\"price\",\n\"service_id\",\n\"quantity\",\n\"tx_nonce\",\n]\n)\nand proposal.values[\"ledger_id\"] == self.ledger_id\nand proposal.values[\"price\"] > 0\nand proposal.values[\"quantity\"] >= self._min_quantity\nand proposal.values[\"quantity\"] <= self._max_quantity\nand proposal.values[\"price\"]\n<= proposal.values[\"quantity\"] * self._max_unit_price\nand proposal.values[\"currency_id\"] == self._currency_id\nand proposal.values[\"service_id\"] == self._service_id\nand isinstance(proposal.values[\"tx_nonce\"], str)\nand proposal.values[\"tx_nonce\"] != \"\"\n)\nreturn result\n

The is_affordable_proposal method in the following code block checks if we can afford the transaction based on the funds we have in our wallet on the ledger. The rest of the methods are self-explanatory.

    def is_affordable_proposal(self, proposal: Description) -> bool:\n\"\"\"\n        Check whether it is an affordable proposal.\n        :param proposal: a description\n        :return: whether it is affordable\n        \"\"\"\nif self.is_ledger_tx:\npayable = proposal.values.get(\"price\", 0) + self._max_tx_fee\nresult = self.balance >= payable\nelse:\nresult = True\nreturn result\ndef get_acceptable_counterparties(\nself, counterparties: Tuple[str, ...]\n) -> Tuple[str, ...]:\n\"\"\"\n        Process counterparties and drop unacceptable ones.\n        :param counterparties: a tuple of counterparties\n        :return: list of counterparties\n        \"\"\"\nvalid_counterparties: List[str] = []\nfor idx, counterparty in enumerate(counterparties):\nif idx < self.max_negotiations:\nvalid_counterparties.append(counterparty)\nreturn tuple(valid_counterparties)\ndef terms_from_proposal(\nself, proposal: Description, counterparty_address: Address\n) -> Terms:\n\"\"\"\n        Get the terms from a proposal.\n        :param proposal: the proposal\n        :param counterparty_address: the counterparty\n        :return: terms\n        \"\"\"\nbuyer_address = self.context.agent_addresses[proposal.values[\"ledger_id\"]]\nterms = Terms(\nledger_id=proposal.values[\"ledger_id\"],\nsender_address=buyer_address,\ncounterparty_address=counterparty_address,\namount_by_currency_id={\nproposal.values[\"currency_id\"]: -proposal.values[\"price\"]\n},\nquantities_by_good_id={\nproposal.values[\"service_id\"]: proposal.values[\"quantity\"]\n},\nis_sender_payable_tx_fee=True,\nnonce=proposal.values[\"tx_nonce\"],\nfee_by_currency_id={proposal.values[\"currency_id\"]: self._max_tx_fee},\n)\nreturn terms\ndef successful_trade_with_counterparty(\nself, counterparty: str, data: Dict[str, str]\n) -> None:\n\"\"\"\n        Do something on successful trade.\n        :param counterparty: the counterparty address\n        :param data: the data\n        \"\"\"\ndef update_search_query_params(self) -> None:\n\"\"\"Update agent location and query for search.\"\"\"\n
"},{"location":"aea-framework-documentation/generic-skills-step-by-step/#step-5-create-the-dialogues_1","title":"Step 5: Create the Dialogues","text":"

As mentioned during the creation of the seller AEA, we should keep track of the various interactions an AEA has with others and this is done via dialogues. Create a new file and name it dialogues.py (in my_generic_buyer/skills/generic_buyer/). Inside this file add the following code:

from typing import Any, Optional, Type\nfrom aea.common import Address\nfrom aea.exceptions import AEAEnforceError, enforce\nfrom aea.helpers.transaction.base import Terms\nfrom aea.protocols.base import Message\nfrom aea.protocols.dialogue.base import Dialogue as BaseDialogue\nfrom aea.protocols.dialogue.base import DialogueLabel as BaseDialogueLabel\nfrom aea.skills.base import Model\nfrom packages.fetchai.protocols.default.dialogues import (\nDefaultDialogue as BaseDefaultDialogue,\n)\nfrom packages.fetchai.protocols.default.dialogues import (\nDefaultDialogues as BaseDefaultDialogues,\n)\nfrom packages.fetchai.protocols.fipa.dialogues import FipaDialogue as BaseFipaDialogue\nfrom packages.fetchai.protocols.fipa.dialogues import FipaDialogues as BaseFipaDialogues\nfrom packages.fetchai.protocols.fipa.message import FipaMessage\nfrom packages.fetchai.protocols.ledger_api.dialogues import (\nLedgerApiDialogue as BaseLedgerApiDialogue,\n)\nfrom packages.fetchai.protocols.ledger_api.dialogues import (\nLedgerApiDialogues as BaseLedgerApiDialogues,\n)\nfrom packages.fetchai.protocols.ledger_api.message import LedgerApiMessage\nfrom packages.fetchai.protocols.oef_search.dialogues import (\nOefSearchDialogue as BaseOefSearchDialogue,\n)\nfrom packages.fetchai.protocols.oef_search.dialogues import (\nOefSearchDialogues as BaseOefSearchDialogues,\n)\nfrom packages.fetchai.protocols.signing.dialogues import (\nSigningDialogue as BaseSigningDialogue,\n)\nfrom packages.fetchai.protocols.signing.dialogues import (\nSigningDialogues as BaseSigningDialogues,\n)\nfrom packages.fetchai.protocols.signing.message import SigningMessage\nDefaultDialogue = BaseDefaultDialogue\nclass DefaultDialogues(Model, BaseDefaultDialogues):\n\"\"\"The dialogues class keeps track of all dialogues.\"\"\"\ndef __init__(self, **kwargs: Any) -> None:\n\"\"\"\n        Initialize dialogues.\n        :param kwargs: keyword arguments\n        \"\"\"\nModel.__init__(self, **kwargs)\ndef role_from_first_message(  # pylint: disable=unused-argument\nmessage: Message, receiver_address: Address\n) -> BaseDialogue.Role:\n\"\"\"Infer the role of the agent from an incoming/outgoing first message\n            :param message: an incoming/outgoing first message\n            :param receiver_address: the address of the receiving agent\n            :return: The role of the agent\n            \"\"\"\nreturn DefaultDialogue.Role.AGENT\nBaseDefaultDialogues.__init__(\nself,\nself_address=self.context.agent_address,\nrole_from_first_message=role_from_first_message,\n)\nclass FipaDialogue(BaseFipaDialogue):\n\"\"\"The dialogue class maintains state of a dialogue and manages it.\"\"\"\n__slots__ = (\n\"_terms\",\n\"_associated_ledger_api_dialogue\",\n)\ndef __init__(\nself,\ndialogue_label: BaseDialogueLabel,\nself_address: Address,\nrole: BaseDialogue.Role,\nmessage_class: Type[FipaMessage] = FipaMessage,\n) -> None:\n\"\"\"\n        Initialize a dialogue.\n        :param dialogue_label: the identifier of the dialogue\n        :param self_address: the address of the entity for whom this dialogue is maintained\n        :param role: the role of the agent this dialogue is maintained for\n        :param message_class: the message class\n        \"\"\"\nBaseFipaDialogue.__init__(\nself,\ndialogue_label=dialogue_label,\nself_address=self_address,\nrole=role,\nmessage_class=message_class,\n)\nself._terms = None  # type: Optional[Terms]\n@property\ndef terms(self) -> Terms:\n\"\"\"Get terms.\"\"\"\nif self._terms is None:\nraise AEAEnforceError(\"Terms not set!\")\nreturn self._terms\n@terms.setter\ndef terms(self, terms: Terms) -> None:\n\"\"\"Set terms.\"\"\"\nenforce(self._terms is None, \"Terms already set!\")\nself._terms = terms\nclass FipaDialogues(Model, BaseFipaDialogues):\n\"\"\"The dialogues class keeps track of all dialogues.\"\"\"\ndef __init__(self, **kwargs: Any) -> None:\n\"\"\"\n        Initialize dialogues.\n        :param kwargs: keyword arguments\n        \"\"\"\nModel.__init__(self, **kwargs)\ndef role_from_first_message(  # pylint: disable=unused-argument\nmessage: Message, receiver_address: Address\n) -> BaseDialogue.Role:\n\"\"\"Infer the role of the agent from an incoming/outgoing first message\n            :param message: an incoming/outgoing first message\n            :param receiver_address: the address of the receiving agent\n            :return: The role of the agent\n            \"\"\"\nreturn BaseFipaDialogue.Role.BUYER\nBaseFipaDialogues.__init__(\nself,\nself_address=self.context.agent_address,\nrole_from_first_message=role_from_first_message,\ndialogue_class=FipaDialogue,\n)\nclass LedgerApiDialogue(BaseLedgerApiDialogue):\n\"\"\"The dialogue class maintains state of a dialogue and manages it.\"\"\"\n__slots__ = (\"_associated_fipa_dialogue\",)\ndef __init__(\nself,\ndialogue_label: BaseDialogueLabel,\nself_address: Address,\nrole: BaseDialogue.Role,\nmessage_class: Type[LedgerApiMessage] = LedgerApiMessage,\n) -> None:\n\"\"\"\n        Initialize a dialogue.\n        :param dialogue_label: the identifier of the dialogue\n        :param self_address: the address of the entity for whom this dialogue is maintained\n        :param role: the role of the agent this dialogue is maintained for\n        :param message_class: the message class\n        \"\"\"\nBaseLedgerApiDialogue.__init__(\nself,\ndialogue_label=dialogue_label,\nself_address=self_address,\nrole=role,\nmessage_class=message_class,\n)\nself._associated_fipa_dialogue = None  # type: Optional[FipaDialogue]\n@property\ndef associated_fipa_dialogue(self) -> FipaDialogue:\n\"\"\"Get associated_fipa_dialogue.\"\"\"\nif self._associated_fipa_dialogue is None:\nraise AEAEnforceError(\"FipaDialogue not set!\")\nreturn self._associated_fipa_dialogue\n@associated_fipa_dialogue.setter\ndef associated_fipa_dialogue(self, fipa_dialogue: FipaDialogue) -> None:\n\"\"\"Set associated_fipa_dialogue\"\"\"\nenforce(self._associated_fipa_dialogue is None, \"FipaDialogue already set!\")\nself._associated_fipa_dialogue = fipa_dialogue\nclass LedgerApiDialogues(Model, BaseLedgerApiDialogues):\n\"\"\"The dialogues class keeps track of all dialogues.\"\"\"\ndef __init__(self, **kwargs: Any) -> None:\n\"\"\"\n        Initialize dialogues.\n        :param kwargs: keyword arguments\n        \"\"\"\nModel.__init__(self, **kwargs)\ndef role_from_first_message(  # pylint: disable=unused-argument\nmessage: Message, receiver_address: Address\n) -> BaseDialogue.Role:\n\"\"\"Infer the role of the agent from an incoming/outgoing first message\n            :param message: an incoming/outgoing first message\n            :param receiver_address: the address of the receiving agent\n            :return: The role of the agent\n            \"\"\"\nreturn BaseLedgerApiDialogue.Role.AGENT\nBaseLedgerApiDialogues.__init__(\nself,\nself_address=str(self.skill_id),\nrole_from_first_message=role_from_first_message,\ndialogue_class=LedgerApiDialogue,\n)\nOefSearchDialogue = BaseOefSearchDialogue\nclass OefSearchDialogues(Model, BaseOefSearchDialogues):\n\"\"\"This class keeps track of all oef_search dialogues.\"\"\"\ndef __init__(self, **kwargs: Any) -> None:\n\"\"\"\n        Initialize dialogues.\n        :param kwargs: keyword arguments\n        \"\"\"\nModel.__init__(self, **kwargs)\ndef role_from_first_message(  # pylint: disable=unused-argument\nmessage: Message, receiver_address: Address\n) -> BaseDialogue.Role:\n\"\"\"Infer the role of the agent from an incoming/outgoing first message\n            :param message: an incoming/outgoing first message\n            :param receiver_address: the address of the receiving agent\n            :return: The role of the agent\n            \"\"\"\nreturn BaseOefSearchDialogue.Role.AGENT\nBaseOefSearchDialogues.__init__(\nself,\nself_address=str(self.skill_id),\nrole_from_first_message=role_from_first_message,\n)\nclass SigningDialogue(BaseSigningDialogue):\n\"\"\"The dialogue class maintains state of a dialogue and manages it.\"\"\"\n__slots__ = (\"_associated_ledger_api_dialogue\",)\ndef __init__(\nself,\ndialogue_label: BaseDialogueLabel,\nself_address: Address,\nrole: BaseDialogue.Role,\nmessage_class: Type[SigningMessage] = SigningMessage,\n) -> None:\n\"\"\"\n        Initialize a dialogue.\n        :param dialogue_label: the identifier of the dialogue\n        :param self_address: the address of the entity for whom this dialogue is maintained\n        :param role: the role of the agent this dialogue is maintained for\n        :param message_class: the message class\n        \"\"\"\nBaseSigningDialogue.__init__(\nself,\ndialogue_label=dialogue_label,\nself_address=self_address,\nrole=role,\nmessage_class=message_class,\n)\nself._associated_ledger_api_dialogue = None  # type: Optional[LedgerApiDialogue]\n@property\ndef associated_ledger_api_dialogue(self) -> LedgerApiDialogue:\n\"\"\"Get associated_ledger_api_dialogue.\"\"\"\nif self._associated_ledger_api_dialogue is None:\nraise AEAEnforceError(\"LedgerApiDialogue not set!\")\nreturn self._associated_ledger_api_dialogue\n@associated_ledger_api_dialogue.setter\ndef associated_ledger_api_dialogue(\nself, ledger_api_dialogue: LedgerApiDialogue\n) -> None:\n\"\"\"Set associated_ledger_api_dialogue\"\"\"\nenforce(\nself._associated_ledger_api_dialogue is None,\n\"LedgerApiDialogue already set!\",\n)\nself._associated_ledger_api_dialogue = ledger_api_dialogue\nclass SigningDialogues(Model, BaseSigningDialogues):\n\"\"\"This class keeps track of all oef_search dialogues.\"\"\"\ndef __init__(self, **kwargs: Any) -> None:\n\"\"\"\n        Initialize dialogues.\n        :param kwargs: keyword arguments\n        \"\"\"\nModel.__init__(self, **kwargs)\ndef role_from_first_message(  # pylint: disable=unused-argument\nmessage: Message, receiver_address: Address\n) -> BaseDialogue.Role:\n\"\"\"Infer the role of the agent from an incoming/outgoing first message\n            :param message: an incoming/outgoing first message\n            :param receiver_address: the address of the receiving agent\n            :return: The role of the agent\n            \"\"\"\nreturn BaseSigningDialogue.Role.SKILL\nBaseSigningDialogues.__init__(\nself,\nself_address=str(self.skill_id),\nrole_from_first_message=role_from_first_message,\ndialogue_class=SigningDialogue,\n)\n

The various dialogues classes in the above code snippet store dialogues with other AEAs, services and components, (e.g. SOEF search node via the fetchai/soef connection, ledgers via the fetchai/ledger connection and the decision maker). They expose useful methods to manipulate these interactions, access previous messages, and enable us to identify possible communications problems between my_generic_seller and my_generic_buyer AEAs.

"},{"location":"aea-framework-documentation/generic-skills-step-by-step/#step-6-update-the-yaml-files_1","title":"Step 6: Update the YAML Files","text":"

After making so many changes to our skill, we have to update the skill.yaml configuration file so it reflects our newly created classes, and contains the values used by the strategy. Make sure skill.yaml contains the following configuration:

name: generic_buyer\nauthor: fetchai\nversion: 0.1.0\ntype: skill\ndescription: The weather client skill implements the skill to purchase weather data.\nlicense: Apache-2.0\naea_version: '>=1.0.0, <2.0.0'\nfingerprint:\nREADME.md: QmTR91jm7WfJpmabisy74NR5mc35YXjDU1zQAUKZeHRw8L\n__init__.py: QmU5vrC8FipyjfS5biNa6qDWdp4aeH5h4YTtbFDmCg8Chj\nbehaviours.py: QmNwvSjEz4kzM3gWtnKbZVFJc2Z85Nb748CWAK4C4Sa4nT\ndialogues.py: QmNen91qQDWy4bNBKrB3LabAP5iRf29B8iwYss4NB13iNU\nhandlers.py: QmZfudXXbdiREiViuwPZDXoQQyXT2ySQHdF7psQsohZXQy\nstrategy.py: QmcrwaEWvKHDCNti8QjRhB4utJBJn5L8GpD27Uy9zHwKhY\nfingerprint_ignore_patterns: []\nconnections:\n- fetchai/ledger:0.21.5\ncontracts: []\nprotocols:\n- fetchai/default:1.1.7\n- fetchai/fipa:1.1.7\n- fetchai/ledger_api:1.1.7\n- fetchai/oef_search:1.1.7\n- fetchai/signing:1.1.7\nskills: []\nbehaviours:\nsearch:\nargs:\nsearch_interval: 5\nclass_name: GenericSearchBehaviour\ntransaction:\nargs:\nmax_processing: 420\ntransaction_interval: 2\nclass_name: GenericTransactionBehaviour\nhandlers:\nfipa:\nargs: {}\nclass_name: GenericFipaHandler\nledger_api:\nargs: {}\nclass_name: GenericLedgerApiHandler\noef_search:\nargs: {}\nclass_name: GenericOefSearchHandler\nsigning:\nargs: {}\nclass_name: GenericSigningHandler\nmodels:\ndefault_dialogues:\nargs: {}\nclass_name: DefaultDialogues\nfipa_dialogues:\nargs: {}\nclass_name: FipaDialogues\nledger_api_dialogues:\nargs: {}\nclass_name: LedgerApiDialogues\noef_search_dialogues:\nargs: {}\nclass_name: OefSearchDialogues\nsigning_dialogues:\nargs: {}\nclass_name: SigningDialogues\nstrategy:\nargs:\nis_ledger_tx: true\nlocation:\nlatitude: 51.5194\nlongitude: 0.127\nmax_negotiations: 1\nmax_tx_fee: 3550000000000000\nmax_unit_price: 20\nmin_quantity: 1\nsearch_query:\nconstraint_type: ==\nsearch_key: seller_service\nsearch_value: generic_service\nsearch_radius: 5.0\nservice_id: generic_service\nstop_searching_on_result: true\nclass_name: GenericStrategy\nis_abstract: false\ndependencies: {}\n

We must pay attention to the models and the strategy\u2019s variables. Here we can change the price we would like to buy each reading at, the maximum transaction fee we are prepared to pay, and so on.

Finally, we fingerprint our new skill:

aea fingerprint skill fetchai/generic_buyer:0.1.0\n

This will hash each file and save the hash in the fingerprint. This way, in the future we can easily track if any of the files have changed.

"},{"location":"aea-framework-documentation/generic-skills-step-by-step/#run-the-aeas","title":"Run the AEAs","text":""},{"location":"aea-framework-documentation/generic-skills-step-by-step/#create-private-keys","title":"Create Private Keys","text":"

For each AEA, create a private key:

aea generate-key fetchai\naea add-key fetchai fetchai_private_key.txt\n

Next, create a private key to secure the AEA's communications:

aea generate-key fetchai fetchai_connection_private_key.txt\naea add-key fetchai fetchai_connection_private_key.txt --connection\n

Finally, certify the key for use by the connections that request that:

aea issue-certificates\n
"},{"location":"aea-framework-documentation/generic-skills-step-by-step/#update-the-aea-configurations","title":"Update the AEA Configurations","text":"

In both AEAs run:

aea config set --type dict agent.default_routing \\\n'{\n  \"fetchai/ledger_api:1.1.7\": \"fetchai/ledger:0.21.5\",\n  \"fetchai/oef_search:1.1.7\": \"fetchai/soef:0.27.6\"\n}'\n
"},{"location":"aea-framework-documentation/generic-skills-step-by-step/#fund-the-buyer-aea","title":"Fund the Buyer AEA","text":"

Create some wealth for your buyer on the Fetch.ai testnet (this operation might take a while).

aea generate-wealth fetchai --sync\n
"},{"location":"aea-framework-documentation/generic-skills-step-by-step/#run-seller-aea","title":"Run Seller AEA","text":"

Add the remaining packages for the seller AEA, then run it:

aea add connection fetchai/p2p_libp2p:0.27.5\naea add connection fetchai/soef:0.27.6\naea add connection fetchai/ledger:0.21.5\naea add protocol fetchai/fipa:1.1.7\naea install\naea build\naea config set agent.default_connection fetchai/p2p_libp2p:0.27.5\naea run\n

Once you see a message of the form To join its network use multiaddr: ['SOME_ADDRESS'] take note of the address.

"},{"location":"aea-framework-documentation/generic-skills-step-by-step/#run-buyer-aea","title":"Run Buyer AEA","text":"

Add the remaining packages for the buyer AEA:

aea add connection fetchai/p2p_libp2p:0.27.5\naea add connection fetchai/soef:0.27.6\naea add connection fetchai/ledger:0.21.5\naea add protocol fetchai/fipa:1.1.7\naea add protocol fetchai/signing:1.1.7\naea install\naea build\naea config set agent.default_connection fetchai/p2p_libp2p:0.27.5\n

Then, update the configuration of the buyer AEA's P2P connection:

aea config set --type dict vendor.fetchai.connections.p2p_libp2p.config \\\n'{\n  \"delegate_uri\": \"127.0.0.1:11001\",\n  \"entry_peers\": [\"SOME_ADDRESS\"],\n  \"local_uri\": \"127.0.0.1:9001\",\n  \"log_file\": \"libp2p_node.log\",\n  \"public_uri\": \"127.0.0.1:9001\"\n}'\n

where SOME_ADDRESS is replaced accordingly.

Then run the buyer AEA:

aea run\n

You will see that the AEAs negotiate and then transact using the Dorado testnet.

"},{"location":"aea-framework-documentation/generic-skills-step-by-step/#delete-the-aeas","title":"Delete the AEAs","text":"

When you are done, go up a level and delete the AEAs.

cd ..\naea delete my_generic_seller\naea delete my_generic_buyer\n
"},{"location":"aea-framework-documentation/generic-skills-step-by-step/#next-steps","title":"Next Steps","text":"

You have completed the \"Getting Started\" series. Congratulations!

The following guide provides some hints on AEA development setup.

"},{"location":"aea-framework-documentation/generic-skills-step-by-step/#recommended","title":"Recommended","text":"

We recommend you build your own AEA next. There are many helpful guides and demos in the documentation, and a developer community on Discord. Speak to you there!

"},{"location":"aea-framework-documentation/generic-skills/","title":"Generic Skills","text":"

The AEA generic buyer and seller skills demonstrate an interaction between two AEAs:

  • An AEA that provides a (data selling) service.
  • An AEA that demands this service.
"},{"location":"aea-framework-documentation/generic-skills/#discussion","title":"Discussion","text":"

The scope of this guide is demonstrating how to create easily configurable AEAs. The buyer AEA finds the seller, negotiates the terms of trade, and if successful purchases the data by sending payment. The seller AEA sells the service specified in its skill.yaml file, delivering it to the buyer upon receiving payment.

Note that these agents do not utilize a smart contract but interact with a ledger to complete a transaction. Moreover, in this setup, the buyer agent has to trust the seller to send the data upon successful payment.

The corresponding packages can be customised to allow for a database or sensor to be defined from which data is loaded. This is done by first modifying the has_data_source variable in skill.yaml file of the generic_seller skill to True. Then you have to provide an implementation for the collect_from_data_source(self) method in the strategy.py file. More detailed instructions is beyond the scope of this guide.

"},{"location":"aea-framework-documentation/generic-skills/#communication","title":"Communication","text":"

The following diagram shows the communication between various entities in this interaction.

    sequenceDiagram\n        participant Search\n        participant Buyer_AEA\n        participant Seller_AEA\n        participant Blockchain\n\n        activate Buyer_AEA\n        activate Search\n        activate Seller_AEA\n        activate Blockchain\n\n        Seller_AEA->>Search: register_service\n        Buyer_AEA->>Search: search_agents\n        Search-->>Buyer_AEA: list_of_agents\n        Buyer_AEA->>Seller_AEA: call_for_proposal\n        Seller_AEA->>Buyer_AEA: propose\n        Buyer_AEA->>Seller_AEA: accept\n        Seller_AEA->>Buyer_AEA: match_accept\n        Buyer_AEA->>Blockchain: transfer_funds\n        Buyer_AEA->>Seller_AEA: send_transaction_hash\n        Seller_AEA->>Blockchain: check_transaction_status\n        Seller_AEA->>Buyer_AEA: send_data\n\n        deactivate Buyer_AEA\n        deactivate Search\n        deactivate Seller_AEA\n        deactivate Blockchain 
"},{"location":"aea-framework-documentation/generic-skills/#preparation-instructions","title":"Preparation Instructions","text":""},{"location":"aea-framework-documentation/generic-skills/#dependencies","title":"Dependencies","text":"

Follow the Preliminaries and Installation sections from the AEA quick start.

"},{"location":"aea-framework-documentation/generic-skills/#demo-instructions","title":"Demo Instructions","text":""},{"location":"aea-framework-documentation/generic-skills/#create-the-seller-aea","title":"Create the Seller AEA","text":"

First, fetch the seller AEA:

aea fetch fetchai/generic_seller:0.29.5 --alias my_seller_aea\ncd my_seller_aea\naea install\naea build\n
Alternatively, create from scratch:

The following steps create the seller from scratch:

aea create my_seller_aea\ncd my_seller_aea\naea add connection fetchai/p2p_libp2p:0.27.5\naea add connection fetchai/soef:0.27.6\naea add connection fetchai/ledger:0.21.5\naea add skill fetchai/generic_seller:0.28.6\naea config set --type dict agent.dependencies \\\n'{\n  \"aea-ledger-fetchai\": {\"version\": \"<2.0.0,>=1.0.0\"}\n}'\naea config set agent.default_connection fetchai/p2p_libp2p:0.27.5\naea config set --type dict agent.default_routing \\\n'{\n  \"fetchai/ledger_api:1.1.7\": \"fetchai/ledger:0.21.5\",\n  \"fetchai/oef_search:1.1.7\": \"fetchai/soef:0.27.6\"\n}'\naea install\naea build\n
"},{"location":"aea-framework-documentation/generic-skills/#create-the-buyer-aea","title":"Create the Buyer AEA","text":"

Then, in another terminal fetch the buyer AEA:

aea fetch fetchai/generic_buyer:0.30.5 --alias my_buyer_aea\ncd my_buyer_aea\naea install\naea build\n
Alternatively, create from scratch:

The following steps create the buyer from scratch:

aea create my_buyer_aea\ncd my_buyer_aea\naea add connection fetchai/p2p_libp2p:0.27.5\naea add connection fetchai/soef:0.27.6\naea add connection fetchai/ledger:0.21.5\naea add skill fetchai/generic_buyer:0.27.6\naea config set --type dict agent.dependencies \\\n'{\n  \"aea-ledger-fetchai\": {\"version\": \"<2.0.0,>=1.0.0\"}\n}'\naea config set agent.default_connection fetchai/p2p_libp2p:0.27.5\naea config set --type dict agent.default_routing \\\n'{\n  \"fetchai/ledger_api:1.1.7\": \"fetchai/ledger:0.21.5\",\n  \"fetchai/oef_search:1.1.7\": \"fetchai/soef:0.27.6\"\n}'\naea install\naea build\n
"},{"location":"aea-framework-documentation/generic-skills/#add-keys-for-the-seller-aea","title":"Add Keys for the Seller AEA","text":"

Create the private key for the seller AEA based on the network you want to transact. To generate and add a private-public key pair for Fetch.ai Dorado use:

aea generate-key fetchai\naea add-key fetchai fetchai_private_key.txt\n

Next, create a private key used to secure the AEA's communications:

aea generate-key fetchai fetchai_connection_private_key.txt\naea add-key fetchai fetchai_connection_private_key.txt --connection\n

Finally, certify the key for use by the connections that request that:

aea issue-certificates\n
"},{"location":"aea-framework-documentation/generic-skills/#add-keys-and-generate-wealth-for-the-buyer-aea","title":"Add Keys and Generate Wealth for the Buyer AEA","text":"

The buyer needs to have some wealth to purchase the data from the seller.

First, create the private key for the buyer AEA based on the network you want to transact. To generate and add a private-public key pair for Fetch.ai Dorado use:

aea generate-key fetchai\naea add-key fetchai fetchai_private_key.txt\n

Then, create some wealth for your buyer based on the network you want to transact with. On the Fetch.ai Dorado network:

aea generate-wealth fetchai\n

Next, create a private key used to secure the AEA's communications:

aea generate-key fetchai fetchai_connection_private_key.txt\naea add-key fetchai fetchai_connection_private_key.txt --connection\n

Finally, certify the key for use by the connections that request that:

aea issue-certificates\n
"},{"location":"aea-framework-documentation/generic-skills/#update-the-skill-configurations","title":"Update the Skill Configurations","text":"

The default skill configurations assume that the transaction is settled against the Fetch.ai ledger.

In the generic seller's skill configuration file (my_seller_aea/vendor/fetchai/skills/generi_seller/skill.yaml) the data_for_sale is the data the seller AEA is offering for sale. In the following case, this is a one item dictionary where key is generic and value is data.

Furthermore, the service_data is used to register the seller's service in the SOEF search node and make your agent discoverable.

models:\n...\nstrategy:\nargs:\ncurrency_id: FET\ndata_for_sale:\ngeneric: data\nhas_data_source: false\nis_ledger_tx: true\nledger_id: fetchai\nlocation:\nlatitude: 51.5194\nlongitude: 0.127\nservice_data:\nkey: seller_service\nvalue: generic_service\nservice_id: generic_service\nunit_price: 10\nclass_name: GenericStrategy\n

The generic buyer skill configuration file (my_buyer_aea/vendor/fetchai/skills/generic_buyer/skill.yaml) includes the search_query which has to match the service_data of the seller.

models:\n...\nstrategy:\nargs:\ncurrency_id: FET\nis_ledger_tx: true\nledger_id: fetchai\nlocation:\nlatitude: 51.5194\nlongitude: 0.127\nmax_negotiations: 1\nmax_tx_fee: 3550000000000000\nmax_unit_price: 20\nsearch_query:\nconstraint_type: ==\nsearch_key: seller_service\nsearch_value: generic_service\nsearch_radius: 5.0\nservice_id: generic_service\nclass_name: GenericStrategy\n
"},{"location":"aea-framework-documentation/generic-skills/#update-the-skill-configurations_1","title":"Update the Skill Configurations","text":"

Both skills are abstract skills, make them instantiable:

cd my_seller_aea\naea config set vendor.fetchai.skills.generic_seller.is_abstract false --type bool\n
cd my_buyer_aea\naea config set vendor.fetchai.skills.generic_buyer.is_abstract false --type bool\n
"},{"location":"aea-framework-documentation/generic-skills/#run-the-aeas","title":"Run the AEAs","text":"

First, run the seller AEA:

aea run\n

Once you see a message of the form To join its network use multiaddr 'SOME_ADDRESS' take note of this address. (Alternatively, use aea get-multiaddress fetchai -c -i fetchai/p2p_libp2p:0.27.5 -u public_uri to retrieve the address.) This is the entry peer address for the local agent communication network created by the seller.

Then, configure the buyer to connect to this same local ACN by running the following command in the buyer terminal, replacing SOME_ADDRESS with the value you noted above:

aea config set --type dict vendor.fetchai.connections.p2p_libp2p.config \\\n'{\n  \"delegate_uri\": \"127.0.0.1:11001\",\n  \"entry_peers\": [\"SOME_ADDRESS\"],\n  \"local_uri\": \"127.0.0.1:9001\",\n  \"log_file\": \"libp2p_node.log\",\n  \"public_uri\": \"127.0.0.1:9001\"\n}'\n

Then run the buyer AEA:

aea run\n

You will see that the AEAs negotiate and then transact using the Fetch.ai testnet.

"},{"location":"aea-framework-documentation/generic-skills/#delete-the-aeas","title":"Delete the AEAs","text":"

When you're done, stop the agents (CTRL+C), go up a level and delete the AEAs.

cd ..\naea delete my_seller_aea\naea delete my_buyer_aea\n
"},{"location":"aea-framework-documentation/generic-storage/","title":"Generic Storage","text":"

The AEA generic storage: description and usage.

"},{"location":"aea-framework-documentation/generic-storage/#aea-generic-storage","title":"AEA Generic Storage","text":"

AEA generic storage allows AEA skill's components to store data permanently and use it any time. The primary scenario: to save AEA data on shutdown and load back on startup. Generic storage provides an API for general data manipulation in key-object style.

"},{"location":"aea-framework-documentation/generic-storage/#configuration","title":"Configuration","text":"

Storage is enabled by providing in the agent configuration (aea-config.yaml) an optional storage_uri. The storage URI consists of the backend name and string data provided to selected backend.

The storage URI schema is <BACKEND_NAME>://[Optional string] Example: storage_uri: sqlite://./some_file.db tells the AEA to use SQLite backend and store data in ./some_file.db.

Supported backends:

  • SQLite - bundled with python simple SQL engine that uses file or in-memory storage.
"},{"location":"aea-framework-documentation/generic-storage/#dialogues-and-storage-integration","title":"Dialogues and Storage Integration","text":"

One of the most useful cases is the integration of the dialogues subsystem and storage. It helps maintain dialogues state during agent restarts and reduced memory requirements due to the offloading feature.

"},{"location":"aea-framework-documentation/generic-storage/#keep-terminal-state-dialogues","title":"Keep Terminal State Dialogues","text":"

The Dialogues class has the optional boolean argument keep_terminal_state_dialogues which specifies whether a dialogue which has reached its terminal state is kept in memory or not. If keep_terminal_state_dialogues is False, dialogues that reach a terminal state are removed from memory and can not be used anymore. If keep_terminal_state_dialogues is True, dialogues that reach a terminal state are kept in memory or storage (if configured). If storage is configured, all dialogues in memory are stored on agent stop and restored on agent start.

It is useful to save memory with terminated dialogues that will (possibly) be never used again.

Default behaviour on keep terminals state dialogues is set according to the protocol specification but can be set explicitly with skill configuration section.

Skill configuration to keep terminated dialogues for DefaultDialogues. Example:

"},{"location":"aea-framework-documentation/generic-storage/#dialogues-dumprestore-on-agent-restart","title":"Dialogues Dump/Restore on Agent Restart","text":"

If storage is enabled then all the dialogues present in memory will be stored on agent's teardown and loaded on agent's start.

"},{"location":"aea-framework-documentation/generic-storage/#offload-terminal-state-dialogues","title":"Offload Terminal State Dialogues","text":"

If keep options is set and storage is available dialogues in terminal state will be dumped to generic storage and removed from memory. This option helps to save memory and handle terminated dialogues with the same functionality as when they are kept in memory.

All the active dialogues will be stored and loaded during agent restart. All the terminated offloaded dialogues will stay in storage on agent restart.

To enable dialogues offloading keep_terminal_state_dialogues has to be enabled and storage configured.

"},{"location":"aea-framework-documentation/generic-storage/#manual-usage-with-skill-components","title":"Manual Usage with Skill Components","text":"

Handlers, Behaviours and Models are able to use storage if enabled.

Storage is available with skill context: self.context.storage if self.context.storage is not None, storage is enabled and ready to use.

Generic storage consists of two parts: objects and collections. Objects consist of the object_id (unique string) and object body. The object body is any JSON friendly python data type: list, dict, int, float, string, bool.

Collection is a group of the objects, objects data types can vary in the same collection. Collection name is name consists of letters, numbers and _.

To get/put specific object collection instance should be used.

my_collection = self.context.storage.get_sync_connection('my_collection')\n

Collection instance provide set of methods to handle data objects. List of collection methods:

    def put(self, object_id: str, object_body: JSON_TYPES) -> None:\n\"\"\"\n        Put object into collection.\n        :param object_id: str object id\n        :param object_body: python dict, json compatible.\n        :return: None\n        \"\"\"\ndef get(self, object_id: str) -> Optional[JSON_TYPES]:\n\"\"\"\n        Get object from the collection.\n        :param object_id: str object id\n        :return: dict if object exists in collection otherwise None\n        \"\"\"\ndef remove(self, object_id: str) -> None:\n\"\"\"\n        Remove object from the collection.\n        :param object_id: str object id\n        :return: None\n        \"\"\"\ndef find(self, field: str, equals: EQUALS_TYPE) -> List[OBJECT_ID_AND_BODY]:\n\"\"\"\n        Get objects from the collection by filtering by field value.\n        :param field: field name to search: example \"parent.field\"\n        :param equals: value field should be equal to\n        :return: List of object bodies\n        \"\"\"\ndef list(self) -> List[OBJECT_ID_AND_BODY]:\n\"\"\"\n        List all objects with keys from the collection.\n        :return: Tuple of objects keys, bodies.\n        \"\"\"\n

Simple behaviour example:

It saves the datetime string of the first act and print it to stdout.

class TestBehaviour(TickerBehaviour):\n\"\"\"Simple behaviour to count how many acts were called.\"\"\"\ndef setup(self) -> None:\n\"\"\"Set up behaviour.\"\"\"\ndef act(self) -> None:\n\"\"\"Make an action.\"\"\"\nif not (self.context.storage and self.context.storage.is_connected):\nreturn\ncollection = self.context.storage.get_sync_collection('my_collection')\nfirst_call_datetime = collection.get(\"first_call_ts\")\nif not first_call_ts:\n# there is no object with \"first_call_ts\" id.\nfirst_call_datetime = str(datetime.datetime.now())\ncol.put(first_call_ts, first_call_datetime)\nprint(\"Act was called for the first time on:\", first_call_datetime)\n

Please, pay attention: datetime object is not JSON friendly and can not be stored directly. it should be transformed to timestamp or string before put into the storage.

"},{"location":"aea-framework-documentation/glossary/","title":"Glossary","text":"

This glossary defines a number of terms commonly used across the documentation. For the definitions of framework components consult the API docs.

  • AEA (Autonomous Economic Agent): An AEA is \"an intelligent agent acting on an owner's behalf, with limited or no interference, and whose goal is to generate economic value to its owner\". AEAs are a special type of agent. [more]

  • Software Agent: a software agent is a computer program that acts on behalf of an entity (e.g. individual, organisation, business). [more]

  • sOEF (Simple Open Economic Framework): The simple-OEF, or sOEF, is a search and discovery service for autonomous economic agents. [more]

  • ACN (Agent Communication Network): The ACN is a peer-to-peer communication network for autonomous economic agents. [more]

"},{"location":"aea-framework-documentation/gym-example/","title":"Gym Example","text":"

The gym example demonstrates the AEA framework's flexibility with respect to Reinforcement Learning using OpenAI's gym framework.

"},{"location":"aea-framework-documentation/gym-example/#discussion","title":"Discussion","text":"

There is no immediate use case for this example as you can train an RL agent without the AEA proxy layer just fine (and faster).

However, the example decouples the RL agent from the gym.Env allowing them to run in separate execution environments, potentially owned by different entities.

"},{"location":"aea-framework-documentation/gym-example/#preparation-instructions","title":"Preparation Instructions","text":""},{"location":"aea-framework-documentation/gym-example/#dependencies","title":"Dependencies","text":"

Follow the Preliminaries and Installation sections from the AEA quick start.

Download the necessary directories into your working directory:

svn export https://github.com/fetchai/agents-aea.git/trunk/examples\nsvn export https://github.com/fetchai/agents-aea.git/trunk/packages\n

Install the gym and numpy library.

pip install numpy gym\n
"},{"location":"aea-framework-documentation/gym-example/#demo-instructions","title":"Demo Instructions","text":""},{"location":"aea-framework-documentation/gym-example/#run-the-example","title":"Run the Example","text":"
python examples/gym_ex/train.py\n

Notice the usual RL setup, i.e. the fit method of the RL agent has the typical signature and a familiar implementation.

Note how train.py demonstrates how easy it is to use an AEA agent as a proxy layer between an OpenAI gym.Env and a standard RL agent.

It is just one line of code to introduce the proxy agent and proxy environment!

from gyms.env import BanditNArmedRandom\nfrom proxy.env import ProxyEnv\nfrom rl.agent import RLAgent\nif __name__ == \"__main__\":\nNB_GOODS = 10\nNB_PRICES_PER_GOOD = 100\nNB_STEPS = 4000\n# Use any gym.Env compatible environment:\ngym_env = BanditNArmedRandom(nb_bandits=NB_GOODS, nb_prices_per_bandit=NB_PRICES_PER_GOOD)\n# Pass the gym environment to a proxy environment:\nproxy_env = ProxyEnv(gym_env)\n# Use any RL agent compatible with the gym environment and call the fit method:\nrl_agent = RLAgent(nb_goods=NB_GOODS)\nrl_agent.fit(env=proxy_env, nb_steps=NB_STEPS)\n
"},{"location":"aea-framework-documentation/gym-skill/","title":"Gym Skill","text":"

The AEA gym skill demonstrates how a custom Reinforcement Learning agent, that uses OpenAI's gym library, may be embedded into an AEA skill and connection.

"},{"location":"aea-framework-documentation/gym-skill/#discussion","title":"Discussion","text":"

The gym skills demonstrate how to wrap a Reinforcement Learning agent in a skill. The example decouples the RL agent from the gym.Env allowing them to run in separate execution environments, potentially owned by different entities.

"},{"location":"aea-framework-documentation/gym-skill/#preparation-instructions","title":"Preparation Instructions","text":""},{"location":"aea-framework-documentation/gym-skill/#dependencies","title":"Dependencies","text":"

Follow the Preliminaries and Installation sections from the AEA quick start.

Download the necessary directories into your working directory:

mkdir gym_skill_agent\nsvn export https://github.com/fetchai/agents-aea.git/trunk/examples\n

Install the gym and numpy library.

pip install numpy gym\n
"},{"location":"aea-framework-documentation/gym-skill/#demo-instructions","title":"Demo Instructions","text":""},{"location":"aea-framework-documentation/gym-skill/#create-the-aea","title":"Create the AEA","text":"

First, fetch the gym AEA:

aea fetch fetchai/gym_aea:0.26.5 --alias my_gym_aea\ncd my_gym_aea\naea install\n
Alternatively, create from scratch:"},{"location":"aea-framework-documentation/gym-skill/#create-the-aea_1","title":"Create the AEA","text":"

In the root directory, create the gym AEA and enter the project.

aea create my_gym_aea\ncd my_gym_aea\n
"},{"location":"aea-framework-documentation/gym-skill/#add-the-gym-skill","title":"Add the gym skill","text":"
aea add skill fetchai/gym:0.21.6\n
"},{"location":"aea-framework-documentation/gym-skill/#set-gym-connection-as-default","title":"Set gym connection as default","text":"
aea config set agent.default_connection fetchai/gym:0.20.6\n
"},{"location":"aea-framework-documentation/gym-skill/#install-the-skill-dependencies","title":"Install the skill dependencies","text":"

To install the gym package, a dependency of the gym skill, from PyPI run

aea install\n
"},{"location":"aea-framework-documentation/gym-skill/#set-up-the-training-environment","title":"Set up the Training Environment","text":""},{"location":"aea-framework-documentation/gym-skill/#copy-the-gym-environment-to-the-aea-directory","title":"Copy the Gym Environment to the AEA Directory","text":"
mkdir gyms\ncp -a ../examples/gym_ex/gyms/. gyms/\n
"},{"location":"aea-framework-documentation/gym-skill/#update-the-connection-configuration","title":"Update the Connection Configuration","text":"
aea config set vendor.fetchai.connections.gym.config.env 'gyms.env.BanditNArmedRandom'\n
"},{"location":"aea-framework-documentation/gym-skill/#create-and-add-a-private-key","title":"Create and Add a Private Key","text":"
aea generate-key fetchai\naea add-key fetchai\n
"},{"location":"aea-framework-documentation/gym-skill/#run-the-aea-with-the-gym-connection","title":"Run the AEA with the Gym Connection","text":"
aea run\n

You will see the gym training logs.

"},{"location":"aea-framework-documentation/gym-skill/#delete-the-aea","title":"Delete the AEA","text":"

When you're done, you can go up a level and delete the AEA.

cd ..\naea delete my_gym_aea\n
"},{"location":"aea-framework-documentation/gym-skill/#communication","title":"Communication","text":"

This diagram shows the communication between the AEA and the gym environment

    sequenceDiagram\n        participant AEA\n        participant Environment\n\n        activate AEA\n        activate Environment\n        AEA->>Environment: reset\n        loop learn\n            AEA->>Environment: act\n            Environment->>AEA: percept\n        end\n        AEA->>Environment: close\n\n        deactivate AEA\n        deactivate Environment
"},{"location":"aea-framework-documentation/gym-skill/#skill-architecture","title":"Skill Architecture","text":"

The skill consists of two core components: GymHandler and GymTask.

In the setup method of the GymHandler the GymTask is initialized, as well as its setup and execute methods called. The handler, which is registered against the GymMessage.protocol_id then filters for messages of that protocol with the performative GymMessage.Performative.PERCEPT. These messages are passed to the proxy_env_queue of the task.

The GymTask is responsible for training the RL agent. In particular, MyRLAgent is initialized and trained against ProxyEnv. The ProxyEnv instantiates a gym.Env class and therefore implements its API. This means the proxy environment is compatible with any gym compatible RL agent. However, unlike other environments it only acts as a proxy and does not implement an environment of its own. It allows for the decoupling of the process environment of the gym.env from the process environment of the RL agent. The actual gym.env against which the agent is trained is wrapped by the gym connection. The proxy environment and gym connection communicate via a protocol, the gym protocol. Note, it would trivially be possible to implement the gym environment in another AEA; this way one AEA could provide gym environments as a service. Naturally, the overhead created by the introduction of the extra layers causes a higher latency when training the RL agent.

In this particular skill, which chiefly serves for demonstration purposes, we implement a very basic RL agent. The agent trains a model of price of n goods: it aims to discover the most likely price of each good. To this end, the agent randomly selects one of the n goods on each training step and then chooses as an action the price which it deems is most likely accepted. Each good is represented by an id and the possible price range [1,100] divided into 100 integer bins. For each price bin, a PriceBandit is created which models the likelihood of this price. In particular, a price bandit maintains a beta distribution. The beta distribution is initialized to the uniform distribution. Each time the price associated with a given PriceBandit is accepted or rejected the distribution maintained by the PriceBandit is updated. For each good, the agent can therefore over time learn which price is most likely.

The illustration shows how the RL agent only interacts with the proxy environment by sending it action (A) and receiving observation (O), reward (R), done (D) and info (I).

"},{"location":"aea-framework-documentation/http-connection-and-skill/","title":"HTTP Connection","text":""},{"location":"aea-framework-documentation/http-connection-and-skill/#description","title":"Description","text":"

The HTTP client and HTTP server connections enable an AEA to communicate with external servers, respectively clients, via HTTP.

The HTTP client connection receives request envelops from an agent's skill, translates each into an HTTP request and sends it to a server external to the agent. If it receives an HTTP response from the server within a timeout window, it translates it into a response envelope, and sends this back to the relevant skill inside the agent.

The HTTP server connection allows you to run a server inside the connection itself which accepts requests from clients external to the agent. The HTTP server connection validates requests it receives against a provided OpenAPI file. It translates each valid request into an envelope and sends it to the skill specified in the connections configuration. If it receives a valid response envelope from the skill within a timeout window, the connection translates the response envelope into an HTTP response and serves it to the client.

"},{"location":"aea-framework-documentation/http-connection-and-skill/#http-client","title":"HTTP Client","text":"

The fetchai/simple_data_request:0.14.6 skill demonstrates a simple use case of the HTTP Client connection.

The HttpRequestBehaviour in behaviours.py periodically sends HTTP envelops to the HTTP client connection. Its act() method, periodically called, simply calls _generate_http_request which contains the logic for enqueueing an HTTP request envelop.

The HttpHandler in handler.py is a basic handler for dealing with HTTP response envelops received from the HTTP client connection. In the handle() method, the responses are dealt with by the private _handle_response method which essentially logs the response and adds the body of the response into the skill's shared state.

"},{"location":"aea-framework-documentation/http-connection-and-skill/#http-server","title":"HTTP Server","text":"

Create a new AEA:

aea create my_aea\ncd my_aea\n

Add the http server connection package:

aea add connection fetchai/http_server:0.23.6\n

Update the default connection:

aea config set agent.default_connection fetchai/http_server:0.23.6\n

Modify the api_spec_path:

aea config set vendor.fetchai.connections.http_server.config.api_spec_path \"../examples/http_ex/petstore.yaml\"\n

Ensure the file exists under the specified path!

Create and add a private key:

aea generate-key fetchai\naea add-key fetchai\n

Install the dependencies:

aea install\n

Write and add your skill:

aea scaffold skill http_echo\n

You can implement a simple http echo skill (modelled after the standard echo skill) which prints out the content of received messages and responds with success.

First, delete the my_model.py and behaviour.py files (in my_aea/skills/http_echo/). The server will be purely reactive, so you only need the handlers.py file, and the dialogues.py to record the state of the dialogues. Update skill.yaml accordingly, so set models: {} and behaviours: {}.

Next implement a basic handler which prints the received envelopes and responds.

Then, replace the content of handlers.py with the following code snippet, after having replaced the placeholder YOUR_USERNAME with the author username (i.e. the output of aea config get agent.author):

import json\nfrom typing import cast\nfrom aea.protocols.base import Message\nfrom aea.skills.base import Handler\nfrom packages.fetchai.protocols.default import DefaultMessage\nfrom packages.fetchai.protocols.http.message import HttpMessage\nfrom packages.YOUR_USERNAME.skills.http_echo.dialogues import (\nDefaultDialogues,\nHttpDialogue,\nHttpDialogues,\n)\nclass HttpHandler(Handler):\n\"\"\"This implements the echo handler.\"\"\"\nSUPPORTED_PROTOCOL = HttpMessage.protocol_id\ndef setup(self) -> None:\n\"\"\"Implement the setup.\"\"\"\ndef handle(self, message: Message) -> None:\n\"\"\"\n        Implement the reaction to an envelope.\n        :param message: the message\n        \"\"\"\nhttp_msg = cast(HttpMessage, message)\n# recover dialogue\nhttp_dialogues = cast(HttpDialogues, self.context.http_dialogues)\nhttp_dialogue = cast(HttpDialogue, http_dialogues.update(http_msg))\nif http_dialogue is None:\nself._handle_unidentified_dialogue(http_msg)\nreturn\n# handle message\nif http_msg.performative == HttpMessage.Performative.REQUEST:\nself._handle_request(http_msg, http_dialogue)\nelse:\nself._handle_invalid(http_msg, http_dialogue)\ndef _handle_unidentified_dialogue(self, http_msg: HttpMessage) -> None:\n\"\"\"\n        Handle an unidentified dialogue.\n        :param http_msg: the message\n        \"\"\"\nself.context.logger.info(\n\"received invalid http message={}, unidentified dialogue.\".format(http_msg)\n)\ndefault_dialogues = cast(DefaultDialogues, self.context.default_dialogues)\ndefault_msg, _ = default_dialogues.create(\ncounterparty=http_msg.sender,\nperformative=DefaultMessage.Performative.ERROR,\nerror_code=DefaultMessage.ErrorCode.INVALID_DIALOGUE,\nerror_msg=\"Invalid dialogue.\",\nerror_data={\"http_message\": http_msg.encode()},\n)\nself.context.outbox.put_message(message=default_msg)\ndef _handle_request(\nself, http_msg: HttpMessage, http_dialogue: HttpDialogue\n) -> None:\n\"\"\"\n        Handle a Http request.\n        :param http_msg: the http message\n        :param http_dialogue: the http dialogue\n        \"\"\"\nself.context.logger.info(\n\"received http request with method={}, url={} and body={!r}\".format(\nhttp_msg.method,\nhttp_msg.url,\nhttp_msg.body,\n)\n)\nif http_msg.method == \"get\":\nself._handle_get(http_msg, http_dialogue)\nelif http_msg.method == \"post\":\nself._handle_post(http_msg, http_dialogue)\ndef _handle_get(self, http_msg: HttpMessage, http_dialogue: HttpDialogue) -> None:\n\"\"\"\n        Handle a Http request of verb GET.\n        :param http_msg: the http message\n        :param http_dialogue: the http dialogue\n        \"\"\"\nhttp_response = http_dialogue.reply(\nperformative=HttpMessage.Performative.RESPONSE,\ntarget_message=http_msg,\nversion=http_msg.version,\nstatus_code=200,\nstatus_text=\"Success\",\nheaders=http_msg.headers,\nbody=json.dumps({\"tom\": {\"type\": \"cat\", \"age\": 10}}).encode(\"utf-8\"),\n)\nself.context.logger.info(\"responding with: {}\".format(http_response))\nself.context.outbox.put_message(message=http_response)\ndef _handle_post(self, http_msg: HttpMessage, http_dialogue: HttpDialogue) -> None:\n\"\"\"\n        Handle a Http request of verb POST.\n        :param http_msg: the http message\n        :param http_dialogue: the http dialogue\n        \"\"\"\nhttp_response = http_dialogue.reply(\nperformative=HttpMessage.Performative.RESPONSE,\ntarget_message=http_msg,\nversion=http_msg.version,\nstatus_code=200,\nstatus_text=\"Success\",\nheaders=http_msg.headers,\nbody=http_msg.body,\n)\nself.context.logger.info(\"responding with: {}\".format(http_response))\nself.context.outbox.put_message(message=http_response)\ndef _handle_invalid(\nself, http_msg: HttpMessage, http_dialogue: HttpDialogue\n) -> None:\n\"\"\"\n        Handle an invalid http message.\n        :param http_msg: the http message\n        :param http_dialogue: the http dialogue\n        \"\"\"\nself.context.logger.warning(\n\"cannot handle http message of performative={} in dialogue={}.\".format(\nhttp_msg.performative, http_dialogue\n)\n)\ndef teardown(self) -> None:\n\"\"\"Implement the handler teardown.\"\"\"\n

Moreover, add a dialogues.py file with the following code:

from typing import Any\nfrom aea.protocols.base import Address, Message\nfrom aea.protocols.dialogue.base import Dialogue as BaseDialogue\nfrom aea.skills.base import Model\nfrom packages.fetchai.protocols.default.dialogues import (\nDefaultDialogue as BaseDefaultDialogue,\n)\nfrom packages.fetchai.protocols.default.dialogues import (\nDefaultDialogues as BaseDefaultDialogues,\n)\nfrom packages.fetchai.protocols.http.dialogues import HttpDialogue as BaseHttpDialogue\nfrom packages.fetchai.protocols.http.dialogues import HttpDialogues as BaseHttpDialogues\nDefaultDialogue = BaseDefaultDialogue\nclass DefaultDialogues(Model, BaseDefaultDialogues):\n\"\"\"The dialogues class keeps track of all dialogues.\"\"\"\ndef __init__(self, **kwargs: Any) -> None:\n\"\"\"\n        Initialize dialogues.\n        :param kwargs: keyword arguments\n        \"\"\"\nModel.__init__(self, **kwargs)\ndef role_from_first_message(  # pylint: disable=unused-argument\nmessage: Message, receiver_address: Address\n) -> BaseDialogue.Role:\n\"\"\"Infer the role of the agent from an incoming/outgoing first message\n            :param message: an incoming/outgoing first message\n            :param receiver_address: the address of the receiving agent\n            :return: The role of the agent\n            \"\"\"\nreturn DefaultDialogue.Role.AGENT\nBaseDefaultDialogues.__init__(\nself,\nself_address=self.context.agent_address,\nrole_from_first_message=role_from_first_message,\n)\nHttpDialogue = BaseHttpDialogue\nclass HttpDialogues(Model, BaseHttpDialogues):\n\"\"\"The dialogues class keeps track of all dialogues.\"\"\"\ndef __init__(self, **kwargs: Any) -> None:\n\"\"\"\n        Initialize dialogues.\n        :param kwargs: keyword arguments\n        \"\"\"\nModel.__init__(self, **kwargs)\ndef role_from_first_message(  # pylint: disable=unused-argument\nmessage: Message, receiver_address: Address\n) -> BaseDialogue.Role:\n\"\"\"Infer the role of the agent from an incoming/outgoing first message\n            :param message: an incoming/outgoing first message\n            :param receiver_address: the address of the receiving agent\n            :return: The role of the agent\n            \"\"\"\nreturn BaseHttpDialogue.Role.SERVER\nBaseHttpDialogues.__init__(\nself,\nself_address=str(self.skill_id),\nrole_from_first_message=role_from_first_message,\n)\n

Then, update the skill.yaml accordingly:

handlers:\nhttp_handler:\nargs: {}\nclass_name: HttpHandler\nmodels:\ndefault_dialogues:\nargs: {}\nclass_name: DefaultDialogues\nhttp_dialogues:\nargs: {}\nclass_name: HttpDialogues\n

Run the fingerprinter (note, you will have to replace the author name with your author handle):

aea fingerprint skill fetchai/http_echo:0.21.6\n

Moreover, we need to tell to the http_server connection to what skill the HTTP requests should be forwarded. In our case, this is the http_echo that you have just scaffolded. Its public id will be <your-author-name>/http_echo:0.1.0.

aea config set vendor.fetchai.connections.http_server.config.target_skill_id \"$(aea config get agent.author)/http_echo:0.1.0\" 

You can now run the AEA:

aea run\n

In a separate terminal, you can create a client and communicate with the server:

import requests\nresponse = requests.get('http://127.0.0.1:8000')\nresponse.status_code\n# >>> 404\n# we receive a not found since the path is not available in the api spec\nresponse = requests.get('http://127.0.0.1:8000/pets')\nresponse.status_code\n# >>> 200\nresponse.content\n# >>> b'{\"tom\": {\"type\": \"cat\", \"age\": 10}}'\nresponse = requests.post('http://127.0.0.1:8000/pets')\nresponse.status_code\n# >>> 200\nresponse.content\n# >>> b''\n
"},{"location":"aea-framework-documentation/identity/","title":"Identity","text":"

Note

This section is incomplete and will soon be updated.

The AEAs currently use the addresses associated with their private-public key pairs to identify themselves.

To learn how to generate a private-public key pair check out the relevant CLI commands .

To learn more about public-key cryptography check out Wikipedia.

An AEA can provide evidence of its identity using third-party solutions. We have implemented a demo using Aries Hyperledger Cloud Agent which is available here and another demo using Yoti which is available here.

"},{"location":"aea-framework-documentation/install/","title":"Installation","text":"

Platforms

The AEA framework can be used on Windows, Ubuntu/Debian and MacOS.

"},{"location":"aea-framework-documentation/install/#system-requirements","title":"System Requirements","text":"
  1. You need Python 3.8, 3.9 or 3.10 on your system.
  2. GCC installation is also required:

    UbuntuMacOS X (with Homebrew)Windows (with choco)
    apt-get install gcc\n
    brew install gcc\n
    choco install mingw\n
Tips
  • Ubuntu/Debian: install Python headers, depending on the Python version you have installed on your machine. For example for Python 3.8:

    sudo apt-get install python3.8-dev\n
  • Windows: install tools for Visual Studio.

"},{"location":"aea-framework-documentation/install/#alternatively-use-docker","title":"Alternatively: Use Docker","text":"

We also provide a Docker image with all the needed dependencies.

  1. Pull the image:

    docker pull fetchai/aea-user:latest\n
  2. Run the image with your current local directory mounted as a docker volume. This allows you to keep your agents local while working on them from within the docker container:

    Linux and MacOsWindows
    docker run -it -v $(pwd):/agents --workdir=/agents fetchai/aea-user:latest\n
    docker run -it -v %cd%:/agents --workdir=/agents fetchai/aea-user:latest\n

Once successfully logged into the docker container, you can follow the rest of the guide the same way as if not using docker.

"},{"location":"aea-framework-documentation/install/#for-agent-development","title":"For Agent Development","text":""},{"location":"aea-framework-documentation/install/#preliminaries","title":"Preliminaries","text":"
  1. Create a new working directory. Let's call it my_aea_projects. This is where you will create your agent projects.

  2. Inside my_aea_projects, add an empty directory called packages. This is a local registry for your agents' components.

You should now have the following directory structure:

my_aea_projects\n\u2514\u2500\u2500 packages\n

Alternatively, clone a template repo:

Instead of the above, you can clone the template repo as described in Approach 1 in the development setup guide.

"},{"location":"aea-framework-documentation/install/#virtual-environment","title":"Virtual Environment","text":"

Unless you are using the docker image, we highly recommend using a virtual environment so that your setup is isolated from the rest of your system. This prevents clashes and ensures consistency across dependencies.

You can use any common virtual environment manager for Python, such as pipenv and poetry. If you do not have either, install one.

Once installed, create a new virtual environment in the my_aea_projects directory and enter it:

pipenvpoetry

Use any Python version supported in the command:

pipenv --python 3.9 && pipenv shell\n

poetry init -n && poetry shell\n
"},{"location":"aea-framework-documentation/install/#installation_1","title":"Installation","text":"

The latest version of the Python implementation of the AEA Framework is:

Note

If you are upgrading your AEA project from a previous version of the AEA framework, make sure you check out the upgrading notes.

"},{"location":"aea-framework-documentation/install/#using-pip","title":"Using pip","text":"

Install the AEA framework using pip:

bash/windowszsh
pip install aea[all]\n
pip install 'aea[all]'\n
Troubleshooting

To ensure no cache is used, add --force --no-cache-dir to the installation command.

"},{"location":"aea-framework-documentation/install/#using-pipx","title":"Using pipx","text":"

Install the AEA framework using pipx:

pipx install aea[all]\n
"},{"location":"aea-framework-documentation/install/#for-contributing-to-the-aea-framework","title":"For Contributing to the AEA Framework","text":"

To contribute to the development of the framework or related tools (e.g. ACN), please refer to the Contribution and Development guides in our GitHub repository.

"},{"location":"aea-framework-documentation/install/#other-tools-you-might-need","title":"Other Tools You Might Need","text":"

Depending on what you want to do, you might need extra tools on your system:

  • To use the Agent Communication Network (ACN) for peer-to-peer communication between agents (e.g. using the fetchai/p2p_libp2p connection) you will need Golang 1.14.2 or higher.
  • The framework uses Google Protocol Buffers for message serialization. If you want to develop protocols, install the protobuf compiler on your system. The version you install must match the protobuf library installed with the project (see pyproject.toml).
  • To update fingerprint hashes of packages, you will need the IPFS daemon.
"},{"location":"aea-framework-documentation/interaction-protocol/","title":"How AEAs Talk to Each Other - Interaction Protocols","text":"

Although one can imagine scenarios where single AEAs pursue their goals in isolation without interacting with other AEAs, there is no doubt that by working together, AEAs have the potential of achieving much more, especially when taking into account agents' heterogeneity, specialisations, and differing and often complimentary local views of the environment.

Interactions in the AEA world are in the form of communication. This is influenced by established practices in the field of multi-agent systems and the prominent speech-act theory which suggests that a communicative expression is not only about transferring information from the speaker to the hearer, but that there may be meanings and commitments beyond the statement's appearance. Therefore, speech may more suitably be considered as action. For example, \"I hereby appoint you as chairman\" is not just a sequence of words, but an action done by the speaker with wide-ranging consequences for the hearer and any other audience to that sentence.

Interaction protocols are thus possible communication scenarios between agents or agent components (specifically, skills and connections).

There are multiple types of interactions an AEA can have:

  • AEA-to-AEA interactions. You can find some examples in the demo section.

  • Interactions between an AEA's internal components.

Usually, an interaction involves three types of framework packages: skills, protocols and connections.

"},{"location":"aea-framework-documentation/interaction-protocol/#examples","title":"Examples","text":""},{"location":"aea-framework-documentation/interaction-protocol/#example-1-negotiation","title":"Example 1: Negotiation","text":"

The generic buyer/seller skills use the fetchai/fipa protocol which defines the negotiation dialogue between two AEAs. The fetchai/generic_buyer and fetchai/generic_seller skills implement specific strategies for engaging in such negotiations, by providing the logic for producing negotiation messages to be sent, handling negotiation messages received. The fetchai/p2p_libp2p connection is then used for connecting to the agent communication network enabling two AEAs with these skills to deliver negotiation messages to each other.

"},{"location":"aea-framework-documentation/interaction-protocol/#example-2-aea-web-client","title":"Example 2: AEA <> Web Client","text":"

In the http connection guide we demonstrate how an AEA with a http server connection (e.g. fetchai/http_server) receives http payloads from web clients, translates them to messages conforming with the fetchai/http protocol and passes it to a skill (e.g. fetchai/http_echo) to process. The fetchai/http protocol in this case is used for communication between the connection and the skill.

"},{"location":"aea-framework-documentation/interaction-protocol/#example-3-aea-3rd-party-server","title":"Example 3 : AEA <> 3rd Party Server","text":"

The fetchai/http_client connection can be used to make requests to third party servers. In this case, a skill containing the logic for the production of http requests would create messages conforming with the fetchai/http protocol and sends it to the fetchai/http_client connection which in turn translates it into http payload and sends it to the destination server.

Note that in general, third party SDKs can be wrapped in a connection and shared with other developers as a package. Often this also involves creating a custom protocol to enforce the type of interactions permitted between skills and the connection wrapping the SDK.

"},{"location":"aea-framework-documentation/interaction-protocol/#next-steps","title":"Next Steps","text":""},{"location":"aea-framework-documentation/interaction-protocol/#recommended","title":"Recommended","text":"

We recommend you continue with the next step in the 'Getting Started' series:

  • Trade between two AEAs
"},{"location":"aea-framework-documentation/interaction-protocol/#relevant-deep-dives","title":"Relevant Deep-Dives","text":"

Most AEA development focuses on developing the Skills and Protocols necessary for an AEA to deliver against its economic objectives and implement interaction protocols.

Understanding Protocols is core to developing your own agent. You can learn more about the Protocols agents use to communicate with each other and how they are created in the following section:

  • Protocols

Most of an AEA developer's time is spent on Skill development. Skills are the core business logic components of an AEA. Check out the following guide to learn more:

  • Skills

In most cases, one of the available Connection packages can be used. Occasionally, you might develop your own Connection:

  • Connections
"},{"location":"aea-framework-documentation/known-limits/","title":"Known Limitations","text":"

The AEA framework makes a multitude of tradeoffs.

Here we present an incomplete list of known limitations:

  • The AEABuilder checks the consistency of packages at the add stage. However, it does not currently check the consistency again at the load stage. This means, if a package is tampered with after it is added to the AEABuilder then these inconsistencies might not be detected by the AEABuilder.

  • The AEABuilder assumes that packages with public ids of identical author and package name have a matching version. As a result, if a developer uses a package with matching author and package name but different version in the public id, then the AEABuilder will not detect this and simply use the last loaded package.

  • The order in which setup and teardown are called on the skills, and act is called on the behaviours, is not guaranteed. Skills should be designed to work independently. Where skills use the shared_context to exchange information they must do so safely.

"},{"location":"aea-framework-documentation/language-agnostic-definition/","title":"Language Agnostic Definition","text":"

Currently, there is an implementation of the AEA framework in Python which enables the development of AEAs in Python, and allows AEAs which are built with it to run.

However, AEAs can be developed in different programming languages. This is further backed by the idea that agent-based solutions are suited for multi-stakeholder environments where the different AEAs may be developed independently of one another, resulting in heterogeneous systems.

This means that in principle, there could be different implementations of the AEA framework, in various programming languages and for different platforms. However, to ensure that AEAs under any implementation are compatible with one another and able to interact, they must satisfy specific definitions. In this page, we compile a set of definitions which any AEA independent of its implementation must satisfy in order to be able to interact with other AEAs.

An AEA, in technical terms, must satisfy the following requirements:

  • It MUST be capable of receiving and sending Envelopes which satisfy the following protobuf schema:

    syntax = \"proto3\";\npackage aea.base.v0_1_0;\nmessage Envelope{\nstring to = 1;\nstring sender = 2;\nstring protocol_id = 3;\nbytes message = 4;\nstring uri = 5;\n}\n

    The format for the above fields are as follows:

    • to and sender: an address derived from the private key of a secp256k1-compatible elliptic curve
    • protocol_id: this must match a defined regular expression (see below)
    • message: a bytes string representing a serialized message in the specified protocol
    • URI: follows this syntax
  • It MUST implement each protocol's message with the required meta-fields:

    syntax = \"proto3\";\npackage aea.base.v0_1_0;\nimport \"google/protobuf/struct.proto\";\nmessage DialogueMessage {\nint32 message_id = 1;\nstring dialogue_starter_reference = 2;\nstring dialogue_responder_reference = 3;\nint32 target = 4;\nbytes content = 5;\n}\nmessage Message {\noneof message {\ngoogle.protobuf.Struct body = 1;\nDialogueMessage dialogue_message = 2;\n}\n}\nmessage Envelope{\nstring to = 1;\nstring sender = 2;\nstring protocol_id = 3;\nbytes message = 4;\nstring uri = 5;\n}\n

    where content is replaced with the protocol specific content (see here for details).

  • It MUST implement protocols according to their specification (see here for details).

  • It SHOULD implement the fetchai/default:1.1.7 protocol which satisfies the following protobuf schema:

    syntax = \"proto3\";\npackage aea.fetchai.default.v1_0_0;\nmessage DefaultMessage{\n// Custom Types\nmessage ErrorCode{\nenum ErrorCodeEnum {\nUNSUPPORTED_PROTOCOL = 0;\nDECODING_ERROR = 1;\nINVALID_MESSAGE = 2;\nUNSUPPORTED_SKILL = 3;\nINVALID_DIALOGUE = 4;\n}\nErrorCodeEnum error_code = 1;\n}\n// Performatives and contents\nmessage Bytes_Performative{\nbytes content = 1;\n}\nmessage Error_Performative{\nErrorCode error_code = 1;\nstring error_msg = 2;\nmap<string, bytes> error_data = 3;\n}\nmessage End_Performative{\n}\noneof performative{\nBytes_Performative bytes = 5;\nEnd_Performative end = 6;\nError_Performative error = 7;\n}\n}\n
  • The protocol id MUST match the following regular expression: ^([a-zA-Z_][a-zA-Z0-9_]{0,127})/([a-zA-Z_][a-zA-Z0-9_]{0,127})(:((any|latest|((0|[1-9]\\d*))\\.((0|[1-9]\\d*))\\.((0|[1-9]\\d*))(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)))?$

  • It is recommended that it processes Envelopes asynchronously. Note, the specification regarding the processing of messages does not impose any particular implementation, and the AEA can be designed to process envelopes either synchronously and asynchronously. However, asynchronous message handling enables the agent to be more responsive and scalable in maintaining many concurrent dialogues with its peers.
  • It MUST have an identity in the form of, at a minimum, an address derived from a public key and its associated private key (where the elliptic curve must be of type SECP256k1).
  • It SHOULD implement handling of errors using the fetchai/default:1.1.7 protocol. The protobuf schema is given above.
  • It MUST implement the following principles when handling messages:
    • It MUST ALWAYS handle incoming envelopes/messages and NEVER raise an exception when decoding and validating the message. This ensures another AEA cannot cause the agent to fail by sending a malicious envelope/message.
    • It MUST NEVER handle outgoing messages and ALWAYS raise an exception when validating the message. An exception implies that the handler is resolving a bug in the implementation.

Note

Additional constraints will be added soon!

"},{"location":"aea-framework-documentation/ledger-integration/","title":"Ledger & Crypto APIs","text":"

In this section, we show you how to integrate the AEA with the Fetch.ai and third-party ledgers.

"},{"location":"aea-framework-documentation/ledger-integration/#ledger-support","title":"Ledger Support","text":"

For a ledger to be considered supported in the framework, three abstract base classes need to be implemented:

  • the LedgerApi class wraps the API to talk to the ledger and its helper methods
  • the Crypto class wraps the API to perform cryptographic operations for the relevant ledger
  • the FaucetApi class wraps the API to talk to a faucet on a testnet

These three classes have their own registries, which allow the developer to import the relevant object where needed.

"},{"location":"aea-framework-documentation/ledger-integration/#ledger-plug-in-architecture","title":"Ledger Plug-in Architecture","text":"

The AEA framework provides a plug-in mechanism to support ledger functionalities in an easily extendable way. At import time, the framework will load all the crypto plug-ins available in the current Python environment.

A crypto plug-in is a Python package which declares some specific setuptools \"entry points\" in its setup.py script. In particular, there are three types of entry points the framework looks up:

  • aea.ledger_apis, which points to instantiable classes implementing the LedgerApi interface;
  • aea.cryptos, which points to instantiable classes implementing the Crypto interface;
  • aea.faucet_apis, which points to instantiable classes implementing the FaucetApi interface.

This is an example of setup.py script for a ledger plug-in aea-ledger-myledger:

# sample ./setup.py file\nfrom setuptools import setup\nsetup(\nname=\"aea-ledger-myledger\",\npackages=[\"aea_ledger_myledger\"],\n# plugins must depend on 'aea'  \ninstall_requires=[\"aea\"], # add other dependencies...\n# the following makes a plugin available to aea\nentry_points={\n\"aea.cryptos\": [\"myledger = aea_ledger_myledger:MyLedgerCrypto\"],\n\"aea.ledger_apis\": [\"myledger = aea_ledger_myledger:MyLedgerApi\"],\n\"aea.faucet_apis\": [\"myledger = aea_ledger_myledger:MyLedgerFaucetApi\"],\n},\n# PyPI classifier for AEA plugins\nclassifiers=[\"Framework :: AEA\"],\n)\n

By convention, such plug-in packages should be named aea-ledger-${LEDGER_ID}, and the importable package name aea_ledger_${LEDGER_ID}. In the example above, the package name is aea-ledger-myledger, and the importable package name is aea_ledger_myledger.

You can search for AEA ledger plug-ins on PyPI: https://pypi.org/search/?q=aea-ledger

"},{"location":"aea-framework-documentation/ledger-integration/#maintained-plug-ins","title":"Maintained Plug-ins","text":"

At the moment, the framework natively supports the following three ledgers:

  • Fetch.ai: PyPI package: aea-ledger-fetchai, and source code.
  • Ethereum: PyPI package: aea-ledger-ethereum, and source code.
  • Cosmos: PyPI package: aea-ledger-cosmos, and source code.

However, support for additional ledgers can be added to the framework at runtime.

"},{"location":"aea-framework-documentation/ledger-integration/#examples","title":"Examples","text":"
  • Examples of how to interact with the crypto registry:
from aea.crypto.registries import crypto_registry, make_crypto, register_crypto\n# by default we can use the native cryptos\nfetchai_crypto = make_crypto(\"fetchai\")\n# we can check what cryptos are registered\ncrypto_registry.supported_ids\n# we can also add a new crypto to the registry\nregister_crypto(id_=\"my_ledger_id\", entry_point=\"some.dotted.path:MyLedgerCrypto\")\n# and then make it anywhere\nmy_ledger_crypto = make_crypto(\"my_ledger_id\")\n
  • Examples of how to interact with the ledger API registry:
from aea.crypto.registries import ledger_apis_registry, make_ledger_api, register_ledger_api\n# by default we can use the native ledger apis\nCONFIG = {\"network\": \"testnet\"}\nfetchai_ledger_api = make_ledger_api(\"fetchai\", **CONFIG)\n# we can check what ledger apis are registered\nledger_apis_registry.supported_ids\n# we can also add a new ledger api to the registry\nregister_ledger_api(id_=\"my_ledger_id\", entry_point=\"some.dotted.path:MyLedgerApi\")\n# and then make it anywhere\nmy_ledger_api = make_ledger_api(\"my_ledger_id\")\n
  • Examples of how to interact with the faucet API registry:
from aea.crypto.registries import faucet_apis_registry, make_faucet_api, register_faucet_api\n# by default we can use the native faucet apis\nCONFIG = dict(poll_interval=1.0)\nfetchai_faucet_api = make_faucet_api(\"fetchai\", **CONFIG)\n# we can check what faucet apis are registered\nfaucet_apis_registry.supported_ids\n# we can also add a new faucet api to the registry\nregister_faucet_api(id_=\"my_ledger_id\", entry_point=\"some.dotted.path:MyLedgerFaucetApi\")\n# and then make it anywhere\nmy_faucet_api = make_faucet_api(\"my_ledger_id\")\n

The framework wraps all LedgerApi classes and exposes them in the LedgerApis classes. The framework also wraps the crypto APIs to create identities on both ledgers and exposes them in the Wallet.

The separation between the Crypto and LedgerApi is fundamental to the framework design. In particular, the object which holds the private key is separated from the object which interacts with the ledger. This design pattern is repeated throughout the framework: the decision maker is the only entity with access to the AEA's Wallet whilst LedgerApis are accessible by all skills.

"},{"location":"aea-framework-documentation/ledger-integration/#stargate-world-fetchai-testnet-for-agents","title":"Stargate World - Fetch.ai Testnet for Agents","text":"

Stargate World is our stable, public testnet for the Fetch Ledger v2. As such, most developers will be interacting with this testnet. This is specifically designed and supported for AEA development.

Parameter Value Chain ID dorado-1 Denomination atestfet Decimals 18 Version v0.8.x RPC Endpoint https://rpc-dorado.fetch.ai:443 REST Endpoint https://rest-dorado.fetch.ai:443 Block Explorer https://explore-dorado.fetch.ai Token Faucet Use block explorer

You can access more details on docs section.

The configurations can be specified for the fetchai/ledger:0.21.5 connection.

"},{"location":"aea-framework-documentation/ledger-integration/#cosmwasm-supporting-chains","title":"CosmWasm Supporting Chains","text":"

The Fetch.ai networks use CosmWasm for smart contract support.

"},{"location":"aea-framework-documentation/limits/","title":"Limitations of v1","text":"

This document describes some of the limitations of v1 of the AEA framework and tradeoffs made in its design.

"},{"location":"aea-framework-documentation/limits/#rejected-ideas","title":"Rejected Ideas","text":""},{"location":"aea-framework-documentation/limits/#handlers-implemented-as-behaviours","title":"Handlers Implemented as Behaviours","text":"

Handlers can be considered a special cases of a \"behaviour that listens for specific events to happen\".

One could implement Handler classes in terms of Behaviours, after having implemented the feature that behaviours can be activated after an event happens (e.g. receiving a message of a certain protocol).

This was rejected in favour of a clear separation of concerns, and to avoid purely reactive (handlers) and proactive (behaviours) components to be conflated into one concept. The proposal would also add complexity to behaviour development.

"},{"location":"aea-framework-documentation/limits/#multiple-versions-of-the-same-package","title":"Multiple Versions of the Same Package","text":"

The framework does not allow for the usage of multiple versions of the same package in a given project.

Although one could re-engineer the project to allow for this, it does introduce significant additional complexities. Furthermore, Python modules are by design only allowed to exist as one version in a given process. Hence, it seems sensible to maintain this approach in the AEA.

"},{"location":"aea-framework-documentation/limits/#potential-extensions-considered-yet-not-decided","title":"Potential Extensions, Considered yet not Decided","text":""},{"location":"aea-framework-documentation/limits/#alternative-skill-design","title":"Alternative Skill Design","text":"

For very simple skills, the splitting of skills into Behaviour, Handler, Model and Task classes can add unnecessary complexity to the framework and a counter-intuitive responsibility split. The splitting also implies the framework needs to introduce the SkillContext object to allow for access to data across the skill. Furthermore, the framework requires implementing all functionality in SkillComponent classes Handler, Behaviour or Model. This approach is consistent and transparent, however it creates a lot of boilerplate code for simple skills.

Hence, for some use cases it would be useful to have a single Skill class with abstract methods setup, act, handle and teardown. Then the developer can decide how to split up their code.

class SkillTemplate(SimpleSkill):\nprotocol_ids: Optional[List[PublicId]] = None\ndef setup():\n# setup skill\ndef handle(message: Message):\n# handle messages\ndef act():\nfor b in behaviours:\nb.act()\ndef teardown():\n# teardown skill\n

Alternatively, we could use decorators to let a developer define whether a function is part of a handler or behaviour. That way, a single file with a number of functions could implement a skill. (Behind the scenes this would utilise a number of virtual Behaviour and Handler classes provided by the framework).

The downside of this approach is that it does not advocate for much modularity on the skill level. Part of the role of a framework is to propose a common way to do things. The above approach can cause for a larger degree of heterogeneity in the skill design which makes it harder for developers to understand each other's code.

The separation between all four base classes does exist both in convention and at the code level. Handlers deal with skill-external events (messages), behaviours deal with scheduled events (ticks), models represent data and tasks are used to manage long-running business logic.

By adopting strong convention around skill development we allow for the framework to take a more active role in providing guarantees. E.g. handlers' and behaviours' execution can be limited to avoid them being blocking, models can be persisted and recreated, tasks can be executed with different task backends. The opinionated approach is thought to allow for better scaling.

"},{"location":"aea-framework-documentation/limits/#further-modularity-for-skill-level-code","title":"Further Modularity for Skill Level Code","text":"

Currently, we have three levels of modularity:

  • PyPI packages
  • framework packages: protocols, contracts, connections and skills
  • framework plugins: CLI, ledger

We could consider having a fourth level: common behaviours, handlers, models exposed as modules which can then speed up skill development.

"},{"location":"aea-framework-documentation/limits/#promise-pattern","title":"\"promise\" Pattern","text":"

Given the asynchronous nature of the framework, it is often hard to implement reactions to specific messages, without making a \"fat\" handler. Take the example of a handler for a certain type of message A for a certain protocol p. The handler for protocol p would look something like this:

class PHandler:\n...\ndef handle(msg):\nif message type is A:\nself._handle_a(msg)\n

However, it could be helpful to overwrite this handler reaction with another callback (e.g. consider this in context):

# callable that handles the reply\ndef my_callback(msg):\n# handle reply\nself.context.outbox.put_message(message, handler_func=my_callback, failure_func=...)\n

This feature would introduce additional complexity for the framework to correctly wire up the callbacks and messages with the dialogues.

"},{"location":"aea-framework-documentation/limits/#cli-using-standard-lib","title":"CLI using Standard Lib","text":"

Removing the click dependency from the CLI would further reduce the dependencies in the AEA framework which is overall desirable.

"},{"location":"aea-framework-documentation/limits/#metadata-vs-configurations","title":"Metadata vs Configurations","text":"

The current approach uses yaml files to specify both metadata and component configuration. It would be desirable to introduce the following separation:

  • package metadata
  • package default developer configuration
  • package default user configuration

A user can only configure a subset of the configuration. The developer should be able to define these constraints for the user. Similarly, a developer cannot modify all fields in a package, some of them are determined by the framework.

"},{"location":"aea-framework-documentation/limits/#configuring-agent-goal-setup","title":"Configuring Agent Goal Setup","text":"

By default, the agent's goals are implicitly defined by its skills and the configurations thereof. This is because the default decision maker signs every message and transaction presented to it.

It is already possible to design a custom decision maker. However, more work needs to be done to understand how to improve the usability and configuration of the decision maker. In this context different types of decision makers can be implemented for the developer/user.

"},{"location":"aea-framework-documentation/limits/#connection-status-monitoring","title":"Connection Status Monitoring","text":"

Currently, connections are responsible for managing their own status after they have been \"connected\" by the Multiplexer. Developers writing connections must take care to properly set its connection status at all times and manage any disconnection. It would potentially be desirable to offer different policies to deal with connection problems on the multiplexer level:

  • disconnect one, keep others alive
  • disconnect all
  • try to reconnect indefinitely
"},{"location":"aea-framework-documentation/limits/#agent-snapshots-on-teardown-or-error","title":"Agent Snapshots on Teardown or Error","text":"

Currently, the developer must implement snapshots on the component level. It would be desirable if the framework offered more help to persist the agent state on teardown or error.

"},{"location":"aea-framework-documentation/limits/#dialogues-management","title":"Dialogues Management","text":"

The current implementation of Dialogues is verbose. Developers often need to subclass Dialogues and Dialogue classes. More effort can be made to simplify and streamline dialogues management.

"},{"location":"aea-framework-documentation/limits/#instantiate-multiple-instances-of-the-same-class-of-skillcomponent","title":"Instantiate Multiple Instances of the Same Class of SkillComponent","text":"

Currently, configuration and metadata of a package are conflated making it not straightforward to run one package component with multiple sets of configuration. It could be desirable to configure an agent to run a given package with multiple different configurations.

This feature could be problematic with respect to component-to-component messaging which currently relies on component ids, which are bound to the package and not its instance.

"},{"location":"aea-framework-documentation/limits/#containerized-agents","title":"Containerized Agents","text":"

Agent management, especially when many of them live on the same host, can be cumbersome. The framework should provide more utilities for these large-scale use cases. But a proper isolation of the agent environment is something that helps also simple use cases.

A new software architecture, somehow inspired to the Docker system. The CLI only involves the initialization of the building of the agent (think of it as the specification of the Dockerfile: the Agentfile), but the actual build and run are done by the AEA engine, a daemon process analogous of the Docker Engine, which exposes APIs for these operations.

Users and developers would potentially like to run many AEAs of different versions and with differences in the versions of their dependencies. It is not possible to import different versions of the same Python (PyPI) package in the same process in a clean way. However, in different processes this is trivial with virtual environments. It would be desirable to consider this in the context of a container solution for agents.

"},{"location":"aea-framework-documentation/limits/#dependency-light-version-of-the-aea-framework","title":"Dependency Light Version of the AEA Framework","text":"

The v1 of the Python AEA implementation makes every effort to minimise the amount of third-party dependencies. However, some dependencies remain to lower development time.

It would be desirable to further reduce the dependencies, and potentially have an implementation that only relies on the Python standard library.

This could be taken further, and a reduced spec version for micropython could be designed.

"},{"location":"aea-framework-documentation/limits/#compiled-aea","title":"Compiled AEA","text":"

Python is not a compiled language. However, various projects attempt this, e.g. Nuitka and it would be desirable to explore how useful and practical this would be in the context of AEA.

"},{"location":"aea-framework-documentation/limits/#did-integration","title":"DID Integration","text":"

It would be great to integrate DID in the framework design, specifically identification of packages (most urgently protocols). Other projects and standards worth reviewing in the context (in particular with respect to identity):

  • ERC 725: Ethereum Identity Standard and here.
  • ERC 735: Claim Holder
"},{"location":"aea-framework-documentation/limits/#optimise-protocol-schemas-and-messages","title":"Optimise Protocol Schemas and Messages","text":"

The focus of protocol development was on extensibility and compatibility, not on optimisation. For instance, the dialogue references use inefficient string representations.

"},{"location":"aea-framework-documentation/limits/#constraints-on-primitive-types-in-protocols","title":"Constraints on Primitive Types in Protocols","text":"

The protocol generator currently does not support custom constraints. The framework could add support for custom constraints for the protocol generator and specification.

There are many types of constraints that could be supported in specification and generator. One could perhaps add support based on the popularity of specific constraints from users/developers.

Example constraints:

  • strings following specific regular expression format (e.g. all lower case, any arbitrary regex format)
  • max number of elements on lists/sets
  • keys in one dict type be equal to keys in another dict type
  • other logical constraints, e.g. as supported in ontological languages
  • support for bounds (i.e. min, max) for numerical types (i.e. int and float) in protocol specification.

Example syntax:

  • pt:int[0, ]
  • pt:float[1.0, 10.0]
  • pt:int[-1000, 1000]
  • pt:float[, 0]

This would automatically enable support for signed/unsigned int and float. This syntax would allow for unbounded positive/negative/both, or arbitrary bounds to be placed on numerical types.

Currently, the developer has to specify a custom type to implement any constraints on primitive types.

"},{"location":"aea-framework-documentation/limits/#sub-protocols-multi-party-interactions","title":"Sub-protocols & Multi-Party Interactions","text":"

Protocols can be allowed to depend on each other. Similarly, protocols might have multiple parties.

Furthermore, a turn-taking function that specifies who's turn it is at any given point in the dialogue could be added.

Then the current fipa setup is a specific case of turn-taking where the turn shifts after a player sends a single move (unique-reply). But generally, it does not have to be like this. Players could be allowed to send multiple messages until the turn shifts, or until they send specific speech-acts (multiple-replies).

"},{"location":"aea-framework-documentation/limits/#timeouts-in-protocols","title":"Timeouts in Protocols","text":"

Protocols currently do not implement the concept of timeouts. We leave it to the skill developer to implement any time-specific protocol rules.

"},{"location":"aea-framework-documentation/limits/#framework-internal-messages","title":"Framework Internal Messages","text":"

The activation/deactivation of skills and addition/removal of components is implemented in a \"passive\" way - the skill posts a request in its skill context queue (in the case of new behaviours), or it just sets a flag (in case of activation/deactivation of skills).

One could consider that a skill can send requests to the framework, via the internal protocol, to modify its resources or its status. The DecisionMaker or the Filter can be the components that take such actions.

This is a further small but meaningful step toward an actor-based model for agent internals.

"},{"location":"aea-framework-documentation/limits/#ledger-transaction-management","title":"Ledger Transaction Management","text":"

Currently, the framework does not manage any aspect of submitting multiple transactions to the ledgers. This responsibility is left to skills. Additionally, the ledger APIs/contract APIs take the ledger as a reference to determine the nonce for a transaction. If a new transaction is sent before a previous transaction has been processed then the nonce will not be incremented correctly for the second transaction. This can lead to submissions of multiple transactions with the same nonce, and therefore failure of subsequent transactions.

A naive approach would involve manually incrementing the nonce and then submitting transactions into the pool with the correct nonce for eventual inclusion. The problem with this approach is that any failure of a transaction will cause none of the subsequent transactions to be processed for some ledgers (https://ethereum.stackexchange.com/questions/2808/what-happens-when-a-transaction-nonce-is-too-high). To recover from a transaction failure not only the failed transaction would need to be handled, but potentially also all subsequent transactions. It is easy to see that logic required to recover from a transaction failure early in a sequence can be arbitrarily complex (involving potentially new negotiations between agents, new signatures having to be generated etc.).

A further problem with the naive approach is that it (imperfectly) replicates the ledger state (with respect to (subset of state of) a specific account).

A simple solution looks as follows: each time a transaction is constructed (requiring a new nonce) the transaction construction is queued until all previous transactions have been included in the ledger or failed. This way, at any one time the agent has only at most one transaction pending with the ledger. Benefits: simple to understand and maintain, transaction only enter the mempool when they are ready for inclusion which has privacy benefits over submitting a whole sequence of transaction at once. Downside: at most one transaction per block.

This approach is currently used and implemented across all the reference skills.

Related, the topic of latency in transactions. State channels provide a solution. E.g. Perun. There could also be an interesting overlap with our protocols here.

"},{"location":"aea-framework-documentation/limits/#unsolved-problems-in-multiplexer-agentloop-interplay","title":"Unsolved Problems in Multiplexer - AgentLoop Interplay","text":"

Problem 1: connection generates too many messages in a short amount of time, that are not consumed by the multiplexer Solution: Can be solved by slowing down connections receive method called, controlled by the inbox messages amount Side effects: Most of the connections should have an internal queue because there is no synchronization between internal logic and multiplexer connection receive calls.

Problem 2: the send method can take a long time (because send retries logic in connection) Solution: Currently, we apply timeouts on send. Other solutions could be considered, like parallelization.

Problem 3: too many messages are produced by a skill. Solution: Raise an exception on outbox is full or slow down agent loop?

"},{"location":"aea-framework-documentation/limits/#acn","title":"ACN","text":""},{"location":"aea-framework-documentation/limits/#agent-mobility-on-acn","title":"Agent Mobility on ACN","text":"

If a peer-client or full client switches peer, then the DHT is not updated properly at the moment under certain conditions.

"},{"location":"aea-framework-documentation/limits/#mailbox-connection","title":"Mailbox Connection","text":"

The two available connections p2p_libp2p and p2p_libp2p_client imply that the agent is continuously connected and therefore must have uninterrupted network access and the resources to maintain a connection.

For more lightweight implementations, a mailbox connection is desirable, as outlined in the ACN documentation.

"},{"location":"aea-framework-documentation/logging/","title":"Logging","text":"

The AEA framework supports flexible logging capabilities with the standard Python logging library.

In this tutorial, we configure logging for an AEA.

First, create your AEA.

aea create my_aea\ncd my_aea\n

The aea-config.yaml file should look like this.

agent_name: my_aea\nauthor: fetchai\nversion: 0.1.0\ndescription: ''\nlicense: Apache-2.0\naea_version: 0.6.0\nfingerprint: {}\nfingerprint_ignore_patterns: []\nconnections:\n- fetchai/stub:0.21.3\ncontracts: []\nprotocols:\n- fetchai/default:1.1.7\nskills:\n- fetchai/error:0.18.6\ndefault_connection: fetchai/stub:0.21.3\ndefault_ledger: fetchai\nrequired_ledgers:\n- fetchai\nlogging_config:\ndisable_existing_loggers: false\nversion: 1\nprivate_key_paths: {}\n

By updating the logging_config section, you can configure the loggers of your application.

The format of this section is specified in the logging.config module.

At this section you'll find the definition of the configuration dictionary schema.

Below is an example of the logging_config value.

logging_config:\nversion: 1\ndisable_existing_loggers: False\nformatters:\nstandard:\nformat: '%(asctime)s [%(levelname)s] %(name)s: %(message)s'\nhandlers:\nlogfile:\nclass: logging.FileHandler\nformatter: standard\nlevel: DEBUG\nfilename: logconfig.log\nconsole:\nclass: logging.StreamHandler\nformatter: standard\nlevel: DEBUG\nloggers:\naea:\nhandlers:\n- logfile\n- console\nlevel: DEBUG\npropagate: False\n

This configuration will set up a logger with name aea. It prints both on console and on file with a format specified by the standard formatter.

"},{"location":"aea-framework-documentation/logging/#streaming-to-browser","title":"Streaming to Browser","text":"

It is possible to configure the AEA to stream logs to a browser.

First, add the following configuration to your AEA:

logging_config:\nversion: 1\ndisable_existing_loggers: false\nformatters:\nstandard:\nformat: '%(asctime)s [%(levelname)s] %(name)s: %(message)s'\nhandlers:\nhttp:\nclass: logging.handlers.HTTPHandler\nformatter: standard\nlevel: INFO\nhost: localhost:5000\nurl: /stream\nmethod: POST\nloggers:\naea:\nhandlers:\n- http\nlevel: INFO\npropagate: false\n

Second, create a log server:

# -*- coding: utf-8 -*-\n\"\"\"A simple flask server to serve logs.\"\"\"\nimport datetime\nimport itertools\nimport queue\nfrom flask import Flask, Response, request, stream_with_context\ndef format_log(log_dict):\n\"\"\"Format a log record.\"\"\"\ndate = datetime.datetime.fromtimestamp(float(log_dict[\"created\"]))\nformatted_log = f\"[{date.isoformat()}] [{log_dict['levelname']}] {log_dict['name']}: {log_dict['msg']}\"\nreturn formatted_log\ndef create_app():\n\"\"\"Create Flask app for streaming logs.\"\"\"\nall_logs = []\nunread_logs = queue.Queue()\napp = Flask(__name__)\n@app.route(\"/\")\ndef index():\n\"\"\"Stream logs to client.\"\"\"\ndef generate():\n# stream old logs\ndiv = \"<div>{}</div>\"\nfor old_row in all_logs:\nyield div.format(old_row)\n# stream unread logs\nwhile True:\nrow = unread_logs.get()\nall_logs.append(row)\nyield f\"<div>{row}</div>\"\nrows = generate()\ntitle = \"<p>Waiting for logs...</p>\"\nreturn Response(stream_with_context(itertools.chain([title], rows)))\n@app.route(\"/stream\", methods=[\"POST\"])\ndef stream():\n\"\"\"Save log record from AEA.\"\"\"\nlog_record_formatted = format_log(dict(request.form))\nunread_logs.put(log_record_formatted)\nreturn {}, 200\napp.run()\nif __name__ == \"__main__\":\ncreate_app()\n

Save the script in a file called server.py, install flask with pip install flask and run the server with python server.py.

Third, run your AEA and visit localhost:5000 in your browser.

"},{"location":"aea-framework-documentation/message-routing/","title":"Message Routing","text":"

Message routing can be split up into the routing of incoming and outgoing Messages.

It is important to keep in mind that interaction protocols can be maintained between agents (agent to agent) and between components of the AEA (component to component). In the former case, the to/sender fields of the Envelope are agent addresses which must follow the address standard of agents, in the latter case they are component public ids. Crucially, both addresses must reference the same type: agent or component.

"},{"location":"aea-framework-documentation/message-routing/#incoming-messages","title":"Incoming Messages","text":"
  • Connections receive or create Envelopes which they deposit in the InBox
  • for agent-to-agent communication only, the Multiplexer keeps track of the connection_id via which the Envelope was received.
  • the AgentLoop picks Envelopes off the InBox
  • the AEA tries to decode the message; errors are handled by the ErrorHandler
  • Messages are dispatched based on two rules:

    1. checks if to field can be interpreted as skill_id, if so uses that together with the protocol_id to dispatch to the protocol's Handler in the specified Skill, else
    2. uses the protocol_id to dispatch to the protocol's Handler in all skills supporting the protocol.

Note

For agent-to-agent communication it is advisable to have a single skill implement a given protocol. Skills can then forward the messages via skill-to-skill communication to other skills where required. Otherwise, received agent-to-agent messages will be forwarded to all skills implementing a handler for the specified protocol and the developer needs to take care to handle them appropriately (e.g. avoid multiple replies to a single message).

"},{"location":"aea-framework-documentation/message-routing/#outgoing-messages","title":"Outgoing Messages","text":"
  • Skills deposit Messages in OutBox
  • OutBox constructs an Envelope from the Message
  • Multiplexer assigns messages to relevant Connection based on the following rules:

    1. Component to component messages are routed by their component_id
    2. Agent to agent messages are routed following four rules:
      1. checks if EnvelopeContext exists and specifies a Connection, if so uses that else
      2. checks which connection handled the last message from sender, if present uses that else
      3. checks if default routing is specified for the protocol_id referenced in the Envelope, if so uses that else
      4. sends to default Connection.
  • Connections can process Envelopes directly or encode them for transport to another agent.

"},{"location":"aea-framework-documentation/message-routing/#usage-of-the-envelopecontext","title":"Usage of the EnvelopeContext","text":"

The EnvelopeContext is used to maintain agent-to-agent communication only and is managed almost entirely by the framework. The developer can set the EnvelopeContext explicitly for the first message in a dialogue to achieve targeted routing to connections (see 2. for outgoing messages). This is relevant when the same agent can be reached via multiple connections.

The EnvelopeContext is not sent to another agent.

"},{"location":"aea-framework-documentation/ml-skills/","title":"ML Skills","text":"

The AEA ML (machine learning) skills demonstrate an interaction between two AEAs, one purchasing data from the other and training a machine learning model with it.

There are two types of AEAs:

  • The ml_data_provider which sells training data.
  • The ml_model_trainer which purchases data and trains a model
"},{"location":"aea-framework-documentation/ml-skills/#discussion","title":"Discussion","text":"

This demo aims to demonstrate the integration of a simple AEA with machine learning using the AEA framework. The ml_data_provider AEA provides some sample data and delivers to the client upon payment. Once the client receives the data, it trains a model. This process can be found in tasks.py. This demo does not utilize a smart contract. As a result, the ledger interaction is only for completing a transaction.

Since the AEA framework enables using third-party libraries from PyPI, we can directly reference any external dependencies. The aea install command installs all dependencies an AEA needs that is listed in one of its skills' YAML file.

"},{"location":"aea-framework-documentation/ml-skills/#communication","title":"Communication","text":"

This diagram shows the communication between the two AEAs.

    sequenceDiagram\n        participant ml_model_trainer\n        participant ml_data_provider\n        participant Search\n        participant Ledger\n\n        activate ml_model_trainer\n        activate ml_data_provider\n        activate Search\n        activate Ledger\n\n        ml_data_provider->>Search: register_service\n        ml_model_trainer->>Search: search\n        Search-->>ml_model_trainer: list_of_agents\n        ml_model_trainer->>ml_data_provider: call_for_terms\n        ml_data_provider->>ml_model_trainer: terms\n        ml_model_trainer->>Ledger: request_transaction\n        ml_model_trainer->>ml_data_provider: accept (incl transaction_hash)\n        ml_data_provider->>Ledger: check_transaction_status\n        ml_data_provider->>ml_model_trainer: data\n        loop train\n            ml_model_trainer->>ml_model_trainer: tran_model\n        end\n\n        deactivate ml_model_trainer\n        deactivate ml_data_provider\n        deactivate Search\n        deactivate Ledger
"},{"location":"aea-framework-documentation/ml-skills/#option-1-aea-manager-approach","title":"Option 1: AEA Manager Approach","text":"

Follow this approach when using the AEA Manager Desktop app. Otherwise, skip and follow the CLI approach below.

"},{"location":"aea-framework-documentation/ml-skills/#preparation-instructions","title":"Preparation Instructions","text":"
  • Install the AEA Manager.
  • Install Tensorflow
"},{"location":"aea-framework-documentation/ml-skills/#demo-instructions","title":"Demo Instructions","text":"

The following steps assume you have launched the AEA Manager Desktop app.

  1. Add a new AEA called ml_data_provider with public id fetchai/ml_data_provider:0.28.0.

  2. Add another new AEA called ml_model_trainer with public id fetchai/ml_model_trainer:0.29.0.

  3. Copy the address from the ml_model_trainer into your clip board. Then go to the Dorado block explorer and request some test tokens via Get Funds.

  4. Run the ml_data_provider AEA. Navigate to its logs and copy the multiaddress displayed.

  5. Navigate to the settings of the ml_model_trainer and under components > connection > fetchai/p2p_libp2p:0.22.0 update as follows (make sure to replace the placeholder with the multiaddress):

    {\n\"delegate_uri\": \"127.0.0.1:11001\",\n  \"entry_peers\": [\"REPLACE_WITH_MULTI_ADDRESS_HERE\"],\n  \"local_uri\": \"127.0.0.1:9001\",\n  \"log_file\": \"libp2p_node.log\",\n  \"public_uri\": \"127.0.0.1:9001\"\n}\n
  6. Run the ml_model_trainer.

In the AEA's logs, you should see the agents trading successfully, and the training agent training its machine learning model using the data purchased. The trainer keeps purchasing data and training its model until stopped.

"},{"location":"aea-framework-documentation/ml-skills/#option-2-cli-approach","title":"Option 2: CLI Approach","text":"

Follow this approach when using the aea CLI.

"},{"location":"aea-framework-documentation/ml-skills/#preparation-instructions_1","title":"Preparation Instructions","text":""},{"location":"aea-framework-documentation/ml-skills/#dependencies","title":"Dependencies","text":"
  • Follow the Preliminaries and Installation sections from the AEA quick start.
  • Install Tensorflow
"},{"location":"aea-framework-documentation/ml-skills/#demo-instructions_1","title":"Demo Instructions","text":""},{"location":"aea-framework-documentation/ml-skills/#create-data-provider-aea","title":"Create Data Provider AEA","text":"

First, fetch the data provider AEA:

aea fetch fetchai/ml_data_provider:0.32.5\ncd ml_data_provider\naea install\naea build\n
Alternatively, create from scratch:

The following steps create the data provider from scratch:

aea create ml_data_provider\ncd ml_data_provider\naea add connection fetchai/p2p_libp2p:0.27.5\naea add connection fetchai/soef:0.27.6\naea add connection fetchai/ledger:0.21.5\naea add skill fetchai/ml_data_provider:0.27.6\naea config set --type dict agent.dependencies \\\n'{\n  \"aea-ledger-fetchai\": {\"version\": \"<2.0.0,>=1.0.0\"}\n}'\naea config set agent.default_connection fetchai/p2p_libp2p:0.27.5\naea config set --type dict agent.default_routing \\\n'{\n  \"fetchai/ledger_api:1.1.7\": \"fetchai/ledger:0.21.5\",\n  \"fetchai/oef_search:1.1.7\": \"fetchai/soef:0.27.6\"\n}'\naea install\naea build\n
"},{"location":"aea-framework-documentation/ml-skills/#create-model-trainer-aea","title":"Create Model Trainer AEA","text":"

Then, fetch the model trainer AEA:

aea fetch fetchai/ml_model_trainer:0.33.5\ncd ml_model_trainer\naea install\naea build\n
Alternatively, create from scratch:

The following steps create the model trainer from scratch:

aea create ml_model_trainer\ncd ml_model_trainer\naea add connection fetchai/p2p_libp2p:0.27.5\naea add connection fetchai/soef:0.27.6\naea add connection fetchai/ledger:0.21.5\naea add skill fetchai/ml_train:0.29.6\naea config set --type dict agent.dependencies \\\n'{\n  \"aea-ledger-fetchai\": {\"version\": \"<2.0.0,>=1.0.0\"}\n}'\naea config set agent.default_connection fetchai/p2p_libp2p:0.27.5\naea config set --type dict agent.default_routing \\\n'{\n  \"fetchai/ledger_api:1.1.7\": \"fetchai/ledger:0.21.5\",\n  \"fetchai/oef_search:1.1.7\": \"fetchai/soef:0.27.6\"\n}'\naea install\naea build\n
"},{"location":"aea-framework-documentation/ml-skills/#add-keys-for-the-data-provider-aea","title":"Add Keys for the Data Provider AEA","text":"

First, create the private key for the data provider AEA based on the network you want to transact. To generate and add a private-public key pair for Fetch.ai Dorado use:

aea generate-key fetchai\naea add-key fetchai fetchai_private_key.txt\n

Next, create a private key used to secure the AEA's communications:

aea generate-key fetchai fetchai_connection_private_key.txt\naea add-key fetchai fetchai_connection_private_key.txt --connection\n

Finally, certify the key for use by the connections that request that:

aea issue-certificates\n
"},{"location":"aea-framework-documentation/ml-skills/#add-keys-and-generate-wealth-for-the-model-trainer-aea","title":"Add Keys and Generate Wealth for the Model Trainer AEA","text":"

The model trainer needs to have some wealth to purchase the data from the data provider.

First, create the private key for the model trainer AEA based on the network you want to transact. To generate and add a private-public key pair for Fetch.ai Dorado use:

aea generate-key fetchai\naea add-key fetchai fetchai_private_key.txt\n

Then, create some wealth for your model trainer based on the network you want to transact with. On the Fetch.ai Dorado network:

aea generate-wealth fetchai\n

Next, create a private key used to secure the AEA's communications:

aea generate-key fetchai fetchai_connection_private_key.txt\naea add-key fetchai fetchai_connection_private_key.txt --connection\n

Finally, certify the key for use by the connections that request that:

aea issue-certificates\n
"},{"location":"aea-framework-documentation/ml-skills/#run-both-aeas","title":"Run both AEAs","text":"

Run both AEAs from their respective terminals.

First, run the data provider AEA:

aea run\n

Once you see a message of the form To join its network use multiaddr 'SOME_ADDRESS' take note of the address. (Alternatively, use aea get-multiaddress fetchai -c -i fetchai/p2p_libp2p:0.27.5 -u public_uri to retrieve the address.) This is the entry peer address for the local agent communication network created by the ML data provider.

Then, in the ML model trainer, run this command (replace SOME_ADDRESS with the correct value as described above):

aea config set --type dict vendor.fetchai.connections.p2p_libp2p.config \\\n'{\n  \"delegate_uri\": \"127.0.0.1:11001\",\n  \"entry_peers\": [\"SOME_ADDRESS\"],\n  \"local_uri\": \"127.0.0.1:9001\",\n  \"log_file\": \"libp2p_node.log\",\n  \"public_uri\": \"127.0.0.1:9001\"\n}'\n

This allows the model trainer to connect to the same local agent communication network as the data provider.

Then run the model trainer AEA:

aea run\n

You can see that the AEAs find each other, negotiate and eventually trade. After the trade, the model trainer AEA trains its ML model using the data it has purchased. This AEA keeps purchasing data and training its model until stopped.

"},{"location":"aea-framework-documentation/ml-skills/#cleaning-up","title":"Cleaning up","text":"

When you're finished, delete your AEAs:

cd ..\naea delete ml_data_provider\naea delete ml_model_trainer\n
"},{"location":"aea-framework-documentation/modes/","title":"Modes of Running an AEA","text":"

We can run an AEA in multiple modes thanks to the configurable design of the framework.

The AEA contains two runnable parts, the AgentLoop, which operates the skills, and the Multiplexer, which operates the connections. The AgentLoop can be configured to run in async or sync mode. The Multiplexer by default runs in async mode. The AEA itself, can be configured to run in async mode, if both the Multiplexer and AgentLoop have the same mode, or in threaded mode. The latter ensures that AgentLoop and Multiplexer are run in separate threads.

"},{"location":"aea-framework-documentation/multi-agent-manager/","title":"Multi Agent Manager","text":"

The MultiAgentManager allows managing multiple agent projects programmatically.

"},{"location":"aea-framework-documentation/multi-agent-manager/#setup","title":"Setup","text":"

We instantiate the manager by providing it with the working directory in which to operate and starting it:

import os\nfrom pathlib import Path\nfrom aea.manager import MultiAgentManager\nWORKING_DIR = \"mam\"\nmanager = MultiAgentManager(WORKING_DIR)\nmanager.start_manager()\n
"},{"location":"aea-framework-documentation/multi-agent-manager/#adding-projects","title":"Adding Projects","text":"

We first add a couple of finished AEA project:

from aea.configurations.base import PublicId\nweather_station_id = PublicId.from_str(\"fetchai/weather_station:0.32.5\")\nweather_client_id = PublicId.from_str(\"fetchai/weather_client:0.33.5\")\nmanager.add_project(weather_station_id)\nmanager.add_project(weather_client_id)\nweather_station_name = weather_station_id.name\nweather_client_name = weather_client_id.name\n
"},{"location":"aea-framework-documentation/multi-agent-manager/#adding-agent-instances","title":"Adding Agent Instances","text":"

Add the agent instances

agent_overrides = {\n\"private_key_paths\": {\"fetchai\": \"fetchai_private_key.txt\"},\n\"connection_private_key_paths\": {\"fetchai\": \"fetchai_connection_private_key.txt\"}\n}\np2p_public_id = PublicId.from_str(\"fetchai/p2p_libp2p:0.27.5\")\nsoef_public_id = PublicId.from_str(\"fetchai/soef:0.27.6\")\ncomponent_overrides = [{\n**p2p_public_id.json,\n\"type\": \"connection\",\n\"cert_requests\": [{\n\"identifier\": \"acn\",\n\"ledger_id\": \"fetchai\",\n\"not_after\": '2022-01-01',\n\"not_before\": '2021-01-01',\n\"public_key\": \"fetchai\",\n\"message_format\": \"{public_key}\",\n\"save_path\": \"conn_cert.txt\"\n}]\n}, {\n**soef_public_id.json,\n\"type\": \"connection\",\n\"config\": {\n\"token_storage_path\": \"soef_token.txt\"\n}\n}]\nmanager.add_agent(weather_station_id, component_overrides=component_overrides, agent_overrides=agent_overrides)\nagent_overrides = {\n\"private_key_paths\": {\"fetchai\": \"fetchai_private_key.txt\"},\n\"connection_private_key_paths\": {\"fetchai\": \"fetchai_connection_private_key.txt\"}\n}\ncomponent_overrides = [{\n**p2p_public_id.json,\n\"type\": \"connection\",\n\"config\": {\n\"delegate_uri\": \"127.0.0.1:11001\",\n\"entry_peers\": ['/dns4/127.0.0.1/tcp/9000/p2p/16Uiu2HAkzgZYyk25XjAhmgXcdMbahrHYi18uuAzHuxPn1KkdmLRw'],\n\"local_uri\": \"127.0.0.1:9001\",\n\"public_uri\": \"127.0.0.1:9001\",\n},\n\"cert_requests\": [{\n\"identifier\": \"acn\",\n\"ledger_id\": \"fetchai\",\n\"not_after\": '2022-01-01',\n\"not_before\": '2021-01-01',\n\"public_key\": \"fetchai\",\n\"message_format\": \"{public_key}\",\n\"save_path\": \"conn_cert.txt\"\n}]\n}, {\n**soef_public_id.json,\n\"type\": \"connection\",\n\"config\": {\n\"token_storage_path\": \"soef_token.txt\"\n}\n}]\nmanager.add_agent(weather_client_id, component_overrides=component_overrides, agent_overrides=agent_overrides)\n

Save the following private keys in the respective files.

FET_PRIVATE_KEY_STATION = b\"72d3149f5689f0749eaec5ebf6dba5deeb1e89b93ae1c58c71fd43dfaa231e87\"\nFET_PRIVATE_KEY_PATH_STATION = Path(manager.data_dir, weather_station_name, \"fetchai_private_key.txt\").absolute()\nFET_PRIVATE_KEY_PATH_STATION.write_bytes(FET_PRIVATE_KEY_STATION)\nFET_CONNECTION_PRIVATE_KEY_STATION = b\"bf529acb2546e13615ef6004c48e393f0638a5dc0c4979631a9a4bc554079f6f\"\nFET_CONNECTION_PRIVATE_KEY_PATH_STATION = Path(manager.data_dir, weather_station_name, \"fetchai_connection_private_key.txt\").absolute()\nFET_CONNECTION_PRIVATE_KEY_PATH_STATION.write_bytes(FET_CONNECTION_PRIVATE_KEY_STATION)\nFET_PRIVATE_KEY_CLIENT = b\"589839ae54b71b8754a7fe96b52045364077c28705a1806b74441debcae16e0a\"\nFET_PRIVATE_KEY_PATH_CLIENT = Path(manager.data_dir, weather_client_name, \"fetchai_private_key.txt\").absolute()\nFET_PRIVATE_KEY_PATH_CLIENT.write_bytes(FET_PRIVATE_KEY_CLIENT)\nFET_CONNECTION_PRIVATE_KEY_CLIENT = b\"c9b38eff57f678f5ab5304447997351edb08eceb883267fa4ad849074bec07e4\"\nFET_CONNECTION_PRIVATE_KEY_PATH_CLIENT = Path(manager.data_dir, weather_client_name, \"fetchai_connection_private_key.txt\").absolute()\nFET_CONNECTION_PRIVATE_KEY_PATH_CLIENT.write_bytes(FET_CONNECTION_PRIVATE_KEY_CLIENT)\n
"},{"location":"aea-framework-documentation/multi-agent-manager/#running-the-agents","title":"Running the Agents","text":"
import time\nmanager.start_agent(weather_station_id.name)\n# wait for ~10 seconds for peer node to go live\ntime.sleep(10.0)\nmanager.start_agent(weather_client_id.name)\ntime.sleep(5.0)\n
"},{"location":"aea-framework-documentation/multi-agent-manager/#stopping-the-agents","title":"Stopping the Agents","text":"
manager.stop_all_agents()\n
"},{"location":"aea-framework-documentation/multi-agent-manager/#cleaning-up","title":"Cleaning up","text":"
manager.stop_manager()\n
"},{"location":"aea-framework-documentation/multi-agent-manager/#limitations","title":"Limitations","text":"

The MultiAgentManager can only be used with compatible package versions, in particular the same package (with respect to author and name) cannot be used in different versions. If you want to run multiple agents with differing versions of the same package then use the aea launch command in the multi-processing mode, or simply launch each agent individually with aea run.

"},{"location":"aea-framework-documentation/multiplexer-standalone/","title":"Use Multiplexer Stand-Alone","text":"

The Multiplexer can be used stand-alone. This way a developer can utilise the protocols and connections independent of the Agent or AEA classes.

First, import the Python and application specific libraries and set the static variables. (Get the packages directory from the AEA repository svn export https://github.com/fetchai/agents-aea.git/trunk/packages.)

import os\nimport time\nfrom copy import copy\nfrom threading import Thread\nfrom typing import Optional\nfrom aea.configurations.base import ConnectionConfig\nfrom aea.helpers.file_io import write_with_lock\nfrom aea.identity.base import Identity\nfrom aea.mail.base import Envelope\nfrom aea.multiplexer import Multiplexer\nfrom packages.fetchai.connections.stub.connection import StubConnection\nfrom packages.fetchai.protocols.default.message import DefaultMessage\nINPUT_FILE = \"input.txt\"\nOUTPUT_FILE = \"output.txt\"\n
"},{"location":"aea-framework-documentation/multiplexer-standalone/#instantiate-a-multiplexer","title":"Instantiate a Multiplexer","text":"

A Multiplexer only needs a list of connections. The StubConnection is a simple connection which reads from and writes to file.

    # Ensure the input and output files do not exist initially\nif os.path.isfile(INPUT_FILE):\nos.remove(INPUT_FILE)\nif os.path.isfile(OUTPUT_FILE):\nos.remove(OUTPUT_FILE)\n# create the connection and multiplexer objects\nconfiguration = ConnectionConfig(\ninput_file=INPUT_FILE,\noutput_file=OUTPUT_FILE,\nconnection_id=StubConnection.connection_id,\n)\nstub_connection = StubConnection(\nconfiguration=configuration,\ndata_dir=\".\",\nidentity=Identity(\"some_agent\", \"some_address\", \"some_public_key\"),\n)\nmultiplexer = Multiplexer([stub_connection], protocols=[DefaultMessage])\n
"},{"location":"aea-framework-documentation/multiplexer-standalone/#start-the-multiplexer","title":"Start the Multiplexer","text":"

We can run a multiplexer by calling, connect() which starts the 'receive' and 'send' loops. We run the multiplexer from a different thread so that we can still use the main thread to pass it messages.

    try:\n# Set the multiplexer running in a different thread\nt = Thread(target=multiplexer.connect)\nt.start()\n# Wait for everything to start up\nfor _ in range(20):\nif multiplexer.is_connected:\nbreak\ntime.sleep(1)\nelse:\nraise Exception(\"Not connected\")\n
"},{"location":"aea-framework-documentation/multiplexer-standalone/#send-and-receive-an-envelope","title":"Send and Receive an Envelope","text":"

We use the input and output text files to send an envelope to our agent and receive a response

        # Create a message inside an envelope and get the stub connection to pass it into the multiplexer\nmessage_text = (\n\"multiplexer,some_agent,fetchai/default:1.0.0,\\x08\\x01*\\x07\\n\\x05hello,\"\n)\nwith open(INPUT_FILE, \"w\") as f:\nwrite_with_lock(f, message_text)\n# Wait for the envelope to get processed\nfor _ in range(20):\nif not multiplexer.in_queue.empty():\nbreak\ntime.sleep(1)\nelse:\nraise Exception(\"No message!\")\n# get the envelope\nenvelope = multiplexer.get()  # type: Optional[Envelope]\nassert envelope is not None\n# Inspect its contents\nprint(\n\"Envelope received by Multiplexer: sender={}, to={}, protocol_specification_id={}, message={}\".format(\nenvelope.sender,\nenvelope.to,\nenvelope.protocol_specification_id,\nenvelope.message,\n)\n)\n# Create a mirrored response envelope\nresponse_envelope = copy(envelope)\nresponse_envelope.to = envelope.sender\nresponse_envelope.sender = envelope.to\n# Send the envelope back\nmultiplexer.put(response_envelope)\n# Read the output envelope generated by the multiplexer\nwith open(OUTPUT_FILE, \"r\") as f:\nprint(\"Envelope received from Multiplexer: \" + f.readline())\n
"},{"location":"aea-framework-documentation/multiplexer-standalone/#shutdown","title":"Shutdown","text":"

Finally, stop our multiplexer and wait for it to finish

    finally:\n# Shut down the multiplexer\nmultiplexer.disconnect()\nt.join()\n
"},{"location":"aea-framework-documentation/multiplexer-standalone/#your-turn","title":"Your Turn","text":"

Now it is your turn to develop a simple use case which utilises the Multiplexer to send and receive Envelopes.

"},{"location":"aea-framework-documentation/multiplexer-standalone/#entire-code-listing","title":"Entire Code Listing","text":"

If you just want to copy and paste the entire script in you can find it here:

Click here to see full listing:
import os\nimport time\nfrom copy import copy\nfrom threading import Thread\nfrom typing import Optional\nfrom aea.configurations.base import ConnectionConfig\nfrom aea.helpers.file_io import write_with_lock\nfrom aea.identity.base import Identity\nfrom aea.mail.base import Envelope\nfrom aea.multiplexer import Multiplexer\nfrom packages.fetchai.connections.stub.connection import StubConnection\nfrom packages.fetchai.protocols.default.message import DefaultMessage\nINPUT_FILE = \"input.txt\"\nOUTPUT_FILE = \"output.txt\"\ndef run():\n\"\"\"Run demo.\"\"\"\n# Ensure the input and output files do not exist initially\nif os.path.isfile(INPUT_FILE):\nos.remove(INPUT_FILE)\nif os.path.isfile(OUTPUT_FILE):\nos.remove(OUTPUT_FILE)\n# create the connection and multiplexer objects\nconfiguration = ConnectionConfig(\ninput_file=INPUT_FILE,\noutput_file=OUTPUT_FILE,\nconnection_id=StubConnection.connection_id,\n)\nstub_connection = StubConnection(\nconfiguration=configuration,\ndata_dir=\".\",\nidentity=Identity(\"some_agent\", \"some_address\", \"some_public_key\"),\n)\nmultiplexer = Multiplexer([stub_connection], protocols=[DefaultMessage])\ntry:\n# Set the multiplexer running in a different thread\nt = Thread(target=multiplexer.connect)\nt.start()\n# Wait for everything to start up\nfor _ in range(20):\nif multiplexer.is_connected:\nbreak\ntime.sleep(1)\nelse:\nraise Exception(\"Not connected\")\n# Create a message inside an envelope and get the stub connection to pass it into the multiplexer\nmessage_text = (\n\"multiplexer,some_agent,fetchai/default:1.0.0,\\x08\\x01*\\x07\\n\\x05hello,\"\n)\nwith open(INPUT_FILE, \"w\") as f:\nwrite_with_lock(f, message_text)\n# Wait for the envelope to get processed\nfor _ in range(20):\nif not multiplexer.in_queue.empty():\nbreak\ntime.sleep(1)\nelse:\nraise Exception(\"No message!\")\n# get the envelope\nenvelope = multiplexer.get()  # type: Optional[Envelope]\nassert envelope is not None\n# Inspect its contents\nprint(\n\"Envelope received by Multiplexer: sender={}, to={}, protocol_specification_id={}, message={}\".format(\nenvelope.sender,\nenvelope.to,\nenvelope.protocol_specification_id,\nenvelope.message,\n)\n)\n# Create a mirrored response envelope\nresponse_envelope = copy(envelope)\nresponse_envelope.to = envelope.sender\nresponse_envelope.sender = envelope.to\n# Send the envelope back\nmultiplexer.put(response_envelope)\n# Read the output envelope generated by the multiplexer\nwith open(OUTPUT_FILE, \"r\") as f:\nprint(\"Envelope received from Multiplexer: \" + f.readline())\nfinally:\n# Shut down the multiplexer\nmultiplexer.disconnect()\nt.join()\nif __name__ == \"__main__\":\nrun()\n
"},{"location":"aea-framework-documentation/oracle-demo/","title":"Oracle Skills","text":"

This demo shows how an AEA can be used to maintain an oracle and how another AEA can request the oracle value.

"},{"location":"aea-framework-documentation/oracle-demo/#discussion","title":"Discussion","text":"

Oracle agents are agents that have permission to update or validate updates to state variables in a smart contract and whose goal is to accurately estimate or predict some real world quantity or quantities.

This demonstration shows how to set up a simple oracle agent who deploys an oracle contract and updates the contract with a token price fetched from a public API. It also shows how to create an oracle client agent that can request the value from the oracle contract.

"},{"location":"aea-framework-documentation/oracle-demo/#preparation-instructions","title":"Preparation Instructions","text":""},{"location":"aea-framework-documentation/oracle-demo/#dependencies","title":"Dependencies","text":"

Follow the Preliminaries and Installation sections from the AEA quick start.

"},{"location":"aea-framework-documentation/oracle-demo/#demo","title":"Demo","text":""},{"location":"aea-framework-documentation/oracle-demo/#create-the-oracle-aea","title":"Create the Oracle AEA","text":"

Fetch the AEA that will deploy and update the oracle contract.

aea fetch fetchai/coin_price_oracle:0.17.6\ncd coin_price_oracle\naea install\n
Alternatively, create from scratch (and customize the data source):

Create the AEA that will deploy the contract.

aea create coin_price_oracle\ncd coin_price_oracle\naea add connection fetchai/http_client:0.24.6\naea add connection fetchai/ledger:0.21.5\naea add connection fetchai/prometheus:0.9.6\naea add skill fetchai/advanced_data_request:0.7.6\naea add skill fetchai/simple_oracle:0.16.5\naea config set --type dict agent.dependencies \\\n'{\n  \"aea-ledger-fetchai\": {\"version\": \"<2.0.0,>=1.0.0\"},\n  \"aea-ledger-ethereum\": {\"version\": \"<2.0.0,>=1.0.0\"}\n}'\naea config set agent.default_connection fetchai/ledger:0.21.5\naea install\n

Set the URL for the data request skill:

aea config set --type str vendor.fetchai.skills.advanced_data_request.models.advanced_data_request_model.args.url \"https://api.coingecko.com/api/v3/simple/price?ids=fetch-ai&vs_currencies=usd\"\n

Specify the name and JSON path of the data to fetch from the API:

aea config set --type list vendor.fetchai.skills.advanced_data_request.models.advanced_data_request_model.args.outputs '[{\"name\": \"price\", \"json_path\": \"fetch-ai.usd\"}]'\n

Set the name of the oracle value in the simple oracle skill:

aea config set vendor.fetchai.skills.simple_oracle.models.strategy.args.oracle_value_name price\n

Then update the agent configuration with the default routing:

aea config set --type dict agent.default_routing \\\n'{\n\"fetchai/contract_api:1.1.7\": \"fetchai/ledger:0.21.5\",\n\"fetchai/http:1.1.7\": \"fetchai/http_client:0.24.6\",\n\"fetchai/ledger_api:1.1.7\": \"fetchai/ledger:0.21.5\"\n}'\n

Update the default ledger.

aea config set agent.default_ledger fetchai\n

Set the following configuration for the oracle skill:

aea config set vendor.fetchai.skills.simple_oracle.models.strategy.args.ledger_id fetchai\naea config set vendor.fetchai.skills.simple_oracle.models.strategy.args.update_function update_oracle_value\n

This demo runs on the fetchai ledger by default. Set the following variable for use in the configuration steps:

LEDGER_ID=fetchai\n
Alternatively, configure the agent to use an ethereum ledger:
LEDGER_ID=ethereum\n

Update the default ledger.

aea config set agent.default_ledger ethereum\n

Set the following configuration for the oracle skill:

aea config set vendor.fetchai.skills.simple_oracle.models.strategy.args.ledger_id ethereum\naea config set vendor.fetchai.skills.simple_oracle.models.strategy.args.update_function updateOracleValue\n

Additionally, create the private key for the oracle AEA. Generate and add a key for use with the ledger:

aea generate-key $LEDGER_ID --add-key\n

If running on a testnet (not including Ganache), generate some wealth for your AEA:

aea generate-wealth $LEDGER_ID\n
"},{"location":"aea-framework-documentation/oracle-demo/#create-the-oracle-client-aea","title":"Create the Oracle Client AEA","text":"

From a new terminal (in the same top-level directory), fetch the AEA that will deploy the oracle client contract and call the function that requests the coin price from the oracle contract.

aea fetch fetchai/coin_price_oracle_client:0.12.6\ncd coin_price_oracle_client\naea install\n
Alternatively, create from scratch:

Create the AEA that will deploy the contract.

aea create coin_price_oracle_client\ncd coin_price_oracle_client\naea add connection fetchai/http_client:0.24.6\naea add connection fetchai/ledger:0.21.5\naea add skill fetchai/simple_oracle_client:0.13.5\naea config set --type dict agent.dependencies \\\n'{\n  \"aea-ledger-fetchai\": {\"version\": \"<2.0.0,>=1.0.0\"},\n  \"aea-ledger-ethereum\": {\"version\": \"<2.0.0,>=1.0.0\"}\n}'\naea config set agent.default_connection fetchai/ledger:0.21.5\naea install\n

Then update the agent configuration with the default routing:

aea config set --type dict agent.default_routing \\\n'{\n\"fetchai/contract_api:1.1.7\": \"fetchai/ledger:0.21.5\",\n\"fetchai/http:1.1.7\": \"fetchai/http_client:0.24.6\",\n\"fetchai/ledger_api:1.1.7\": \"fetchai/ledger:0.21.5\"\n}'\n

Set the default ledger:

aea config set agent.default_ledger fetchai\n
Set the following configuration for the oracle client skill:

aea config set vendor.fetchai.skills.simple_oracle_client.models.strategy.args.ledger_id fetchai\naea config set vendor.fetchai.skills.simple_oracle_client.models.strategy.args.query_function query_oracle_value\n

Similar to above, set a temporary variable LEDGER_ID=fetchai or LEDGER_ID=ethereum.

Follow these steps to configure for an ethereum ledger:

Set the default ledger:

aea config set agent.default_ledger ethereum\n

Set the following configuration for the oracle client skill:

aea config set vendor.fetchai.skills.simple_oracle_client.models.strategy.args.ledger_id ethereum\naea config set vendor.fetchai.skills.simple_oracle_client.models.strategy.args.query_function queryOracleValue\n

Create the private key for the oracle client AEA. Generate and add a key for use on the ledger:

aea generate-key $LEDGER_ID --add-key\n

If running on a testnet (not including Ganache), generate some wealth for your AEA:

aea generate-wealth $LEDGER_ID\n
"},{"location":"aea-framework-documentation/oracle-demo/#configuring-a-ledger","title":"Configuring a Ledger","text":"

The oracle AEAs require either a locally running ledger node or a connection to a remote ledger. By default, they are configured to use the latest fetchai testnet.

Follow these steps to configure local Ethereum test node:

The easiest way to test the oracle agents on an Ethereum-based ledger to set up a local test node using Ganache. This can be done by running the following docker command from the directory you started from (in a new terminal). This command will also fund the accounts of the AEAs:

docker run -p 8545:8545 trufflesuite/ganache-cli:latest --verbose --gasPrice=0 --gasLimit=0x1fffffffffffff --account=\"$(cat coin_price_oracle/ethereum_private_key.txt),1000000000000000000000\" --account=\"$(cat coin_price_oracle_client/ethereum_private_key.txt),1000000000000000000000\"\n

Run the following Python script (with web3 installed) from the top-level directory to deploy a mock Fetch ERC20 contract and give some test FET to the client agent.

import json\nimport os\nfrom web3 import Web3\nFILE_DIR = os.path.dirname(os.path.realpath(__file__))\nCONTRACT_PATH = os.path.join(FILE_DIR, \"coin_price_oracle_client/vendor/fetchai/contracts/fet_erc20/build/FetERC20Mock.json\")\nORACLE_PRIVATE_KEY_PATH = os.path.join(FILE_DIR, \"coin_price_oracle/ethereum_private_key.txt\")\nCLIENT_PRIVATE_KEY_PATH = os.path.join(FILE_DIR, \"coin_price_oracle_client/ethereum_private_key.txt\")\n# Solidity source code\nwith open(CONTRACT_PATH) as file:\ncompiled_sol = json.load(file)\n# web3.py instance\nw3 = Web3(Web3.HTTPProvider('http://127.0.0.1:8545'))\n# Import oracle account from private key and set to default account\nwith open(ORACLE_PRIVATE_KEY_PATH) as file:\nprivate_key = file.read()\noracle_account = w3.eth.account.privateKeyToAccount(private_key)\nw3.eth.defaultAccount = oracle_account.address\n# Import client account from private key\nwith open(CLIENT_PRIVATE_KEY_PATH) as file:\nprivate_key = file.read()\nclient_account = w3.eth.account.privateKeyToAccount(private_key)\n# Deploy mock Fetch ERC20 contract\nFetERC20Mock = w3.eth.contract(abi=compiled_sol['abi'], bytecode=compiled_sol['bytecode'])\n# Submit the transaction that deploys the contract\ntx_hash = FetERC20Mock.constructor(\nname=\"FetERC20Mock\",\nsymbol=\"MFET\",\ninitialSupply=int(1e23),\ndecimals_=18).transact()\n# Wait for the transaction to be mined, and get the transaction receipt\ntx_receipt = w3.eth.waitForTransactionReceipt(tx_hash)\n# Print out the contract address\nprint(\"FetERC20Mock contract deployed at:\", tx_receipt.contractAddress)\n# Get deployed contract\nfet_erc20_mock = w3.eth.contract(address=tx_receipt.contractAddress, abi=compiled_sol['abi'])\n# Transfer some test FET to oracle client account\ntx_hash = fet_erc20_mock.functions.transfer(client_account.address, int(1e20)).transact()\ntx_receipt = w3.eth.waitForTransactionReceipt(tx_hash)\n

Set the ERC20 contract address for the oracle AEA

aea config set vendor.fetchai.skills.simple_oracle.models.strategy.args.erc20_address $ERC20_ADDRESS\n

as well as for the oracle client AEA

aea config set vendor.fetchai.skills.simple_oracle_client.models.strategy.args.erc20_address $ERC20_ADDRESS\n

where ERC20_ADDRESS is in the output of the script above.

"},{"location":"aea-framework-documentation/oracle-demo/#run-the-oracle-aea","title":"Run the Oracle AEA","text":"

Run the oracle agent. This will deploy a contract to the testnet, grant oracle permissions to the AEA's wallet address, and periodically update the contract with the latest price of FET (or whichever coin was specified).

aea run\n

After a few moments, you should see the following notices in the logs:

info: [coin_price_oracle] Oracle contract successfully deployed at address: ...\n...\ninfo: [coin_price_oracle] Oracle role successfully granted!\n...\ninfo: [coin_price_oracle] Oracle value successfully updated!\n

The oracle contract will continue to be updated with the latest retrieved coin price at the default time interval (every 15 seconds).

"},{"location":"aea-framework-documentation/oracle-demo/#set-the-erc20-and-oracle-contract-addresses-for-the-oracle-client-aea","title":"Set the ERC20 and Oracle Contract Addresses for the Oracle Client AEA","text":"
aea config set vendor.fetchai.skills.simple_oracle_client.models.strategy.args.oracle_contract_address $ORACLE_ADDRESS\n

where ORACLE_ADDRESS should be set to the address shown in the oracle AEA logs:

Oracle contract successfully deployed at address: ORACLE_ADDRESS\n
"},{"location":"aea-framework-documentation/oracle-demo/#run-the-oracle-client-aea","title":"Run the Oracle Client AEA","text":"

Run the oracle client agent. This will deploy an oracle client contract to the testnet, approve the contract to spend tokens on behalf of the AEA, and periodically call the contract function that requests the latest price of FET (or whichever coin was specified).

aea run\n

After a few moments, you should see the following notices in the logs:

info: [coin_price_oracle_client] Oracle client contract successfully deployed at address: ...\n...\ninfo: [coin_price_oracle_client] Oracle value successfully requested!\n

The AEA will continue to request the latest coin price at the default time interval (every 15 seconds).

"},{"location":"aea-framework-documentation/orm-integration/","title":"ORM Integration","text":"

This guide demonstrates how to configure an AEA to interact with a database using python-sql objects.

"},{"location":"aea-framework-documentation/orm-integration/#discussion","title":"Discussion","text":"

Object-relational-mapping (ORM) is the idea of being able to write SQL queries, using the object-oriented paradigm of your preferred programming language. The scope of this guide is to demonstrate how you can create an easily configurable AEA that reads data from a database using ORMs.

  • We assume, that you followed the guide for the thermometer-skills.
  • We assume, that we have a database genericdb.db with table name data. This table contains the following columns timestamp and thermometer.
  • We assume, that we have a hardware thermometer sensor that adds the readings in the genericdb database (although you can follow the guide without having access to a sensor).

Since the AEA framework enables us to use third-party libraries hosted on PyPI we can directly reference the external dependencies. The aea install command will install each dependency that the specific AEA needs and which is listed in the skill's YAML file.

"},{"location":"aea-framework-documentation/orm-integration/#communication","title":"Communication","text":"

This diagram shows the communication between the various entities in the case where the thermometer data is successfully sold by the seller AEA to the buyer.

    sequenceDiagram\n        participant Search\n        participant Buyer_AEA\n        participant Seller_AEA\n        participant Blockchain\n\n        activate Buyer_AEA\n        activate Search\n        activate Seller_AEA\n        activate Blockchain\n\n        Seller_AEA->>Search: register_service\n        Buyer_AEA->>Search: search\n        Search-->>Buyer_AEA: list_of_agents\n        Buyer_AEA->>Seller_AEA: call_for_proposal\n        Seller_AEA->>Buyer_AEA: propose\n        Buyer_AEA->>Seller_AEA: accept\n        Seller_AEA->>Buyer_AEA: match_accept\n        Buyer_AEA->>Blockchain: transfer_funds\n        Buyer_AEA->>Seller_AEA: send_transaction_hash\n        Seller_AEA->>Blockchain: check_transaction_status\n        Seller_AEA->>Buyer_AEA: send_data\n\n        deactivate Buyer_AEA\n        deactivate Search\n        deactivate Seller_AEA\n        deactivate Blockchain    
"},{"location":"aea-framework-documentation/orm-integration/#preparation-instructions","title":"Preparation Instructions","text":""},{"location":"aea-framework-documentation/orm-integration/#dependencies","title":"Dependencies","text":"

Follow the Preliminaries and Installation sections from the AEA quick start.

"},{"location":"aea-framework-documentation/orm-integration/#demo-instructions","title":"Demo Instructions","text":"

This demo involves a true ledger transaction on Fetch.ai's testnet network or Ethereum's ropsten. This demo assumes the buyer trusts the seller AEA to send the data upon successful payment.

"},{"location":"aea-framework-documentation/orm-integration/#create-the-seller-aea","title":"Create the Seller AEA","text":"

First, fetch the seller AEA which provides thermometer data:

aea fetch fetchai/thermometer_aea:0.30.5 --alias my_thermometer_aea\ncd my_thermometer_aea\naea install\naea build\n
Alternatively, create from scratch:

The following steps create the seller from scratch:

aea create my_thermometer_aea\ncd my_thermometer_aea\naea add connection fetchai/p2p_libp2p:0.27.5\naea add connection fetchai/soef:0.27.6\naea add connection fetchai/ledger:0.21.5\naea add skill fetchai/thermometer:0.27.6\naea config set --type dict agent.dependencies \\\n'{\n  \"aea-ledger-fetchai\": {\"version\": \"<2.0.0,>=1.0.0\"}\n}'\naea config set agent.default_connection fetchai/p2p_libp2p:0.27.5\naea config set --type dict agent.default_routing \\\n'{\n  \"fetchai/ledger_api:1.1.7\": \"fetchai/ledger:0.21.5\",\n  \"fetchai/oef_search:1.1.7\": \"fetchai/soef:0.27.6\"\n}'\naea install\naea build\n
"},{"location":"aea-framework-documentation/orm-integration/#create-the-buyer-client","title":"Create the Buyer Client","text":"

In another terminal, fetch the buyer AEA:

aea fetch fetchai/thermometer_client:0.32.5 --alias my_thermometer_client\ncd my_thermometer_client\naea install\naea build\n
Alternatively, create from scratch:

The following steps create the car data client from scratch:

aea create my_thermometer_client\ncd my_thermometer_client\naea add connection fetchai/p2p_libp2p:0.27.5\naea add connection fetchai/soef:0.27.6\naea add connection fetchai/ledger:0.21.5\naea add skill fetchai/thermometer_client:0.26.6\naea config set --type dict agent.dependencies \\\n'{\n  \"aea-ledger-fetchai\": {\"version\": \"<2.0.0,>=1.0.0\"}\n}'\naea config set agent.default_connection fetchai/p2p_libp2p:0.27.5\naea config set --type dict agent.default_routing \\\n'{\n  \"fetchai/ledger_api:1.1.7\": \"fetchai/ledger:0.21.5\",\n  \"fetchai/oef_search:1.1.7\": \"fetchai/soef:0.27.6\"\n}'\naea install\naea build\n
"},{"location":"aea-framework-documentation/orm-integration/#add-keys-for-the-seller-aea","title":"Add Keys for the Seller AEA","text":"

First, create the private key for the seller AEA based on the network you want to transact. To generate and add a private-public key pair for Fetch.ai Dorado use:

aea generate-key fetchai\naea add-key fetchai fetchai_private_key.txt\n

Next, create a private key used to secure the AEA's communications:

aea generate-key fetchai fetchai_connection_private_key.txt\naea add-key fetchai fetchai_connection_private_key.txt --connection\n

Finally, certify the key for use by the connections that request that:

aea issue-certificates\n
"},{"location":"aea-framework-documentation/orm-integration/#add-keys-and-generate-wealth-for-the-buyer-aea","title":"Add Keys and Generate Wealth for the Buyer AEA","text":"

The buyer needs to have some wealth to purchase the thermometer data.

First, create the private key for the buyer AEA based on the network you want to transact. To generate and add a private-public key pair for Fetch.ai use:

aea generate-key fetchai\naea add-key fetchai fetchai_private_key.txt\n

Then, create some wealth for the buyer based on the network you want to transact with. On the Fetch.ai Dorado network:

aea generate-wealth fetchai\n

Next, create a private key used to secure the AEA's communications:

aea generate-key fetchai fetchai_connection_private_key.txt\naea add-key fetchai fetchai_connection_private_key.txt --connection\n

Finally, certify the key for use by the connections that request that:

aea issue-certificates\n
"},{"location":"aea-framework-documentation/orm-integration/#update-the-seller-and-buyer-aea-skill-configurations","title":"Update the Seller and Buyer AEA Skill Configurations","text":"

In my_thermometer_aea/vendor/fetchai/skills/thermometer/skill.yaml, replace the data_for_sale with your data:

models:\n...\nstrategy:\nargs:\ncurrency_id: FET\ndata_for_sale:\ntemperature: 26\nhas_data_source: false\nis_ledger_tx: true\nledger_id: fetchai\nlocation:\nlatitude: 51.5194\nlongitude: 0.127\nservice_data:\nkey: seller_service\nvalue: thermometer_data\nservice_id: thermometer_data\nunit_price: 10\nclass_name: Strategy\ndependencies:\nSQLAlchemy: {}\n

The service_data is used to register the service in the SOEF search node and make your agent discoverable.

In my_thermometer_client/vendor/fetchai/skills/thermometer_client/skill.yaml) ensure you have matching data.

models:\n...\nstrategy:\nargs:\ncurrency_id: FET\nis_ledger_tx: true\nledger_id: fetchai\nlocation:\nlatitude: 51.5194\nlongitude: 0.127\nmax_negotiations: 1\nmax_tx_fee: 3550000000000000\nmax_unit_price: 20\nsearch_query:\nconstraint_type: ==\nsearch_key: seller_service\nsearch_value: thermometer_data\nsearch_radius: 5.0\nservice_id: thermometer_data\nclass_name: Strategy\n

After changing the skill configuration files you should run the following command for both agents to install each dependency:

aea install\n
"},{"location":"aea-framework-documentation/orm-integration/#modify-the-sellers-strategy","title":"Modify the Seller's Strategy","text":"

Before being able to modify a package we need to eject it from vendor:

aea eject skill fetchai/thermometer:0.27.6\n

This will move the package to your skills directory and reset the version to 0.1.0 and the author to your author handle.

Open strategy.py (in my_thermometer_aea/skills/thermometer/strategy.py) and make the following modifications:

Import the newly installed sqlalchemy library in your strategy.

import sqlalchemy as db\n

Then modify your strategy's __init__ function to match the following code:

class Strategy(GenericStrategy):\n\"\"\"This class defines a strategy for the agent.\"\"\"\ndef __init__(self, **kwargs) -> None:\n\"\"\"\n        Initialize the strategy of the agent.\n        :param register_as: determines whether the agent registers as seller, buyer or both\n        :param search_for: determines whether the agent searches for sellers, buyers or both\n        :return: None\n        \"\"\"\nself._db_engine = db.create_engine(\"sqlite:///genericdb.db\")\nself._tbl = self.create_database_and_table()\nself.insert_data()\nsuper().__init__(**kwargs)\n

At the end of the file modify the collect_from_data_source function:

    def collect_from_data_source(self) -> Dict[str, str]:\n\"\"\"Implement the logic to collect data.\"\"\"\nconnection = self._db_engine.connect()\nquery = db.select([self._tbl])\nresult_proxy = connection.execute(query)\ndata_points = result_proxy.fetchall()\nreturn {\"data\": json.dumps(list(map(tuple, data_points)))}\n

Also, create two new functions, one that creates a connection with the database, and another that populates the database with some fake data. This is needed in the case you do not have access to an actual thermometer sensor that inserts data in the database.

    def create_database_and_table(self):\n\"\"\"Creates a database and a table to store the data if not exists.\"\"\"\nmetadata = db.MetaData()\ntbl = db.Table(\n\"data\",\nmetadata,\ndb.Column(\"timestamp\", db.Integer()),\ndb.Column(\"temprature\", db.String(255), nullable=False),\n)\nmetadata.create_all(self._db_engine)\nreturn tbl\ndef insert_data(self):\n\"\"\"Insert data in the database.\"\"\"\nconnection = self._db_engine.connect()\nfor _ in range(10):\nquery = db.insert(self._tbl).values(  # nosec\ntimestamp=time.time(), temprature=str(random.randrange(10, 25))\n)\nconnection.execute(query)\n

After modifying the skill we need to fingerprint it:

aea fingerprint skill {YOUR_AUTHOR_HANDLE}/thermometer:0.1.0\n
"},{"location":"aea-framework-documentation/orm-integration/#run-both-aeas","title":"Run Both AEAs","text":"

First, run the thermometer (seller) AEA:

aea run\n

Once you see a message of the form To join its network use multiaddr 'SOME_ADDRESS' take note of this address. (Alternatively, use aea get-multiaddress fetchai -c -i fetchai/p2p_libp2p:0.27.5 -u public_uri to retrieve the address.) This is the entry peer address for the local agent communication network created by the thermometer AEA.

Then, configure the thermometer client (buyer) to connect to this same local ACN by running the following command in the buyer terminal, replacing SOME_ADDRESS with the value you noted above:

aea config set --type dict vendor.fetchai.connections.p2p_libp2p.config \\\n'{\n  \"delegate_uri\": \"127.0.0.1:11001\",\n  \"entry_peers\": [\"SOME_ADDRESS\"],\n  \"local_uri\": \"127.0.0.1:9001\",\n  \"log_file\": \"libp2p_node.log\",\n  \"public_uri\": \"127.0.0.1:9001\"\n}'\n

Then run the thermometer client AEA:

aea run\n

You will see that the AEAs negotiate and then transact using the configured testnet.

"},{"location":"aea-framework-documentation/orm-integration/#delete-the-aeas","title":"Delete the AEAs","text":"

When you're done, stop the agents (CTRL+C), go up a level and delete the AEAs.

cd ..\naea delete my_thermometer_aea\naea delete my_thermometer_client\n
"},{"location":"aea-framework-documentation/p2p-connection/","title":"P2P Connection","text":"

The fetchai/p2p_libp2p:0.27.5 connection allows AEAs to create a peer-to-peer communication network. In particular, the connection creates an overlay network which maps agents' public keys to IP addresses.

"},{"location":"aea-framework-documentation/p2p-connection/#local-demo","title":"Local Demo","text":"

First, make sure you have installed the crypto plugin of the target test-net. E.g. for Fetch.AI:

pip install aea-ledger-fetchai\n
"},{"location":"aea-framework-documentation/p2p-connection/#create-and-run-the-genesis-aea","title":"Create and Run the Genesis AEA","text":"

Create one AEA as follows:

aea create my_genesis_aea\ncd my_genesis_aea\naea add connection fetchai/p2p_libp2p:0.27.5\naea config set agent.default_connection fetchai/p2p_libp2p:0.27.5\naea install\naea build\n

Establish the proof of representation:

aea generate-key fetchai\naea add-key fetchai fetchai_private_key.txt\naea generate-key fetchai fetchai_connection_private_key.txt\naea add-key fetchai fetchai_connection_private_key.txt --connection\naea issue-certificates\n

Run the AEA:

aea run --connections fetchai/p2p_libp2p:0.27.5\n

Once you see a message of the form To join its network use multiaddr 'SOME_ADDRESS' take note of the address. (Alternatively, use aea get-multiaddress fetchai -c -i fetchai/p2p_libp2p:0.27.5 -u public_uri to retrieve the address.) This is the entry peer address for the local agent communication network created by the genesis AEA.

"},{"location":"aea-framework-documentation/p2p-connection/#create-and-run-another-aea","title":"Create and Run Another AEA","text":"

Create a second AEA:

aea create my_other_aea\ncd my_other_aea\naea add connection fetchai/p2p_libp2p:0.27.5\naea config set agent.default_connection fetchai/p2p_libp2p:0.27.5\naea install\naea build\n

Establish the proof of representation:

aea generate-key fetchai\naea add-key fetchai fetchai_private_key.txt\naea generate-key fetchai fetchai_connection_private_key.txt\naea add-key fetchai fetchai_connection_private_key.txt --connection\naea issue-certificates\n

Provide the AEA with the information it needs to find the genesis:

aea config set --type dict vendor.fetchai.connections.p2p_libp2p.config \\\n'{\n  \"delegate_uri\": \"127.0.0.1:11001\",\n  \"entry_peers\": [\"SOME_ADDRESS\"],\n  \"local_uri\": \"127.0.0.1:9001\",\n  \"log_file\": \"libp2p_node.log\",\n  \"public_uri\": \"127.0.0.1:9001\"\n}'\n

Here SOME_ADDRESS needs to be replaced with the list of multi addresses displayed in the log output of the genesis AEA.

Run the AEA:

aea run --connections fetchai/p2p_libp2p:0.27.5\n

You can inspect the libp2p_node.log log files of the AEA to see how they discover each other.

Note

Currently p2p_libp2p connection limits the total message size to 3 MB.

"},{"location":"aea-framework-documentation/p2p-connection/#local-demo-with-skills","title":"Local Demo with Skills","text":"

Explore the demo section for further examples.

"},{"location":"aea-framework-documentation/p2p-connection/#deployed-agent-communication-network","title":"Deployed Agent Communication Network","text":"

You can connect to the deployed public test network by adding one or multiple of the following addresses as the p2p_libp2p connection's entry_peers:

/dns4/acn.fetch.ai/tcp/9000/p2p/16Uiu2HAkw1ypeQYQbRFV5hKUxGRHocwU5ohmVmCnyJNg36tnPFdx\n
/dns4/acn.fetch.ai/tcp/9001/p2p/16Uiu2HAmVWnopQAqq4pniYLw44VRvYxBUoRHqjz1Hh2SoCyjbyRW\n

Specifically, in an AEA's configuration aea-config.yaml add the above addresses for entry_peers as follows:

---\npublic_id: fetchai/p2p_libp2p:0.27.5\ntype: connection\nconfig:\ndelegate_uri: null\nentry_peers: [/dns4/acn.fetch.ai/tcp/9000/p2p/16Uiu2HAkw1ypeQYQbRFV5hKUxGRHocwU5ohmVmCnyJNg36tnPFdx,/dns4/acn.fetch.ai/tcp/9001/p2p/16Uiu2HAmVWnopQAqq4pniYLw44VRvYxBUoRHqjz1Hh2SoCyjbyRW]\npublic_uri: null\nlocal_uri: 127.0.0.1:9001\n

Note, this configuration change must be made for all agents attempting to communicate with each other via the Agent Communication Network. For example, in demos involving two agents, both agents will need the above modifications to their respective aea-config.yaml file. However, remember to use different ports in local_uri. This will allow both agents to default to this communication network without the added overhead of opening ports and specifying hosts on the individual host machines running each agent.

"},{"location":"aea-framework-documentation/p2p-connection/#configuring-the-connectionyaml-entries","title":"Configuring the connection.yaml Entries","text":"

To learn more about how to configure your fetchai/p2p_libp2p:0.27.5 connection consult the README.md file supplied with the connection package.

"},{"location":"aea-framework-documentation/p2p-connection/#running-go-peer-standalone","title":"Running Go Peer Standalone","text":"

You can run a peer node in standalone mode; that is, as a Go process with no dependency on the AEA framework. To facilitate such a deployment, we provide a script run_acn_node_standalone.py and a corresponding Dockerfile.

First, you need to build the node's binary (libp2p_node) either:

  • locally

    svn export https://github.com/fetchai/agents-aea.git/trunk/packages/fetchai/connections/p2p_libp2p\ncd p2p_libp2p\ngo build\nchmod +x libp2p_node\n

    Make sure you satisfy the system requirements.

  • or within a docker image using the provided Dockerfile:

    docker build -t acn_node_standalone -f scripts/acn/Dockerfile .\n

Next, to run the node binary in standalone mode, it requires values for the following entries:

  • AEA_P2P_ID: the node's private key, will be used as its identity
  • AEA_P2P_URI: the local host and port to use by node
  • AEA_P2P_URI_PUBLIC: the URI under which the peer is publicly reachable
  • AEA_P2P_DELEGATE_URI: the URI under which the peer receives delegate connections
  • AEA_P2P_ENTRY_URIS: an optionally supplied list of comma-separated (,) entry multiaddresses for the peer to bootstrap

The script allows different methods to pass these values to the node:

  • As environment variables exported in the format <ENTRY_KEYWORD>=<ENTRY_VALUE> for each entry. Then:

    python3 run_acn_node_standalone.py libp2p_node --config-from-env\n
  • Using an environment file containing the entries and their values in the format <ENTRY_KEYWORD>=<ENTRY_VALUE>, one entry per line. Then:

    python3 run_acn_node_standalone.py libp2p_node --config-from-file <env-file-path>\n

    or

    docker run -v <acn_config_file>:/acn/acn_config -it acn_node_standalone --config-from-file /acn/acn_config\n
  • Using command line arguments:

    python3 run_acn_node_standalone.py libp2p_node --key-file <node_private_key.txt> \\\n--uri <AEA_P2P_URI> --uri-external <AEA_P2P_URI_PUBLIC>  \\\n--uri-delegate <AEA_P2P_DELEGATE_URI> \\\n--entry-peers-maddrs <AEA_P2P_ENTRY_URI_1> <AEA_P2P_ENTRY_URI_2> ...\n

    or

    docker run -v <node_private_key.txt>:/acn/key.txt -it acn_node_standalone --key-file /acn/key.txt \\\n--uri <AEA_P2P_URI> --uri-external <AEA_P2P_URI_PUBLIC>  \\\n--uri-delegate <AEA_P2P_DELEGATE_URI> \\\n--entry-peers-maddrs <AEA_P2P_ENTRY_URI_1> <AEA_P2P_ENTRY_URI_2> ...\n

Note that the script will always save the configuration of the running node as a file under the name .acn_config in the current working directory. This can be handy when you want the exact same configuration for future runs of the node.

"},{"location":"aea-framework-documentation/package-imports/","title":"File Structure","text":"

An agent that is generated using the AEA framework is a modular system with different connections, contracts, protocols and skills.

"},{"location":"aea-framework-documentation/package-imports/#an-aea-projects-file-structure","title":"An AEA Project's File Structure","text":"

The file structure of an AEA is fixed.

The top level directory has the AEA's name. Below is a aea-config.yaml configuration file, then directories containing the connections, contracts, protocols, and skills developed by the developer as part of the given project. The connections, contracts, protocols and skills used from the registry (local or remote - added via aea fetch or aea add) are located in vendor and sorted by author. Build artefacts are placed in the .build/ directory and certificates are placed in the .certs/ directory. Finally, there are files containing the private keys of the AEA.

When we create a new agent with the command aea create my_aea we create the file structure that looks like the following:

aea_name/\n  aea-config.yaml       YAML configuration of the AEA\n  fetchai_private_key.txt   The private key file\n  connections/          Directory containing all the connections developed as part of the given project.\n    connection_1/       First connection\n    ...                 ...\n    connection_n/       nth connection\n  contracts/            Directory containing all the contracts developed as part of the given project.\n    connection_1/       First connection\n    ...                 ...\n    connection_n/       nth connection\n  protocols/            Directory containing all the protocols developed as part of the given project.\n    protocol_1/         First protocol\n    ...                 ...\n    protocol_m/         mth protocol\n  skills/               Directory containing all the skills developed as part of the given project.\n    skill_1/            First skill\n    ...                 ...\n    skill_k/            kth skill\n  vendor/               Directory containing all the added resources from the registry, sorted by author.\n    author_1/           Directory containing all the resources added from author_1\n      connections/      Directory containing all the added connections from author_1\n        ...             ...\n      protocols/        Directory containing all the added protocols from author_1\n        ...             ...\n      skills/           Directory containing all the added skills from author_1\n        ...             ...\n

The developer can create new directories where necessary but the core structure must remain the same.

"},{"location":"aea-framework-documentation/package-imports/#aea-configuration-yaml","title":"AEA Configuration YAML","text":"

The aea-config.yaml is the top level configuration file of an AEA. It defines the global configurations as well as the component/package dependencies of the AEA. In some sense, the AEA can therefore be understood as an orchestrator of components.

For the AEA to use a package, the public_id for the package must be listed in the aea-config.yaml file, e.g.

connections:\n- fetchai/stub:0.21.3\n

The above shows a part of the aea-config.yaml. If you see the connections, you will see that we follow a pattern of author/name_package:version to identify each package, also referred to as public_id. Here the author is the author of the package.

"},{"location":"aea-framework-documentation/package-imports/#vendor-and-package-directories","title":"Vendor and Package Directories","text":"

The vendor folder contains the packages from the registry (local or remote) which have been developed by ourselves, other authors or Fetch.ai and are placed in different namespaces according to the author name.

The packages we develop as part of the given AEA project are in the respective connections/, contracts/, protocols/, and skills/ folders.

In the above configuration example, the package is authored by Fetch.ai and is located inside the vendor/fetchai/connections folder.

"},{"location":"aea-framework-documentation/package-imports/#importing-modules-from-packages","title":"Importing Modules from Packages","text":"

The way we import modules from packages inside the agent is in the form of packages.{author}.{package_type}.{package_name}.{module_name}. So for the above example, the import path is packages.fetchai.connections.stub.{module_name}.

The framework loads the modules from the local agent project and adds them to Python's sys.modules under the respective path.

We use a custom package management approach for the AEAs rather than the default Python one as it provides us with more flexibility, especially when it comes to extension beyond the Python ecosystem.

"},{"location":"aea-framework-documentation/package-imports/#python-dependencies-of-packages","title":"Python Dependencies of Packages","text":"

Python dependencies of packages are specified in their respective configuration files under dependencies. They will be installed when aea install is run on an agent project.

"},{"location":"aea-framework-documentation/package-imports/#create-a-package","title":"Create a Package","text":"

If you want to create a package, you can use the CLI command aea scaffold connection/contract/protocol/skill [name] and this will create the package and put it inside the respective folder based on the command for example if we scaffold skill with the name my_skill it will be located inside the folder skills in the root directory of the agent (my_aea/skills/my_skill).

"},{"location":"aea-framework-documentation/package-imports/#use-published-packages-from-the-registry","title":"Use Published Packages from the Registry","text":"

If you want to use a finished package, you can use a package from the registry.

There or two registries. The remote registry operated by Fetch.ai and a local registry stub. The local registry stub is a directory called packages which contains packages in a nested structure with authors on the top level, followed by the package type, then package name. An example of such a directory is the packages directory located in the AEA repository. The local registry is useful for development.

You can use the CLI to interact with the registry. By default, the CLI points to the remote registry. You can point it to the local registry via the flag --local.

"},{"location":"aea-framework-documentation/package-imports/#package-versioning","title":"Package Versioning","text":"

By default, the AEA can only handle one version per package. That is, a project should never use both some_author/some_package_name:0.1.0 and some_author/some_package_name:0.2.0.

If two AEA packages with the same author and name but different versions are used in the same Python process, then only the code from one of the packages (generally not deterministic) will be available in sys.modules. This can lead to inconsistencies and exceptions at runtime.

"},{"location":"aea-framework-documentation/performance-benchmark/","title":"Performance Benchmark","text":"

Test AEA framework performance.

"},{"location":"aea-framework-documentation/performance-benchmark/#what-is-it","title":"What is it?","text":"

The benchmark module is a set of tools to measure execution time, CPU load and memory usage of the AEA Python code. It produces text reports and draws charts to present the results.

"},{"location":"aea-framework-documentation/performance-benchmark/#how-does-it-work","title":"How does it Work?","text":"

The framework:

  • spawns a dedicated process for each test run to execute the function to test.
  • measures CPU and RAM usage periodically.
  • waits for function exits or terminates them by timeout.
  • repeats test execution multiple times to get more accurate results.
"},{"location":"aea-framework-documentation/performance-benchmark/#how-to-use","title":"How to Use","text":"

Steps to run a test:

  • Write a function you would like to test with all arguments you would like to parametrise, add some doc strings.
  • Split the function into two parts: 'prepare' and 'performance' part. The 'prepare' part will not be included in the measurement.
  • Add BenchmarkControl support, to notify framework to start measurement.
  • Import TestCli class, TestCli().run(function_to_be_tested)
  • Call it from console to get text results.
"},{"location":"aea-framework-documentation/performance-benchmark/#simple-example","title":"Simple Example","text":"

cpuburn - simple test of CPU load depends on idle sleep time. Shows how much CPU consumed during the execution.

import time\nfrom benchmark.framework.benchmark import BenchmarkControl\nfrom benchmark.framework.cli import TestCli\ndef cpu_burn(benchmark: BenchmarkControl, run_time=10, sleep=0.0001) -> None:\n\"\"\"\n    Do nothing, just burn cpu to check cpu load changed on sleep.\n    :param benchmark: benchmark special parameter to communicate with executor\n    :param run_time: time limit to run this function\n    :param sleep: time to sleep in loop\n    :return: None\n    \"\"\"\nbenchmark.start()\nstart_time = time.time()\nwhile True:\ntime.sleep(sleep)\nif time.time() - start_time >= run_time:\nbreak\nif __name__ == \"__main__\":\nTestCli(cpu_burn).run()\n

Run it with python ./benchmark/cases/cpu_burn.py --help to get help about usage.

Usage: cpu_burn.py [OPTIONS] [ARGS]...\n\nDo nothing, just burn cpu to check cpu load changed on sleep.\n\n:param benchmark: benchmark special parameter to communicate with executor\n  :param run_time: time limit to run this function :param sleep: time to sleep in loop\n\n:return: None\n\nARGS is function arguments in format: `run_time,sleep`\ndefault ARGS is `10,0.0001`\nOptions:\n  --timeout FLOAT               Executor timeout in seconds  [default: 10.0]\n--period FLOAT                Period for measurement  [default: 0.1]\n-N, --num-executions INTEGER  Number of runs for each case  [default: 1]\n-P, --plot INTEGER            X axis parameter idx\n  --help                        Show this message and exit.\n

Run it with python ./benchmark/cases/cpu_burn.py to start with default parameters.

Test execution timeout: 10.0\nTest execution measure period: 0.1\nTested function name: cpu_burn\nTested function description:\n    Do nothing, just burn cpu to check cpu load changed on sleep.\n\n:param benchmark: benchmark special parameter to communicate with executor\n    :param run_time: time limit to run this function\n:param sleep: time to sleep in loop\n\n:return: None\n\nTested function argument names: ['run_time', 'sleep']\nTested function argument default values: [10, 0.0001]\n== Report created 2020-04-27 15:14:56.076549 ==\nArguments are `[10, 0.0001]`\nNumber of runs: 1\nNumber of time terminated: 0\nTime passed (seconds): 10.031443119049072 \u00b1 0\ncpu min (%): 0.0 \u00b1 0\ncpu max (%): 10.0 \u00b1 0\ncpu mean (%): 3.4 \u00b1 0\nmem min (kb): 53.98828125 \u00b1 0\nmem max (kb): 53.98828125 \u00b1 0\nmem mean (kb): 53.98828125 \u00b1 0\n

Here you can see test report for default arguments set.

Run with multiple arguments set, multiple repeats and draw a chart on resources python ./benchmark/cases/cpu_burn.py -N 5 -P 1 3,0.00001 3,0.001 3,0.01

Report is:

Test execution timeout: 10.0\nTest execution measure period: 0.1\nTested function name: cpu_burn\nTested function description:\n    Do nothing, just burn cpu to check cpu load changed on sleep.\n\n:param benchmark: benchmark special parameter to communicate with executor\n    :param run_time: time limit to run this function\n:param sleep: time to sleep in loop\n\n:return: None\n\nTested function argument names: ['run_time', 'sleep']\nTested function argument default values: [10, 0.0001]\n== Report created 2020-04-27 15:38:17.849535 ==\nArguments are `(3, 1e-05)`\nNumber of runs: 5\nNumber of time terminated: 0\nTime passed (seconds): 3.0087939262390138 \u00b1 0.0001147521277690166\ncpu min (%): 0.0 \u00b1 0.0\ncpu max (%): 11.0 \u00b1 2.23606797749979\ncpu mean (%): 6.2 \u00b1 0.18257418583505522\nmem min (kb): 54.0265625 \u00b1 0.11180339887498948\nmem max (kb): 54.0265625 \u00b1 0.11180339887498948\nmem mean (kb): 54.0265625 \u00b1 0.11180339887498948\n== Report created 2020-04-27 15:38:32.947308 ==\nArguments are `(3, 0.001)`\nNumber of runs: 5\nNumber of time terminated: 0\nTime passed (seconds): 3.014109659194946 \u00b1 0.0004416575764579524\ncpu min (%): 0.0 \u00b1 0.0\ncpu max (%): 8.0 \u00b1 2.7386127875258306\ncpu mean (%): 1.9986666666666666 \u00b1 0.002981423969999689\nmem min (kb): 53.9890625 \u00b1 0.10431954926750306\nmem max (kb): 53.9890625 \u00b1 0.10431954926750306\nmem mean (kb): 53.9890625 \u00b1 0.10431954926750306\n== Report created 2020-04-27 15:38:48.067511 ==\nArguments are `(3, 0.01)`\nNumber of runs: 5\nNumber of time terminated: 0\nTime passed (seconds): 3.0181806087493896 \u00b1 0.0022409499756841883\ncpu min (%): 0.0 \u00b1 0.0\ncpu max (%): 1.0 \u00b1 2.23606797749979\ncpu mean (%): 0.06666666666666667 \u00b1 0.14907119849998599\nmem min (kb): 53.9078125 \u00b1 0.11487297672320501\nmem max (kb): 53.9078125 \u00b1 0.11487297672320501\nmem mean (kb): 53.9078125 \u00b1 0.11487297672320501\n

Chart is drawn for argument 1: sleep:

The most interesting part is CPU usage, as you can see CPU usage decreases with increasing value of idle sleep. Memory usage and execution time can slightly differ per case execution.

"},{"location":"aea-framework-documentation/performance-benchmark/#requirements-for-tested-function","title":"Requirements for Tested Function","text":"
  • The first function's argument has to be benchmark: BenchmarkControl which is passed by default by the framework.
  • All arguments except the fist one have to set default values.
  • Function doc string is required, it used for help information.
  • benchmark.start() has to be called once in the function body to start measurement. The timeout is counted from this point!
  • All the \"prepare part\" in the function that should not be measured has to be placed before benchmark.start()
  • Code to be measured has to go after benchmark.start()
  • Try to avoid infinitive loops and assume the test should exit after a while.
"},{"location":"aea-framework-documentation/performance-benchmark/#execution-options","title":"Execution Options","text":"
  • To pass an arguments set just provide it as a comma separated string like 10,0.1
  • To pass several argument sets just separate them by white space 10,0.1 20,0.2
  • --timeout FLOAT is test execution timeout in seconds. If the test takes more time, it will be terminated.
  • --period FLOAT is measurement interval in seconds, how often to make CPU and RAM usage measurements.
  • -N, --num-executions INTEGER - how many times to run the same argument set to make result more accurate.
  • -P, --plot INTEGER - Draw a chart, using values in the argument on the X axis, argument positions started with 0, argument benchmark not counted. For example -P 0 will use run_time values, -P 1 will use sleep values, and so on.
"},{"location":"aea-framework-documentation/performance-benchmark/#limitations","title":"Limitations","text":"

Currently, the benchmark framework does not measure resources consumed by subprocess spawned in python code. So try to keep one process solutions during tests.

Asynchronous functions or coroutines are not supported directly. So you have to set up an event loop inside test function and start loop manually.

"},{"location":"aea-framework-documentation/performance-benchmark/#testing-aea-handlers-example","title":"Testing AEA: Handlers Example","text":"

Test react speed on specific messages amount.

def react_speed_in_loop(benchmark: BenchmarkControl, inbox_amount=1000) -> None:\n\"\"\"\n    Test inbox message processing in a loop.\n    :param benchmark: benchmark special parameter to communicate with executor\n    :param inbox_amount: num of inbox messages for every agent\n    :return: None\n    \"\"\"\nskill_definition = {\n\"handlers\": {\"dummy_handler\": DummyHandler}\n}\naea_test_wrapper = AEATestWrapper(\nname=\"dummy agent\",\nskills=[skill_definition],\n)\nfor _ in range(inbox_amount):\naea_test_wrapper.put_inbox(aea_test_wrapper.dummy_envelope())\naea_test_wrapper.set_loop_timeout(0.0)\nbenchmark.start()\naea_test_wrapper.start_loop()\nwhile not aea_test_wrapper.is_inbox_empty():\ntime.sleep(0.1)\naea_test_wrapper.stop_loop()\n

Create AEA wrapper with specified handler:

skill_definition = {\n\"handlers\": {\"dummy_handler\": DummyHandler}\n}\naea_test_wrapper = AEATestWrapper(\nname=\"dummy agent\",\nskills=[skill_definition],\n)\n

Populate inbox with dummy messages:

for _ in range(inbox_amount):\naea_test_wrapper.put_inbox(aea_test_wrapper.dummy_envelope())\n

Set timeout 0, for maximum messages processing speed: aea_test_wrapper.set_loop_timeout(0.0)

Start benchmark: benchmark.start()

Start/stop AEA:

aea_test_wrapper.start()\n...\naea_test_wrapper.stop()\n

Wait till messages present in inbox:

while not aea_test_wrapper.is_inbox_empty():\ntime.sleep(0.1)\n
"},{"location":"aea-framework-documentation/por/","title":"Proof of Representation","text":"

An AEA can use several key pairs. In particular, it can use different keys for securing its communication and for engaging in exchange. In the ACN we make use of this fact. To be able to signal to other agents that the address derived from one key pair is allowed to represent the agent controlling the other key pair, the key pair which is being represented must sign a message to prove that the other key pair is allowed to represent it. The aea issue-certificates command allows to create this association.

The proof of representation feature is used in the context of the fetchai/p2p_libp2p and fetchai/p2p_libp2p_client connection.

In the former connection, the configuration YAML specifies a cert_requests field:

cert_requests:\n- identifier: acn\nledger_id: fetchai\nnot_after: '2023-01-01'\nnot_before: '2022-01-01'\npublic_key: fetchai\nmessage_format: '{public_key}'\nsave_path: .certs/conn_cert.txt\n

The identifier refers to the environment for which the signature is generated, here acn. The ledger_id refers to the key pair to be used from the private_key_paths specified in aea-config.yaml for signing. The not_after and not_before fields specify constraints on the validity of the signature. The public_key can specify either the identifier of the key pair in connection_private_key_paths, of which the public key is signed, or it can contain the to be signed public key in plain text. The save_path specifies the path where the certificate is to be saved at.

In the above example, the connection requests a certificate which is a signature of the fetchai public key in connection_private_key_paths with the fetchai key pair in private_key_paths. The validity of the signature will be constrained to the year 2021 for the environment acn.

"},{"location":"aea-framework-documentation/prometheus/","title":"Prometheus Monitoring","text":"

AEAs can create and update prometheus metrics for remote monitoring by sending messages to the prometheus connection fetchai/prometheus:0.9.6.

To see this working in an agent, fetch and run the coin_price_feed agent and check localhost:9090/metrics to see the latest values of the metrics num_retrievals and num_requests:

aea fetch fetchai/coin_price_feed:0.15.5\ncd coin_price_feed\naea install\naea build\naea run\n

You can then instruct a prometheus server running on the same computing cluster as a deployed agent to scrape these metrics for remote monitoring and visualisation with the Prometheus/Grafana toolset.

To use this connection, add a model prometheus_dialogues to your skill to handle the metrics configuration and messages to the prometheus connection.

Click here for example:
class PrometheusDialogues(Model, BasePrometheusDialogues):\n\"\"\"The dialogues class keeps track of all prometheus dialogues.\"\"\"\ndef __init__(self, **kwargs) -> None:\n\"\"\"\n        Initialize dialogues.\n        :return: None\n        \"\"\"\nself.enabled = kwargs.pop(\"enabled\", False)\nself.metrics = kwargs.pop(\"metrics\", [])\nModel.__init__(self, **kwargs)\ndef role_from_first_message(  # pylint: disable=unused-argument\nmessage: Message, receiver_address: Address\n) -> BaseDialogue.Role:\n\"\"\"Infer the role of the agent from an incoming/outgoing first message\n            :param message: an incoming/outgoing first message\n            :param receiver_address: the address of the receiving agent\n            :return: The role of the agent\n            \"\"\"\nreturn PrometheusDialogue.Role.AGENT\nBasePrometheusDialogues.__init__(\nself,\nself_address=str(self.skill_id),\nrole_from_first_message=role_from_first_message,\n)\n

Then configure your metrics in the skill.yaml file. For example (from the advanced_data_request skill):

models:\nprometheus_dialogues:\nargs:\nenabled: true\nmetrics:\n- name: num_retrievals\ntype: Gauge\ndescription: Number of price quotes retrieved\nlabels: {}\n- name: num_requests\ntype: Gauge\ndescription: Number of price quote requests served\nlabels: {}\nclass_name: PrometheusDialogues\n

Add a metric metric_name of type metric_type {Gauge, Counter, ...} and description description by sending a message with performative ADD_METRIC to the prometheus connection:

def add_prometheus_metric(\nself,\nmetric_name: str,\nmetric_type: str,\ndescription: str,\nlabels: Dict[str, str],\n) -> None:\n\"\"\"\n    Add a prometheus metric.\n    :param metric_name: the name of the metric to add.\n    :param type: the type of the metric.\n    :param description: a description of the metric.\n    :param labels: the metric labels.\n    :return: None\n    \"\"\"\n# context\nprom_dialogues = cast(PrometheusDialogues, self.context.prometheus_dialogues)\n# prometheus update message\nmessage, _ = prom_dialogues.create(\ncounterparty=str(PROM_CONNECTION_ID),\nperformative=PrometheusMessage.Performative.ADD_METRIC,\ntype=metric_type,\ntitle=metric_name,\ndescription=description,\nlabels=labels,\n)\n# send message\nself.context.outbox.put_message(message=message)\n

where PROM_CONNECTION_ID should be imported to your skill as follows:

from packages.fetchai.connections.prometheus.connection import (\nPUBLIC_ID as PROM_CONNECTION_ID,\n)\n

Update metric metric_name with update function update_func {inc, set, observe, ...} and value value by sending a message with performative UPDATE_METRIC to the prometheus connection:

def update_prometheus_metric(\nself, metric_name: str, update_func: str, value: float, labels: Dict[str, str],\n) -> None:\n\"\"\"\n    Update a prometheus metric.\n    :param metric_name: the name of the metric.\n    :param update_func: the name of the update function (e.g. inc, dec, set, ...).\n    :param value: the value to provide to the update function.\n    :param labels: the metric labels.\n    :return: None\n    \"\"\"\n# context\nprom_dialogues = cast(PrometheusDialogues, self.context.prometheus_dialogues)\n# prometheus update message\nmessage, _ = prom_dialogues.create(\ncounterparty=str(PROM_CONNECTION_ID),\nperformative=PrometheusMessage.Performative.UPDATE_METRIC,\ntitle=metric_name,\ncallable=update_func,\nvalue=value,\nlabels=labels,\n)\n# send message\nself.context.outbox.put_message(message=message)\n

Initialize the metrics from the configuration file in the behaviour setup:

def setup(self) -> None:\n\"\"\"Implement the setup of the behaviour\"\"\"\nprom_dialogues = cast(PrometheusDialogues, self.context.prometheus_dialogues)\nif prom_dialogues.enabled:\nfor metric in prom_dialogues.metrics:\nself.context.logger.info(\"Adding Prometheus metric: \" + metric[\"name\"])\nself.add_prometheus_metric(\nmetric[\"name\"], metric[\"type\"], metric[\"description\"], dict(metric[\"labels\"]),\n

Then call the update_prometheus_metric function from the appropriate places. For example, the following code in handlers.py for the advanced_data_request skill updates the number of http requests served:

if self.context.prometheus_dialogues.enabled:\nself.context.behaviours.advanced_data_request_behaviour.update_prometheus_metric(\n\"num_requests\", \"inc\", 1.0, {}\n)\n

Finally, you can add a PrometheusHandler to your skill to process response messages from the prometheus connection.

Click here for example:
class PrometheusHandler(Handler):\n\"\"\"This class handles responses from the prometheus server.\"\"\"\nSUPPORTED_PROTOCOL = PrometheusMessage.protocol_id\ndef __init__(self, **kwargs):\n\"\"\"Initialize the handler.\"\"\"\nsuper().__init__(**kwargs)\nself.handled_message = None\ndef setup(self) -> None:\n\"\"\"Set up the handler.\"\"\"\nif self.context.prometheus_dialogues.enabled:\nself.context.logger.info(\"setting up PrometheusHandler\")\ndef handle(self, message: Message) -> None:\n\"\"\"\n        Implement the reaction to a message.\n        :param message: the message\n        :return: None\n        \"\"\"\nmessage = cast(PrometheusMessage, message)\n# recover dialogue\nprometheus_dialogues = cast(\nPrometheusDialogues, self.context.prometheus_dialogues\n)\nprometheus_dialogue = cast(\nPrometheusDialogue, prometheus_dialogues.update(message)\n)\nif prometheus_dialogue is None:\nself._handle_unidentified_dialogue(message)\nreturn\nself.handled_message = message\nif message.performative == PrometheusMessage.Performative.RESPONSE:\nself.context.logger.debug(\nf\"Prometheus response ({message.code}): {message.message}\"\n)\nelse:\nself.context.logger.debug(\nf\"got unexpected prometheus message: Performative = {PrometheusMessage.Performative}\"\n)\ndef _handle_unidentified_dialogue(self, msg: Message) -> None:\n\"\"\"\n        Handle an unidentified dialogue.\n        :param msg: the unidentified message to be handled\n        :return: None\n        \"\"\"\nself.context.logger.info(\n\"received invalid message={}, unidentified dialogue.\".format(msg)\n)\ndef teardown(self) -> None:\n\"\"\"\n        Teardown the handler.\n        :return: None\n        \"\"\"\n
"},{"location":"aea-framework-documentation/protocol-generator/","title":"Generating Protocols","text":""},{"location":"aea-framework-documentation/protocol-generator/#how-to-run","title":"How to Run","text":"

First make sure you are inside your AEA's folder (see here on how to create a new agent).

Then run

aea generate protocol <path-to-protocol-specification>\n

where <path-to-protocol-specification> is the path to a protocol specification file.

If there are no errors, this command will generate the protocol and place it in your AEA project. The name of the protocol's directory will match the protocol name given in the specification. The author will match the registered author in the CLI. The generator currently produces the following files (assuming the name of the protocol in the specification is sample):

  1. message.py: defines messages valid under the sample protocol
  2. serialisation.py: defines how messages are serialized/deserialized
  3. __init__.py: makes the directory a package
  4. protocol.yaml: contains package information about the sample protocol
  5. sample.proto protocol buffer schema file
  6. sample_pb2.py: the generated protocol buffer implementation
  7. custom_types.py: stub implementations for custom types (created only if the specification contains custom types)
"},{"location":"aea-framework-documentation/protocol-generator/#full-mode-vs-protobuf-only-mode","title":"Full Mode vs Protobuf Only Mode","text":"

Currently, the generator can operate in full mode for Python, creating a complete protocol package (files 1 to 7 above) from a protocol specification. The generator also has a protobuf only mode which only creates the protocol buffer schema and implementation files (files 5 and 6 above). The languages supported in the protobuf only mode and their respective ids are below:

  • go: go
  • c++: cpp
  • java: java
  • c#: csharp
  • ruby: ruby
  • objective-c: objc
  • javascript: js

To use the generator in protobuf only mode for any of the above languages:

aea generate protocol --l <language> <path-to-protocol-specification>\n

where <language> is a language id.

The protocol buffer compiler requires a plugin to generate Go code. Install it with:

Note

Note the protocol buffer compiler protoc that the generator uses requires a plugin to produce go code. Follow this instruction.

"},{"location":"aea-framework-documentation/protocol-generator/#protocol-specification","title":"Protocol Specification","text":"

A protocol can be described in a YAML file. This is called a protocol specification. The following is an example protocol specification:

---\nname: two_party_negotiation\nauthor: fetchai\nversion: 0.1.0\ndescription: An example of a protocol specification that describes a protocol for bilateral negotiation.\nlicense: Apache-2.0\naea_version: '>=1.0.0, <2.0.0'\nspeech_acts:\ncfp:\nquery: ct:Query\npropose:\nprice: pt:float\nproposal: pt:dict[pt:str, pt:str]\nconditions: pt:optional[pt:union[pt:str, pt:dict[pt:str,pt:str], pt:set[pt:str]]]\nresources: pt:list[pt:bytes]\naccept: {}\ndecline: {}\n...\n---\nct:Query: |\nbytes query_bytes = 1;\n...\n---\ninitiation: [cfp]\nreply:\ncfp: [propose, decline]\npropose: [propose, accept, decline]\naccept: []\ndecline: []\ntermination: [accept, decline]\nroles: {buyer, seller}\nend_states: [agreement_reached, agreement_unreached]\nkeep_terminal_state_dialogues: true\n...\n

Each protocol specification must follow the YAML format, and have a minimum of one and a maximum of three YAML documents (each YAML document is enclosed within --- and ...).

"},{"location":"aea-framework-documentation/protocol-generator/#basic-protocol-detail-and-messages-syntax","title":"Basic Protocol Detail and Messages Syntax","text":"

The first YAML document is mandatory in any protocol specification. It contains some basic information about the protocol and describes the syntax of communicative messages allowed under this protocol.

The allowed fields and what they represent are:

  • name: The name of the protocol (written in snake_case)
  • author: The creator of the protocol
  • version: The current version of the protocol
  • license: Licensing information
  • aea_version: The version(s) of the framework that support this protocol. The format is described here.
  • description: A short description of the protocol
  • protocol_specification_id: The id which identifies the protocol for over-the-wire transport. This id is decoupled from the protocol_id ({author}/{name}:{version}) which is tied to the Python implementation.

All of the above fields are mandatory and each is a key/value pair, where both key and value are YAML strings.

Additionally, the first YAML document of a protocol specification must describe the syntax of valid messages according to this protocol. Therefore, it must contain another mandatory speech-acts field which defines the set of performatives valid under this protocol, and a set of contents for each performative.

A performative defines the type of a message (e.g. propose, accept) and has a set of contents (or parameters) of varying types.

The format of the speech-act is as follows: speech-act is a dictionary, where each key is a unique performative (YAML string), and the value is a content dictionary. If a performative does not have any content, then its content dictionary is empty, for instance accept and decline in the specification above.

A content dictionary in turn has key/value pairs, where each key is the name of a content (YAML string) and the value is its type (YAML string). For example, the cfp (short for 'call for proposal') performative has one content whose name is query and whose type is ct:Query.

"},{"location":"aea-framework-documentation/protocol-generator/#types","title":"Types","text":"

The specific types which could be assigned to contents in a protocol specification are described in the table below.

Types are either user defined (i.e. custom types) or primitive:

  • Custom types are prepended with ct: and their format is described using regular expression in the table below.
  • Primitive types are prepended with pt:. There are different categories of primitive types. For example, <PT> such as integers and booleans, <PCT> such as sets and lists, and so on. Primitive types are compositional:
    • For example, consider pt:set[...] under <PCT>, i.e. an unordered collection of elements without duplicates. A pt:set[...] describes the type of its elements (called \"sub-type\") in square brackets. The subtype of a pt:set[...] must be a <PT> (e.g. pt:int, pt:bool).
    • In describing the format of types, / between two subtypes should be treated as \"or\". For example, the subtype of a pt:optional[...] is either a <PT>, <CT>, <PCT>, <PMT> or <MT>.

A multi type denotes an \"or\" separated set of subtypes. For example, a content whose type is specified as pt:union[pt:str, pt:int] should either be pt:int or pt:float.

An optional type pt:optional[...] assigned to a content means the content's existence is optional, but if it is present, its type must match pt:optional[...]'s subtype.

Type Code Format Example In Python Custom types1 <CT> ct:RegExp(^[A-Z][a-zA-Z0-9]*$) ct:DataModel Custom Class Primitive types <PT> pt:bytes pt:bytes bytes pt:int pt:int int pt:float pt:float float pt:bool pt:bool bool pt:str pt:str str Primitive collection types <PCT> pt:set[<PT>] pt:set[pt:str] FrozenSet[str] pt:list[<PT>] pt:list[pt:int] Tuple[int, ...]* Primitive mapping types2 <PMT> pt:dict[<PT>, <PT>] pt:dict[pt:str, pt:bool] Dict[str, bool] Multi types <MT> pt:union[<PT>/<CT>/<PCT>/<PMT>, ..., <PT>/<CT>/<PCT>/<PMT>] pt:union[ct:DataModel, pt:set[pt:str]] Union[DataModel, FrozenSet[str]] Optional types <O> pt:optional[<MT>/<PMT>/<PCT>/<PT>/<CT>] pt:optional[pt:bool] Optional[bool]

* This is how variable length tuples containing elements of the same type are declared in Python; see here.

"},{"location":"aea-framework-documentation/protocol-generator/#protocol-buffer-schema","title":"Protocol Buffer Schema","text":"

Currently, the AEA framework does not officially support describing custom types in a programming language independent format. This means that if a protocol specification includes custom types, the required serialisation logic must be provided manually.

Therefore, if any of the contents declared in speech-acts is of a custom type, the specification must then have a second YAML document, containing the protocol buffer schema code for each custom type.

You can see an example of the second YAML document in the above protocol specification.

"},{"location":"aea-framework-documentation/protocol-generator/#dialogues","title":"Dialogues","text":"

You can optionally specify the structure of dialogues conforming to your protocol in a third YAML document in the specification.

The allowed fields and what they represent are:

  • initiation: The list of initial performatives
  • reply: The reply structure of speech-acts
  • termination: The list of terminal performatives
  • roles: The roles of players participating in a dialogue
  • end_states: The possible outcomes a terminated dialogue.
  • keep_terminal_state_dialogues: whether to keep or drop a terminated dialogue. When a storage backend is configured, the dialogues will be persisted in storage when kept.

All of the above fields are mandatory.

initiation is a YAML list, containing the performatives which can be used to start a dialogue.

reply specifies for every performative, what its valid replies are. If a performative per_1 is a valid reply to another per_2, this means a message with performative per_1 can target a message whose performative is per_2.

reply is a YAML dictionary, where the keys are the performatives (YAML string) defined in speech-acts. For each performative key, its value is a list of performatives which are defined to be a valid reply. For example, valid replies to cfp are propose and decline.

termination is a YAML list, containing the performatives which terminate a dialogue. Once any of these performatives are used in a dialogue, the dialogue is terminated and no other messages may be added to it.

roles is a YAML set, containing the roles players participating in dialogues can take. roles may contain one or two roles, each role being a YAML string. If there are two roles, each participant has a distinguished role in the dialogue (e.g. buyer and seller in the above specification). If there is only one role, then both participants in a dialogue have this same role.

end_states lists the final states a terminated dialogue may have. end_states is a YAML list of strings.

keep_terminal_state_dialogues has a boolean value and specifies whether the terminated dialogues of this protocol are to be kept or discarded.

"},{"location":"aea-framework-documentation/protocol-generator/#design-guidelines","title":"Design Guidelines","text":"
  1. initiation and termination cannot be empty.

  2. Make sure that when defining reply, you include every speech-act you specified under speech_acts. If any of the speech-acts does not have a reply, indicate that with an empty list [] similar to accept and decline in the specification above.

  3. If a speech-act is listed in termination, it must not have any replies in reply. The reason is simple: a terminal speech-act terminates a dialogue and so its reply can never be used.

  4. If a speech-act replies to no other speech-acts, it should be listed in initiation otherwise it could never be used in a dialogue (neither to a start a dialogue with, nor as a reply to another speech-act).

"},{"location":"aea-framework-documentation/protocol-generator/#notes","title":"Notes","text":"
  1. Currently, there is no way to describe custom types in a programming language independent format. This means that if a protocol specification includes custom types, the required implementations must be provided manually. _ Before generating the protocol, the protocol buffer schema code for every custom type must be provided in the protocol specification.
    • Once the generator is called, it produces a custom_types module containing stub implementations for every custom type in the specification. The user must then modify this module and add implementations for every custom type in the specification. This includes implementations of how an object of a custom type can be encoded and decoded using protocol buffer.
    • Note, currently the way custom types are dealt with in the generator is admittedly inconvenient. The reason is, the generator does not know the structure of custom types and how they may be serialized/deserialized. Although this approach works, it is only a temporary solution until further work on a programming language-independent type description language is finished (similar to how the generator is designed to be a programming language-independent protocol description language).
  2. Currently, the first element in pt:dict cannot be a <CT>, pt:float or pt:bytes. This is because of a constraint in protocol buffer version 3 which is the framework's underlying serialisation mechanism. In a future version, we may address this limitation, in which case we will relax this constraint.
  3. In protocol buffer version 3, which is the version used by the generator, there is no way to check whether an optional field (i.e. contents of type pt:optional[...]) has been set or not (see discussion here). In proto3, all optional fields are assigned a default value (e.g. 0 for integers types, false for boolean types, etc). Therefore, given an optional field whose value is the default value, there is no way to know from the optional field itself, whether it is not set, or in fact is set but its value happens to be the default value. Because of this, in the generated protocol schema file (the .proto file), for every optional content there is a second field that declares whether this field is set or not. We will maintain this temporary solution until a cleaner alternative is found.
  4. Be aware that currently, using the generated protocols in python, there might be some rounding errors when serialising and then deserializing values of pt:float contents.
"},{"location":"aea-framework-documentation/protocol-generator/#demo-instructions","title":"Demo Instructions","text":"

First, create a new AEA project:

aea create my_aea\ncd my_aea\n

Second, run the generator on the sample specification:

aea generate protocol ../examples/protocol_specification_ex/sample.yaml\n

This will generate the protocol and place it in your AEA project.

Third, try generating other protocols by first defining a specification, then running the generator.

"},{"location":"aea-framework-documentation/protocol/","title":"Protocols","text":"

Protocols define the structure of agent-to-agent and component-to-component interactions, which in the AEA world, are in the form of communication. To learn more about interactions and interaction protocols, see here.

Protocols in the AEA world provide definitions for:

  • messages defining the structure and syntax of messages;
  • serialization defining how a message is encoded/decoded for transport; and optionally
  • dialogues defining the structure of dialogues formed from exchanging series of messages.

The framework provides a default protocol. This protocol provides a bare-bones implementation for an AEA protocol which includes a DefaultMessage class and associated DefaultSerializer and DefaultDialogue classes.

Additional protocols - i.e. a new type of interaction - can be added as packages or generated with the protocol generator.

We highly recommend you to not attempt writing your protocol manually as they tend to have involved logic; always use existing packages or the protocol generator!

"},{"location":"aea-framework-documentation/protocol/#components-of-a-protocol","title":"Components of a Protocol","text":"

A protocol package contains the following files:

  • __init__.py
  • message.py, which defines message representation
  • serialization.py, which defines the encoding and decoding logic
  • two protobuf related files

It optionally also contains

  • dialogues.py, which defines the structure of dialogues formed from the exchange of a series of messages
  • custom_types.py, which defines custom types

All protocols are for point to point interactions between two agents or agent-like services.

"},{"location":"aea-framework-documentation/protocol/#metadata","title":"Metadata","text":"

Each Message in an interaction protocol has a set of default fields:

  • dialogue_reference: Tuple[str, str], a reference of the dialogue the message is part of. The first part of the tuple is the reference assigned to by the agent who first initiates the dialogue (i.e. sends the first message). The second part of the tuple is the reference assigned to by the other agent. The default value is (\"\", \"\").
  • message_id: int, the identifier of the message in a dialogue. The default value is 1.
  • target: int, the id of the message this message is replying to. The default value is 0.
  • performative: Enum, the purpose/intention of the message.
  • sender: Address, the address of the sender of this message.
  • to: Address, the address of the receiver of this message.

The default values for message_id and target assume the message is the first message in a dialogue. Therefore, the message_id is set to 1 indicating the first message in the dialogue and target is 0 since the first message is the only message that does not reply to any other.

By default, the values of dialogue_reference, message_id, target are set. However, most interactions involve more than one message being sent as part of the interaction and potentially multiple simultaneous interactions utilising the same protocol. In those cases, the dialogue_reference allows different interactions to be identified as such. The message_id and target are used to keep track of messages and their replies. For instance, on receiving of a message with message_id=1 and target=0, the responding agent could respond with another with message_id=2 and target=1 replying to the first message. In particular, target holds the id of the message being replied to. This can be the preceding message, or an older one.

"},{"location":"aea-framework-documentation/protocol/#contents","title":"Contents","text":"

Each message may optionally have any number of contents of varying types.

"},{"location":"aea-framework-documentation/protocol/#dialogue-rules","title":"Dialogue Rules","text":"

Protocols can optionally have a dialogue module. A dialogue, respectively dialogues object, maintains the state of a single, respectively, all dialogues associated with a protocol.

The framework provides a number of helpful classes which implement most of the logic to maintain dialogues, namely the Dialogue and Dialogues base classes.

"},{"location":"aea-framework-documentation/protocol/#custom-protocol","title":"Custom Protocol","text":"

The developer can generate custom protocols with the protocol generator. This lets the developer specify the speech-acts as well as optionally the dialogue structure (e.g. roles of agents participating in a dialogue, the states a dialogue may end in, and the reply structure of the speech-acts in a dialogue).

We highly recommend you do not attempt to write your own protocol code; always use existing packages or the protocol generator!

"},{"location":"aea-framework-documentation/protocol/#fetchaidefault117-protocol","title":"fetchai/default:1.1.7 Protocol","text":"

The fetchai/default:1.1.7 protocol is meant to be implemented by every AEA. It serves AEA to AEA interaction and includes three message performatives:

from enum import Enum\nclass Performative(Enum):\n\"\"\"Performatives for the default protocol.\"\"\"\nBYTES = \"bytes\"\nEND = \"end\"\nERROR = \"error\"\ndef __str__(self):\n\"\"\"Get the string representation.\"\"\"\nreturn self.value\n
  • The DefaultMessage of performative DefaultMessage.Performative.BYTES is used to send payloads of byte strings to other AEAs. An example is:
from packages.fetchai.protocols.default.message import DefaultMessage\nmsg = DefaultMessage(\nperformative=DefaultMessage.Performative.BYTES,\ncontent=b\"This is a bytes payload\",\n)\n
  • The DefaultMessage of performative DefaultMessage.Performative.ERROR is used to notify other AEAs of errors in an interaction, including errors with other protocols, by including an error_code in the payload:
class ErrorCode(Enum):\n\"\"\"This class represents an instance of ErrorCode.\"\"\"\nUNSUPPORTED_PROTOCOL = 0\nDECODING_ERROR = 1\nINVALID_MESSAGE = 2\nUNSUPPORTED_SKILL = 3\nINVALID_DIALOGUE = 4\n

An example is:

msg = DefaultMessage(\nperformative=DefaultMessage.Performative.ERROR,\nerror_code=DefaultMessage.ErrorCode.UNSUPPORTED_PROTOCOL,\nerror_msg=\"This protocol is not supported by this AEA.\",\nerror_data={\"unsupported_msg\": b\"serialized unsupported protocol message\"},\n)\n
  • The DefaultMessage of performative DefaultMessage.Performative.END is used to terminate a default protocol dialogue. An example is:
from packages.fetchai.protocols.default.message import DefaultMessage\nmsg = DefaultMessage(\nperformative=DefaultMessage.Performative.END,\n)\n

Each AEA's fetchai/error:0.18.6 skill utilises the fetchai/default:1.0.0 protocol for error handling.

"},{"location":"aea-framework-documentation/protocol/#fetchaioef_search117-protocol","title":"fetchai/oef_search:1.1.7 Protocol","text":"

The fetchai/oef_search:1.1.7 protocol is used by AEAs to interact with an SOEF search node to register and unregister their own services and search for services registered by other agents.

The fetchai/oef_search:1.1.7 protocol definition includes an OefSearchMessage with the following message types:

class Performative(Enum):\n\"\"\"Performatives for the oef_search protocol.\"\"\"\nREGISTER_SERVICE = \"register_service\"\nUNREGISTER_SERVICE = \"unregister_service\"\nSEARCH_SERVICES = \"search_services\"\nOEF_ERROR = \"oef_error\"\nSEARCH_RESULT = \"search_result\"\nSUCCESS = \"success\"\ndef __str__(self):\n\"\"\"Get string representation.\"\"\"\nreturn self.value\n

We show some example messages below:

  • To register a service, we require a reference to the dialogue in string form (used to keep different dialogues apart), for instance
my_dialogue_reference = \"a_unique_register_service_dialogue_reference\"\n

and a description of the service we would like to register, for instance

from aea.helpers.search.models import Description\nmy_service_data = {\"country\": \"UK\", \"city\": \"Cambridge\"}\nmy_service_description = Description(\nmy_service_data,\ndata_model=my_data_model,\n)\n

where we use, for instance

from aea.helpers.search.generic import GenericDataModel\ndata_model_name = \"location\"\ndata_model = {\n\"attribute_one\": {\n\"name\": \"country\",\n\"type\": \"str\",\n\"is_required\": True,\n},\n\"attribute_two\": {\n\"name\": \"city\",\n\"type\": \"str\",\n\"is_required\": True,\n},\n}\nmy_data_model = GenericDataModel(data_model_name, data_model)\n

We can then create the message to register this service:

msg = OefSearchMessage(\nperformative=OefSearchMessage.Performative.REGISTER_SERVICE,\ndialogue_reference=(my_dialogue_reference, \"\"),\nservice_description=my_service_description,\n)\n
  • To unregister a service, we require a reference to the dialogue in string form, for instance
my_dialogue_reference = \"a_unique_unregister_service_dialogue_reference\"\n

the description of the service we would like to unregister, say my_service_description from above and construct the message:

msg = OefSearchMessage(\nperformative=OefSearchMessage.Performative.UNREGISTER_SERVICE,\ndialogue_reference=(my_dialogue_reference, \"\"),\nservice_description=my_service_description,\n)\n
  • To search a service, we similarly require a reference to the dialogue in string form, and then the query we would like the search node to evaluate, for instance
from aea.helpers.search.models import Constraint, ConstraintType, Query\nquery_data = {\n\"search_term\": \"country\",\n\"search_value\": \"UK\",\n\"constraint_type\": \"==\",\n}\nquery = Query(\n[\nConstraint(\nquery_data[\"search_term\"],\nConstraintType(\nquery_data[\"constraint_type\"],\nquery_data[\"search_value\"],\n),\n)\n],\nmodel=None,\n)\n

We can then create the message to search these services:

oef_msg = OefSearchMessage(\nperformative=OefSearchMessage.Performative.SEARCH_SERVICES,\ndialogue_reference=(my_dialogue_reference, \"\"),\nquery=query,\n)\n
  • The SOEF search node will respond with a message msg of type OefSearchMessage with performative OefSearchMessage.Performative.SEARCH_RESULT. To access the tuple of agents which match the query, simply use msg.agents. In particular, this will return the agent addresses matching the query. The agent address can then be used to send a message to the agent utilising the P2P agent communication network and any protocol other than fetchai/oef_search:1.0.0.

  • If the SOEF search node encounters any errors with the messages you send, it will return an OefSearchMessage of performative OefSearchMessage.Performative.OEF_ERROR and indicate the error operation encountered:

class OefErrorOperation(Enum):\n\"\"\"This class represents an instance of OefErrorOperation.\"\"\"\nREGISTER_SERVICE = 0\nUNREGISTER_SERVICE = 1\nSEARCH_SERVICES = 2\nSEND_MESSAGE = 3\nOTHER = 10000\n
"},{"location":"aea-framework-documentation/protocol/#fetchaifipa117-protocol","title":"fetchai/fipa:1.1.7 Protocol","text":"

This protocol provides classes and functions necessary for communication between AEAs via a variant of the FIPA Agent Communication Language.

The fetchai/fipa:1.1.7 protocol definition includes a FipaMessage with the following performatives:

class Performative(Enum):\n\"\"\"Performatives for the fipa protocol.\"\"\"\nACCEPT = \"accept\"\nACCEPT_W_INFORM = \"accept_w_inform\"\nCFP = \"cfp\"\nDECLINE = \"decline\"\nEND = \"end\"\nINFORM = \"inform\"\nMATCH_ACCEPT = \"match_accept\"\nMATCH_ACCEPT_W_INFORM = \"match_accept_w_inform\"\nPROPOSE = \"propose\"\ndef __str__(self):\n\"\"\"Get the string representation.\"\"\"\nreturn self.value\n

FipaMessages are constructed with a performative, dialogue_reference, message_id, and target as well as the kwargs specific to each message performative.

def __init__(\nself,\nperformative: Performative,\ndialogue_reference: Tuple[str, str] = (\"\", \"\"),\nmessage_id: int = 1,\ntarget: int = 0,\n**kwargs,\n)\n

The fetchai/fipa:1.1.7 protocol also defines a FipaDialogue class which specifies the valid reply structure and provides other helper methods to maintain dialogues.

For examples of the usage of the fetchai/fipa:1.1.7 protocol check out the generic skills step by step guide.

"},{"location":"aea-framework-documentation/protocol/#fipa-dialogue","title":"Fipa Dialogue","text":"

Below, we give an example of a dialogue between two agents. In practice; both dialogues would be maintained in the respective agent.

We first create concrete implementations of FipaDialogue and FipaDialogues for the buyer and seller:

from aea.common import Address\nfrom aea.helpers.search.models import Constraint, ConstraintType, Description, Query\nfrom aea.mail.base import Envelope\nfrom aea.protocols.base import Message\nfrom aea.protocols.dialogue.base import Dialogue as BaseDialogue\nfrom aea.protocols.dialogue.base import DialogueLabel\nfrom packages.fetchai.protocols.fipa.dialogues import FipaDialogue, FipaDialogues\nfrom packages.fetchai.protocols.fipa.message import FipaMessage\nclass BuyerDialogue(FipaDialogue):\n\"\"\"The dialogue class maintains state of a dialogue and manages it.\"\"\"\ndef __init__(\nself,\ndialogue_label: DialogueLabel,\nself_address: Address,\nrole: BaseDialogue.Role,\nmessage_class: Type[FipaMessage] = FipaMessage,\n) -> None:\n\"\"\"\n        Initialize a dialogue.\n        :param dialogue_label: the identifier of the dialogue\n        :param self_address: the address of the entity for whom this dialogue is maintained\n        :param role: the role of the agent this dialogue is maintained for\n        :return: None\n        \"\"\"\nFipaDialogue.__init__(\nself,\ndialogue_label=dialogue_label,\nself_address=self_address,\nrole=role,\nmessage_class=message_class,\n)\nself.proposal = None  # type: Optional[Description]\nclass BuyerDialogues(FipaDialogues):\n\"\"\"The dialogues class keeps track of all dialogues.\"\"\"\ndef __init__(self, self_address: Address) -> None:\n\"\"\"\n        Initialize dialogues.\n        :return: None\n        \"\"\"\ndef role_from_first_message(\nmessage: Message, receiver_address: Address\n) -> BaseDialogue.Role:\n\"\"\"Infer the role of the agent from an incoming/outgoing first message\n            :param message: an incoming/outgoing first message\n            :param receiver_address: the address of the receiving agent\n            :return: The role of the agent\n            \"\"\"\nreturn BaseFipaDialogue.Role.BUYER\nFipaDialogues.__init__(\nself,\nself_address=self_address,\nrole_from_first_message=role_from_first_message,\ndialogue_class=FipaDialogue,\n)\nclass SellerDialogue(FipaDialogue):\n\"\"\"The dialogue class maintains state of a dialogue and manages it.\"\"\"\ndef __init__(\nself,\ndialogue_label: DialogueLabel,\nself_address: Address,\nrole: BaseDialogue.Role,\nmessage_class: Type[FipaMessage] = FipaMessage,\n) -> None:\n\"\"\"\n        Initialize a dialogue.\n        :param dialogue_label: the identifier of the dialogue\n        :param self_address: the address of the entity for whom this dialogue is maintained\n        :param role: the role of the agent this dialogue is maintained for\n        :return: None\n        \"\"\"\nFipaDialogue.__init__(\nself,\ndialogue_label=dialogue_label,\nself_address=self_address,\nrole=role,\nmessage_class=message_class,\n)\nself.proposal = None  # type: Optional[Description]\nclass SellerDialogues(FipaDialogues):\n\"\"\"The dialogues class keeps track of all dialogues.\"\"\"\ndef __init__(self, self_address: Address) -> None:\n\"\"\"\n        Initialize dialogues.\n        :return: None\n        \"\"\"\ndef role_from_first_message(\nmessage: Message, receiver_address: Address\n) -> BaseDialogue.Role:\n\"\"\"Infer the role of the agent from an incoming/outgoing first message\n            :param message: an incoming/outgoing first message\n            :param receiver_address: the address of the receiving agent\n            :return: The role of the agent\n            \"\"\"\nreturn FipaDialogue.Role.SELLER\nFipaDialogues.__init__(\nself,\nself_address=self_address,\nrole_from_first_message=role_from_first_message,\ndialogue_class=FipaDialogue,\n)\n

Next, we can imitate a dialogue between the buyer and the seller. We first instantiate the dialogues models:

buyer_address = \"buyer_address_stub\"\nseller_address = \"seller_address_stub\"\nbuyer_dialogues = BuyerDialogues(buyer_address)\nseller_dialogues = SellerDialogues(seller_address)\n

First, the buyer creates a message destined for the seller and updates the dialogues:

cfp_msg = FipaMessage(\nmessage_id=1,\ndialogue_reference=buyer_dialogues.new_self_initiated_dialogue_reference(),\ntarget=0,\nperformative=FipaMessage.Performative.CFP,\nquery=Query([Constraint(\"something\", ConstraintType(\">\", 1))]),\n)\ncfp_msg.counterparty = seller_addr\n# Extends the outgoing list of messages.\nbuyer_dialogue = buyer_dialogues.update(cfp_msg)\n

If the message has been correctly constructed, the buyer_dialogue will be returned, otherwise it will be None.

In a skill, the message could now be sent:

# In a skill we would do:\n# self.context.outbox.put_message(message=cfp_msg)\n

However, here we simply continue with the seller:

# change the incoming message field & counterparty\ncfp_msg.is_incoming = True\ncfp_msg.counterparty = buyer_address\n

In the skill, the above two lines will be done by the framework; you can simply receive the message in the handler.

We update the seller's dialogues model next to generate a new dialogue:

# Creates a new dialogue for the seller side based on the income message.\nseller_dialogue = seller_dialogues.update(cfp_msg)\n

Next, the seller can generate a proposal:

# Generate a proposal message to send to the buyer.\nproposal = Description({\"foo1\": 1, \"bar1\": 2})\nmessage_id = cfp_msg.message_id + 1\ntarget = cfp_msg.message_id\nproposal_msg = FipaMessage(\nmessage_id=message_id,\ndialogue_reference=seller_dialogue.dialogue_label.dialogue_reference,\ntarget=target,\nperformative=FipaMessage.Performative.PROPOSE,\nproposal=proposal,\n)\nproposal_msg.counterparty = cfp_msg.counterparty\n# Then we update the dialogue\nseller_dialogue.update(proposal_msg)\n

In a skill, the message could now be sent:

# In a skill we would do:\n# self.context.outbox.put_message(message=proposal_msg)\n

The dialogue can continue like this.

To retrieve a dialogue for a given message, we can do the following:

retrieved_dialogue = seller_dialogues.get_dialogue(cfp_msg)\n
"},{"location":"aea-framework-documentation/query-language/","title":"The Query Language","text":"

We recommend reading Defining a Data Model before reading this section.

Along with the Data Model language, the AEA framework offers the possibility to specify queries defined over data models.

The aea.helpers.search module implements the API that allows you to build queries.

In one sentence, a Query is a set of constraints, defined over a data model. The outcome is a set of description (that is, instances of Description) matching with the query. That is, all the description whose attributes satisfy the constraints in the query.

In the next sections, we describe how to build queries.

"},{"location":"aea-framework-documentation/query-language/#constraints","title":"Constraints","text":"

A Constraint is associated with an attribute name and imposes restrictions on the domain of that attribute. That is, it imposes some limitations on the values the attribute can assume.

We have different types of constraints:

  • relation constraints:

  • the author of the book must be Stephen King

  • the publication year must be greater than 1990

  • set constraints:

  • the genre must fall into the following set of genres: Horror, Science fiction, Non-fiction.

  • range constraints:

  • the average rating must be between 3.5 and 4.5

  • distance constraints:

  • the nearest bookshop must be within a distance from a given location.

The class that implements the constraint concept is Constraint In the following, we show how to define them.

"},{"location":"aea-framework-documentation/query-language/#relation","title":"Relation","text":"

There are several ConstraintTypes that allows you to impose specific values for the attributes.

The types of relation constraints are:

  • Equal: ==
  • Not Equal: !=
  • Less than: <
  • Less than or Equal: <=
  • Greater than: >
  • Greater than or Equal: >=

Examples: using the attributes we used before:

from aea.helpers.search.models import Constraint, ConstraintType\n# all the books whose author is Stephen King\nConstraint(\"author\", ConstraintType(\"==\", \"Stephen King\"))\n# all the books that are not of the genre Horror\nConstraint(\"genre\", ConstraintType(\"!=\", \"Horror\"))\n# all the books published before 1990\nConstraint(\"year\", ConstraintType(\"<\", 1990))\n# the same of before, but including 1990\nConstraint(\"year\", ConstraintType(\"<=\", 1990))\n# all the books with rating greater than 4.0\nConstraint(\"average_rating\", ConstraintType(\">\", 4.0))\n# all the books published after 2000, included\nConstraint(\"year\", ConstraintType(\">=\", 2000))\n
"},{"location":"aea-framework-documentation/query-language/#set","title":"Set","text":"

The set is a constraint type that allows you to restrict the values of the attribute in a specific set.

There are two kind of set constraints:

  • In (a set of values): in
  • Not in (a set of values): not_in

Examples:

from aea.helpers.search.models import Constraint, ConstraintType\n# all the books whose genre is one of `Horror`, `Science fiction`, `Non-fiction`\nConstraint(\"genre\", ConstraintType(\"in\", (\"horror\", \"science fiction\", \"non-fiction\")))\n# all the books that have not been published neither in 1990, nor in 1995, nor in 2000\nConstraint(\"year\", ConstraintType(\"not_in\", (1990, 1995, 2000)))\n
"},{"location":"aea-framework-documentation/query-language/#range","title":"Range","text":"

The range is a constraint type that allows you to restrict the values of the attribute in a given range.

Examples:

from aea.helpers.search.models import Constraint, ConstraintType\n# all the books whose title is between 'A' and 'B' (alphanumeric order)\nConstraint(\"title\", ConstraintType(\"within\", (\"A\", \"B\")))\n# all the books that have been published between 1960 and 1970\nConstraint(\"genre\", ConstraintType(\"within\", (1960, 1970)))\n
"},{"location":"aea-framework-documentation/query-language/#distance","title":"Distance","text":"

The distance is a constraint type that allows you to put a limit on a Location attribute type. More specifically, you can set a maximum distance from a given location (the centre), such that will be considered only the instances whose location attribute value is within a distance from the centre.

Examples:

from aea.helpers.search.models import Constraint, ConstraintType, Description, Location\n# define a location of interest, e.g. the Tour Eiffel\ntour_eiffel = Location(48.8581064, 2.29447)\n# find all the locations close to the Tour Eiffel within 1 km\nclose_to_tour_eiffel = Constraint(\"position\", ConstraintType(\"distance\", (tour_eiffel, 1.0)))\n# Le Jules Verne, a famous restaurant close to the Tour Eiffel, satisfies the constraint.\nle_jules_verne_restaurant = Location(48.8579675, 2.2951849)\nclose_to_tour_eiffel.check(Description({\"position\": le_jules_verne_restaurant}))  # gives `True`\n# The Colosseum does not satisfy the constraint (farther than 1 km from the Tour Eiffel).\ncolosseum = Location(41.8902102, 12.4922309)\nclose_to_tour_eiffel.check(Description({\"position\": colosseum}))  # gives `False`\n
"},{"location":"aea-framework-documentation/query-language/#constraint-expressions","title":"Constraint Expressions","text":"

The constraints mentioned above can be combined with the common logical operators (i.e. and, or and not), yielding more complex expression.

In particular, we can specify any conjunction/disjunction/negations of the previous constraints or composite ConstraintExpressions, e.g.:

  • books that belong to Horror and has been published after 2000, but not published by Stephen King.
  • books whose author is either J. K. Rowling or J. R. R. Tolkien

The classes that implement these operators are Not, And and Or.

"},{"location":"aea-framework-documentation/query-language/#not","title":"Not","text":"

The Not is a constraint expression that allows you to specify a negation of a constraint expression. The Not constraint is satisfied whenever its subexpression is not satisfied.

Example:

from aea.helpers.search.models import Constraint, ConstraintType, Not\n# all the books whose year of publication is not between 1990 and 2000\nNot(Constraint(\"year\", ConstraintType(\"within\", (1990, 2000))))\n
"},{"location":"aea-framework-documentation/query-language/#and","title":"And","text":"

The And is a constraint type that allows you to specify a conjunction of constraints over an attribute. That is, the And constraint is satisfied whenever all the subexpressions that constitute the and are satisfied.

Notice: the number of subexpressions must be at least 2.

Example:

from aea.helpers.search.models import Constraint, ConstraintType, And\n# all the books whose title is between 'I' and 'J' (alphanumeric order) but not equal to 'It'\nAnd([Constraint(\"title\", ConstraintType(\"within\", (\"I\", \"J\"))), Constraint(\"title\", ConstraintType(\"!=\", \"It\"))])\n
"},{"location":"aea-framework-documentation/query-language/#or","title":"Or","text":"

The class Or is a constraint type that allows you to specify a disjunction of constraints. That is, the Or constraint is satisfied whenever at least one of the constraints that constitute the or is satisfied.

Notice: the number of subexpressions must be at least 2.

Example:

from aea.helpers.search.models import Constraint, ConstraintType, Or\n# all the books that have been published either before the year 1960 or after the year 1970\nOr([Constraint(\"year\", ConstraintType(\"<\", 1960)), Constraint(\"year\", ConstraintType(\">\", 1970))])\n
"},{"location":"aea-framework-documentation/query-language/#queries","title":"Queries","text":"

A query is simply a list of constraint expressions, interpreted as a conjunction (that is, a matching description with the query must satisfy every constraint expression.)

Examples:

from aea.helpers.search.models import Query, Constraint, ConstraintType\n# query all the books written by Stephen King published after 1990, and available as an e-book:\nQuery([\nConstraint(\"author\", ConstraintType(\"==\", \"Stephen King\")),\nConstraint(\"year\", ConstraintType(\">=\", 1990)),\nConstraint(\"ebook_available\", ConstraintType(\"==\", True))\n], book_model)\n

Where book_model is the DataModel object. However, the data model is an optional parameter, but to avoid ambiguity is recommended to include it.

"},{"location":"aea-framework-documentation/query-language/#the-check-method","title":"The check Method","text":"

The Query class supports a way to check whether a Description matches with the query. This method is called Query.check.

Examples:

from aea.helpers.search.models import Query, Constraint, ConstraintType\nfrom aea.helpers.search.models import Description\nq = Query([\nConstraint(\"author\", ConstraintType(\"==\", \"Stephen King\")),\nConstraint(\"year\", ConstraintType(\">=\", 1990)),\nConstraint(\"ebook_available\", ConstraintType(\"==\", True))\n])\n# With a query, you can check that a `Description` object satisfies the constraints.\nq.check(Description({\"author\": \"Stephen King\", \"year\": 1991, \"ebook_available\": True}))  # True\nq.check(Description({\"author\": \"George Orwell\", \"year\": 1948, \"ebook_available\": False})) # False\n
"},{"location":"aea-framework-documentation/query-language/#validity","title":"Validity","text":"

A Query object must satisfy some conditions in order to be instantiated.

  • The list of constraints expressions can't be empty; must have at least one constraint expression.
  • If the data model is specified:

    • For every constraint expression that constitute the query, check if they are valid with respect to the data model.

A ConstraintExpr c (that is, one of And, Or, Not, Constraint) is valid with respect to a DataModel if:

  • If c is an instance of And, Or or Not, then every subexpression of c must be valid (with respect to the data model);
  • If c is an instance of Constraint, then:

    • if the constraint type is one of <, <=, >, >=, the value in the constructor must be one of str, int or float.
    • if the constraint type is a within, then the types in the range must be one of int, str, float or Location.
    • if the constraint type is a distance, then the only valid type is Location.
    • if the constraint type is a in, then the types supported are str, int, float, bool, Location. Notice though that a set of bool is trivial, so you may find yourself more comfortable by using other alternatives.
    • for the other constraint types, i.e. == and !=, the value can be one of the allowed types for Attribute, that is str, int, float, bool, Location.
  • Moreover, when c is a Constraint, the attribute must have a consistent type with respect to the data model. E.g. consider a Constraint like:

Constraint(\"foo\", ConstraintType(\"==\", True))\n

Consider a DataModel where there is an Attribute \"foo\" of type str. Then the constraint is not compatible with the mentioned data model, because the constraint expect an equality comparison with a boolean True, instead of a str.

"},{"location":"aea-framework-documentation/questions-and-answers/","title":"Q&A","text":"What is an AEA?

AEA stands for \"Autonomous Economic Agent\". An AEA can represent an individual, organisation or object and looks after its owner's interests. AEAs act independently of constant user input and autonomously execute actions to achieve their prescribed goals. Their purpose is to create economic value for their owners.

How do AEAs talk to each other when they do not know each other?

For an Autonomous Economic Agent (AEA) to talk to other AEAs, it first needs to find them. Once it does, it should ensure that they both use the same protocol for communication, and if so, they then have to send messages to each other.

The AEA framework, together with some of the services it provides, address all three problems. You can read more about search and discovery here, protocols here, and the Agent Communication Network (ACN) here.

How does an AEA use blockchain?

The AEA framework enables agents to interact with blockchains to settle transactions. Currently, the framework has native support for three different networks: Fetch.ai, Ethereum and Cosmos.

You can read more about the framework's integration with the different blockchains here and gain a high level overview here.

How does one install third party libraries?

The framework supports the use of third-party libraries hosted on PyPI. You can directly reference the external dependencies of an AEA package (e.g. skill) in its configuration file. From inside an AEA's project directory, the install command can be used to install all the dependencies of the AEA which are listed in the configuration files belonging to any of its packages.

How does one connect to a database?

You have two options to connect to a database: using the built-in storage solution or using a custom ORM (object-relational mapping) library and backend.

The use of the built-in storage is explained here. For a detailed example of how to use an ORM, follow the ORM guide.

How does one connect a frontend?

There are multiple options. The most obvious is using an HTTP server connection and creating a client that communicates with this connection.

You can find a more detailed discussion here.

Is the AEA framework ideal for agent-based modelling?

The goal of agent-based modelling (ABM) is to study the unknown (often complex) behaviour of systems comprised of agents with known (much simpler) behaviour. ABM is a popular technique for studying biological and social systems. Despite some similarities between ABM and the AEA framework, the two have fundamentally different goals. ABM's goal is not the design of agents or solving specific practical or engineering problems. Although it would be potentially possible, it would likely be inefficient to use the AEA framework for that kind of problems.

You can find more details on the application areas of the AEA framework here.

When a new AEA is created, is the vendor folder populated with some default packages?

All AEA projects by default hold the fetchai/default:1.1.7, fetchai/state_update:1.1.7 and fetchai/signing:1.1.7 protocols. These (as all other packages installed from the registry) are placed in the vendor folder.

You can find more details about the file structure here.

Is there a standardization for private key files?

Currently, the private keys are stored in .txt files. This is temporary and will be improved soon.

How to use the same protocol in different skills?

The details of envelope/message routing by the AEA framework are discussed in this guide.

Why does the AEA framework use its own package registry?

AEA packages could be described as personalized plugins for the AEA runtime. They are not like a library - they have no direct use outside the context of the framework - and therefore are not suitable for distribution via PyPI.

"},{"location":"aea-framework-documentation/quickstart/","title":"AEA Quick Start","text":"

If you want to create Autonomous Economic Agents (AEAs) that can act independently of constant user input and autonomously execute actions to achieve their objective, you can use the AEA framework.

This example will take you through a simple AEA to familiarise you with the basics of the framework.

"},{"location":"aea-framework-documentation/quickstart/#echo-skill-demo","title":"Echo Skill Demo","text":"

This is a simple demo that introduces you to the main components of an AEA.

The fastest way to have your first AEA is to fetch one that already exists!

aea fetch fetchai/my_first_aea:0.28.5\ncd my_first_aea\n

To learn more about the folder structure of an AEA project read on here.

Alternatively: step by step install:

Create a new AEA

First, create a new AEA project and enter it.

aea create my_first_aea\ncd my_first_aea\n

Add the stub connection

Second, add the stub connection to the project.

aea add connection fetchai/stub:0.21.3\n

Add the echo skill

Third, add the echo skill to the project.

aea add skill fetchai/echo:0.20.6\n

This copies the fetchai/echo:0.20.6 skill code containing the \"behaviours\", and \"handlers\" into the project, ready to run. The identifier of the skill fetchai/echo:0.20.6 consists of the name of the author of the skill, followed by the skill name and its version.

"},{"location":"aea-framework-documentation/quickstart/#echo-skill","title":"Echo Skill","text":"

Just like humans, AEAs can have skills to achieve their tasks. As an agent developer, you can create skills to add to your own AEAs. You can also choose to publish your skills so others add them to their AEAs. More details on skills can be found on this page .

The above agent has an echo skill, fetched from the registry, which simply echoes any messages it receives back to its sender.

"},{"location":"aea-framework-documentation/quickstart/#communication-via-envelopes-and-messages","title":"Communication via Envelopes and Messages","text":"

AEAs use envelopes containing messages for communication. To learn more, check out the next section.

"},{"location":"aea-framework-documentation/quickstart/#stub-connection","title":"Stub Connection","text":"

Besides skills, AEAs may have one or more connections enabling them to interface with entities in the outside world. For example, an HTTP client connection allows an AEA to communicate with HTTP servers. To read more about connections see this page.

In this demo, we use the stub connection (fetchai/stub0.15.0) to send envelopes to and receive envelopes from the AEA.

A stub connection provides an I/O reader and writer. It uses two files for communication: one for incoming envelopes and the other for outgoing envelopes.

The AEA waits for a new envelope posted to the file my_first_aea/input_file, and adds a response to the file my_first_aea/output_file.

The format of each envelope is the following:

TO,SENDER,PROTOCOL_ID,ENCODED_MESSAGE,\n

For example:

recipient_aea,sender_aea,fetchai/default:1.0.0,\\x08\\x01\\x12\\x011*\\x07\\n\\x05hello,\n
"},{"location":"aea-framework-documentation/quickstart/#install-aea-dependencies","title":"Install AEA Dependencies","text":"
aea install\n
"},{"location":"aea-framework-documentation/quickstart/#add-and-create-a-private-key","title":"Add and Create a Private Key","text":"

All AEAs need a private key to run. Add one now:

aea generate-key fetchai\naea add-key fetchai\n
"},{"location":"aea-framework-documentation/quickstart/#run-the-aea","title":"Run the AEA","text":"

Run the AEA.

aea run\n

You will see the echo skill running in the terminal window (an output similar to the one below).

    _     _____     _\n   / \\   | ____|   / \\\n/ _ \\  |  _|    / _ \\\n/ ___ \\ | |___  / ___ \\\n/_/   \\_\\|_____|/_/   \\_\\\nv1.1.1\n\nStarting AEA 'my_first_aea' in 'async' mode ...\ninfo: Echo Handler: setup method called.\ninfo: Echo Behaviour: setup method called.\ninfo: [my_first_aea]: Start processing messages...\ninfo: Echo Behaviour: act method called.\ninfo: Echo Behaviour: act method called.\ninfo: Echo Behaviour: act method called.\n...\n

The framework first calls the setup methods in the skill's Handler and Behaviour classes in that order; after which it repeatedly calls the act method of Behaviour class. This is the main agent loop in action.

"},{"location":"aea-framework-documentation/quickstart/#add-a-message-to-the-input-file","title":"Add a Message to the Input File","text":"

You can send the AEA a message wrapped in an envelope using the CLI's interact command.

From a different terminal and same directory (ensure you are in the same virtual environment: pipenv shell):

cd my_first_aea\naea interact\n

You can now send messages to this AEA via an interactive tool by typing anything into the prompt and hitting enter twice (once to send the message and once more to check for a response).

Let us send hello to this AEA (type hello and press enter twice). In the original terminal, you will see the Echo Handler dealing with this envelope and its contained message. You should see an output similar to the one below but with a different dialogue_reference.

info: Echo Behaviour: act method called.\ninfo: Echo Handler: message=Message(dialogue_reference=('1', '') message_id=1 target=0 performative=bytes content=b'hello'), sender=my_first_aea_interact\ninfo: Echo Behaviour: act method called.\ninfo: Echo Behaviour: act method called.\n
Manual approach:

Optionally, from a different terminal and same directory (i.e. the my_first_aea project), you can send the AEA a message wrapped in an envelope via the input file.

echo 'my_first_aea,sender_aea,fetchai/default:1.0.0,\\x12\\x10\\x08\\x01\\x12\\x011*\\t*\\x07\\n\\x05hello,' >> input_file\n

You will see the Echo Handler dealing with the envelope and responding with the same message to the output_file, and also decoding the Base64 encrypted message in this case.

info: Echo Behaviour: act method called.\nEcho Handler: message=Message(sender=sender_aea,to=my_first_aea,content=b'hello',dialogue_reference=('1', ''),message_id=1,performative=bytes,target=0), sender=sender_aea\ninfo: Echo Behaviour: act method called.\ninfo: Echo Behaviour: act method called.\n

Note, due to the dialogue reference having to be incremented, you can only send the above envelope once! This approach does not work in conjunction with the aea interact command.

"},{"location":"aea-framework-documentation/quickstart/#stop-the-aea","title":"Stop the AEA","text":"

You can stop an AEA by pressing CTRL C.

Once you do, you should see the AEA being interrupted and then calling the teardown() methods:

info: Echo Behaviour: act method called.\ninfo: Echo Behaviour: act method called.\n^C my_first_aea interrupted!\nmy_first_aea stopping ...\ninfo: Echo Handler: teardown method called.\ninfo: Echo Behaviour: teardown method called.\n
"},{"location":"aea-framework-documentation/quickstart/#write-a-test-for-the-aea","title":"Write a Test for the AEA","text":"

We can write an end-to-end test for the AEA utilising helper classes provided by the framework.

Writing tests:

The following test class replicates the preceding demo and tests its correct behaviour. The AEATestCase classes are a tool for AEA developers to write useful end-to-end tests of their AEAs.

First, get the packages directory from the AEA repository (execute from the working directory which contains the my_first_aea folder):

svn export https://github.com/fetchai/agents-aea.git/trunk/packages\n

Then write the test:

import signal\nimport time\nfrom aea.common import Address\nfrom aea.mail.base import Envelope\nfrom aea.protocols.base import Message\nfrom aea.protocols.dialogue.base import Dialogue\nfrom packages.fetchai.protocols.default.dialogues import DefaultDialogue, DefaultDialogues\nfrom packages.fetchai.protocols.default.message import DefaultMessage\nfrom packages.fetchai.protocols.default.serialization import DefaultSerializer\nfrom aea.test_tools.test_cases import AEATestCase\nclass TestEchoSkill(AEATestCase):\n\"\"\"Test that echo skill works.\"\"\"\ndef test_echo(self):\n\"\"\"Run the echo skill sequence.\"\"\"\nprocess = self.run_agent()\nis_running = self.is_running(process)\nassert is_running, \"AEA not running within timeout!\"\n# add sending and receiving envelope from input/output files\nsender_aea = \"sender_aea\"\ndef role_from_first_message(\nmessage: Message, receiver_address: Address\n) -> Dialogue.Role:\nreturn DefaultDialogue.Role.AGENT\ndialogues = DefaultDialogues(sender_aea, role_from_first_message)\nmessage_content = b\"hello\"\nmessage = DefaultMessage(\nperformative=DefaultMessage.Performative.BYTES,\ndialogue_reference=dialogues.new_self_initiated_dialogue_reference(),\ncontent=message_content,\n)\nsent_envelope = Envelope(\nto=self.agent_name,\nsender=sender_aea,\nprotocol_id=message.protocol_id,\nmessage=DefaultSerializer().encode(message),\n)\nself.send_envelope_to_agent(sent_envelope, self.agent_name)\ntime.sleep(2.0)\nreceived_envelope = self.read_envelope_from_agent(self.agent_name)\nassert sent_envelope.to == received_envelope.sender\nassert sent_envelope.sender == received_envelope.to\nassert sent_envelope.protocol_id == received_envelope.protocol_id\nreceived_message = DefaultMessage.serializer.decode(received_envelope.message)\nassert message.content == received_message.content\ncheck_strings = (\n\"Echo Handler: setup method called.\",\n\"Echo Behaviour: setup method called.\",\n\"Echo Behaviour: act method called.\",\n\"content={}\".format(message_content),\n)\nmissing_strings = self.missing_from_output(process, check_strings)\nassert (\nmissing_strings == []\n), \"Strings {} didn't appear in agent output.\".format(missing_strings)\nassert (\nself.is_successfully_terminated()\n), \"Echo agent wasn't successfully terminated.\"\n

Place the above code into a file test.py in your AEA project directory (the same level as the aea-config.yaml file).

To run, execute the following:

pytest test.py\n
"},{"location":"aea-framework-documentation/quickstart/#delete-the-aea","title":"Delete the AEA","text":"

Delete the AEA from the parent directory (cd .. to go to the parent directory).

aea delete my_first_aea\n
"},{"location":"aea-framework-documentation/quickstart/#next-steps","title":"Next Steps","text":"

To gain an understanding of the core components of the framework, please continue to the next page:

  • Core components - Part 1

For more demos, use cases or step-by-step guides, please check the following:

  • Generic skill use case
  • Weather skill demo
  • Generic step by step guide
"},{"location":"aea-framework-documentation/raspberry-set-up/","title":"Build an AEA on a Raspberry Pi","text":"

This guide explains how to run an AEA inside a Raspberry Pi.

"},{"location":"aea-framework-documentation/raspberry-set-up/#prerequisites","title":"Prerequisites","text":"
  • Raspberry Pi 4 (You can also use Raspberry Pi3 b or Raspberry Pi3 b+)
  • Internet connection (preferably wireless to minimise the number of wires connecting into your device)
"},{"location":"aea-framework-documentation/raspberry-set-up/#preparing-the-raspberry-pi","title":"Preparing the Raspberry Pi","text":"

The easiest and recommended way to get started is to download and unzip our custom AEA Raspberry Pi Image, which includes the AEA installation as well as the most common dependencies.

However, you can also do the installation manually, and if you have a new Raspberry Pi, you can boot the system using the included SD card and skip the next section.

"},{"location":"aea-framework-documentation/raspberry-set-up/#raspberry-pi-imager","title":"Raspberry Pi Imager","text":"

Raspberry Pi Imager is a way to write to an SD card for easy installation on a Raspberry Pi.

First download the tool from this link.

Then follow this guide to set up your SD card. When you get to the step of choosing an operating system, select the downloaded and unzipped AEA Raspberry Pi Image (AEA_RPI.IMG), or for a manual installation, select the latest Raspberry Pi OS.

Once you have set up your SD card, plug it into your Raspberry Pi, connect the power and boot up.

"},{"location":"aea-framework-documentation/raspberry-set-up/#booting-up-with-the-aea-raspberry-pi-image","title":"Booting up with the AEA Raspberry Pi Image","text":"

After booting up, you may be prompted to log in as the aea user and the password is fetch. Next, navigate to settings menu to set up your internet connection. Your Raspberry Pi is now ready to run an AEA! You can find some preloaded demos in the folder ~/aea/demos. To run these demos, navigate to one of the sub-folders and enter aea run.

"},{"location":"aea-framework-documentation/raspberry-set-up/#booting-up-with-the-raspberry-pi-os-for-manual-installation","title":"Booting up with the Raspberry Pi OS for Manual Installation","text":"

When you first boot your Raspberry Pi, you will be prompted to enter a password for the Raspberry Pi and your Wi-Fi password so the device can access the internet. You may also be given the option to update the operating system and software. We recommend that you let the system update. Once finished you will be prompted to restart.

Even if your Raspberry Pi updated itself, we recommend that you make sure it is completely up-to-date using the terminal. Open a Terminal window (your Raspberry Pi might restart a few times during this process):

sudo apt update -y sudo apt-get update\nsudo apt-get dist-upgrade 
"},{"location":"aea-framework-documentation/raspberry-set-up/#install-common-dependencies","title":"Install Common Dependencies","text":"
sudo apt install cmake golang -y\n
"},{"location":"aea-framework-documentation/raspberry-set-up/#install-less-common-dependencies-optional","title":"Install Less Common Dependencies (optional)","text":"

For some of the more advanced AEAs that make use of SciPy, such as the Car Park Detector, you will need some additional dependencies.

Install additional dependencies with the enclosed steps:

Install additional dependencies

sudo apt install gfortran libatlas-base-dev libopenblas-dev -y\n

Increase the swap space for the SciPy installation:

sudo /bin/dd if=/dev/zero of=/var/swap.1 bs=1M count=1024\nsudo /sbin/mkswap /var/swap.1\nsudo chmod 600 /var/swap.1\nsudo /sbin/swapon /var/swap.1\n

Install NumPy and scikit-image (including SciPy)

pip install numpy --upgrade\npip install scikit-image\n

Revert to default swap space

sudo swapoff /var/swap.1\nsudo rm /var/swap.1\n
"},{"location":"aea-framework-documentation/raspberry-set-up/#install-the-aea-framework","title":"Install the AEA Framework","text":"

Add to the local PATH environment variable (this will happen automatically the next time you log in):

export PATH=\"$HOME/.local/bin:$PATH\"\n

Finally, install the AEA framework from PyPI:

pip install aea[all]\n

Check to make sure installation was successful:

aea --version\n

Your Raspberry Pi is now ready to run an AEA!

"},{"location":"aea-framework-documentation/runtime-cost/","title":"Profiling","text":""},{"location":"aea-framework-documentation/runtime-cost/#measuring-runtime-cost","title":"Measuring Runtime Cost","text":"

It is important to emphasise the fact that the AEA is a framework, so ultimately its running cost will highly depend on the number and type of components which are being run as part of a given AEA. The other cost factor is determined by the cost of running the core framework itself and how fast and efficient the framework is in interconnecting the components.

These observations can provide guidance on what to report as part of the cost of running an AEA.

Here is a list of suggestion on how to measure the cost of running an AEA:

  • the cost of running the framework itself: by running a minimal agent with an idle loop (the default one) with no connections, skills or protocols and measuring memory usage and CPU consumption as a baseline.
  • the cost of interconnecting components: by running an agent with a basic skill (e.g. fetchai/echo) and measuring memory usage and CPU consumption relative to number of messages exchanged as well as bandwidth.
  • the cost of basic components: dialogues memory relative to number of messages, SOEF connection baseline memory usage, P2P connection baseline memory usage, smart contract baseline memory usage

The aea run --profiling SECONDS command can be used to report measures in all of the above scenarios.

"},{"location":"aea-framework-documentation/scaffolding/","title":"Scaffolding Packages","text":""},{"location":"aea-framework-documentation/scaffolding/#scaffold-generator","title":"Scaffold Generator","text":"

The scaffold generator builds out the directory structure required when adding new skills, protocols, contracts and connections to the AEA.

For example, create a new AEA project (add the author flag using your own author handle if this is your first project using the aea package).

aea create my_aea --author \"fetchai\"\ncd my_aea\n

Then, enter into your project directory and scaffold your project skill, protocol, or connection.

"},{"location":"aea-framework-documentation/scaffolding/#scaffold-a-skill","title":"Scaffold a Skill","text":"
aea scaffold skill my_skill\n
"},{"location":"aea-framework-documentation/scaffolding/#scaffold-a-protocol","title":"Scaffold a Protocol","text":"
aea scaffold protocol my_protocol\n
"},{"location":"aea-framework-documentation/scaffolding/#scaffold-a-contract","title":"Scaffold a Contract","text":"
aea scaffold contract my_contract\n
"},{"location":"aea-framework-documentation/scaffolding/#scaffold-a-connection","title":"Scaffold a Connection","text":"
aea scaffold connection my_connection\n

After running the above commands, you are able to develop your own skill, protocol, contract and connection.

Once you have made changes to your scaffolded packages, make sure you update the fingerprint of the package:

aea fingerprint [package_name] [public_id]\n

Then you are ready to run the AEA.

"},{"location":"aea-framework-documentation/security/","title":"Security","text":"

The AEA framework takes every care to follow best practice around security.

The following advice will help you when writing your own code:

  • Many potential common security vulnerabilities can be caught by static code analysis. We recommend you use safety, pylint and bandit to analyse your code.

  • Don't use relative import paths, these can lead to malicious code being executed.

  • Try to avoid using the subprocess module. If needed, make sure you sanitise commands passed to subprocess.

  • Try to avoid using the pickle module. Pickle should never be used for agent-to-agent communication protocols.

  • By design, the framework prevents skill code from accessing private keys directly, as they are not reachable from the skill execution context through attribute getters. However, if the flag -p or the option --password are not used when generating private keys for an AEA project via the aea CLI tool, the private keys will be stored in plaintext. This allows the skills to access them via interaction with the OS file system. We recommend to always specify a password to encrypt private keys by using the flag argument.

"},{"location":"aea-framework-documentation/setup/","title":"Setting up","text":"

Once you successfully install the AEA framework, you can set it up for agent development.

"},{"location":"aea-framework-documentation/setup/#specify-author-handle","title":"Specify Author Handle","text":"

You need an author handle before being able to develop agents or agent components. This handle is used in the author field of any agent or component you create.

AEAs and their components can be developed by anyone and pushed to the AEA registry for others to use. To publish packages to the registry, you also need to register your author handle.

"},{"location":"aea-framework-documentation/setup/#pick-author-handle-and-register","title":"Pick Author Handle and Register","text":"

If you are intending to use the registry:

aea init --register\n

This will let you pick a new author handle and register it at the same time.

"},{"location":"aea-framework-documentation/setup/#pick-author-handle-only","title":"Pick Author Handle Only","text":"

If you are unsure whether you will need a registry account, or intending not to use it, simply pick a new author handle:

aea init\n
"},{"location":"aea-framework-documentation/setup/#register-author-handle","title":"Register Author Handle","text":"

To register an already created author handle with the AEA registry:

aea register\n

Note

The author handle is your unique author (or developer) name in the AEA ecosystem.

"},{"location":"aea-framework-documentation/simple-oef-usage/","title":"SOEF Connection","text":"

You can use the SOEF in the agent framework by using the SOEF connection as a package in your agent project.

"},{"location":"aea-framework-documentation/simple-oef-usage/#add-the-soef-package","title":"Add the SOEF Package","text":"

Check out the CLI guide on details how to add a connection. You will want to add the fetchai/soef:0.27.6 connection package.

"},{"location":"aea-framework-documentation/simple-oef-usage/#register-your-agent-and-its-services","title":"Register your Agent and its Services","text":""},{"location":"aea-framework-documentation/simple-oef-usage/#register-agent-location","title":"Register Agent Location","text":"

To register your agent's location, you have to send a message in the fetchai/oef_search:1.0.0 protocol to the SOEF connection.

First, define a data model for location data:

from aea.helpers.search.models import Attribute, DataModel, Location\nAGENT_LOCATION_MODEL = DataModel(\n\"location_agent\",\n[Attribute(\"location\", Location, True, \"The location where the agent is.\")],\n\"A data model to describe location of an agent.\",\n)\n

It is important to use this exact data model, as the SOEF connection can only process specific data models.

Second, create a location object:

from aea.helpers.search.models import Location\nagent_location = Location(52.2057092, 2.1183431)\n

Third, construct a service description instance with location and data model:

from aea.helpers.search.models import Description\nservice_instance = {\"location\": agent_location}\nservice_description = Description(\nservice_instance, data_model=AGENT_LOCATION_MODEL\n)\n

Finally, construct a message and send it:

from packages.fetchai.protocols.oef_search.message import OefSearchMessage\nmessage = OefSearchMessage(\nperformative=OefSearchMessage.Performative.REGISTER_SERVICE,\nservice_description=service_description,\n)\n

In case everything is registered OK, you will not receive any message back.

If something goes wrong you will receive an error message with performative OefSearchMessage.Performative.OEF_ERROR.

"},{"location":"aea-framework-documentation/simple-oef-usage/#register-personality-pieces","title":"Register Personality Pieces","text":"

To register personality pieces, you have to use a specific data model:

from aea.helpers.search.models import Attribute, DataModel, Location\nAGENT_PERSONALITY_MODEL = DataModel(\n\"personality_agent\",\n[\nAttribute(\"piece\", str, True, \"The personality piece key.\"),\nAttribute(\"value\", str, True, \"The personality piece value.\"),\n],\n\"A data model to describe the personality of an agent.\",\n)\n

An example follows:

service_instance = {\"piece\": \"genus\", \"value\": \"service\"}\nservice_description = Description(\nservice_instance, data_model=AGENT_PERSONALITY_MODEL\n)\n
"},{"location":"aea-framework-documentation/simple-oef-usage/#register-services","title":"Register Services","text":"

To set some service key and value you have to use a specific data model:

SET_SERVICE_KEY_MODEL = DataModel(\n\"set_service_key\",\n[\nAttribute(\"key\", str, True, \"Service key name.\"),\nAttribute(\"value\", str, True, \"Service key value.\"),\n],\n\"A data model to set service key.\",\n)\n

An example follows:

service_instance = {\"key\": \"test\", \"value\": \"test\"}\nservice_description = Description(\nservice_instance, data_model=SET_SERVICE_KEY_MODEL\n)\n
"},{"location":"aea-framework-documentation/simple-oef-usage/#remove-service-key","title":"Remove Service Key","text":"

To remove service key have to use a specific data model:

REMOVE_SERVICE_KEY_MODEL = DataModel(\n\"remove_service_key\",\n[Attribute(\"key\", str, True, \"Service key name.\")],\n\"A data model to remove service key.\",\n)\n

An example follows:

service_instance = {\"key\": \"test\"}\nservice_description = Description(\nservice_instance, data_model=REMOVE_SERVICE_KEY_MODEL\n)\n

Note

Currently, the soef does not allow for multiple registrations to be combined into a single command.

"},{"location":"aea-framework-documentation/simple-oef-usage/#perform-a-search","title":"Perform a Search","text":"

To perform a search for services registered you have to define a search query consisting of constraints. The location constraints is required, personality pieces or services keys constraints are optional.

An example follows:

from aea.helpers.search.models import (\nConstraint,\nConstraintType,\nLocation,\nQuery,\n)\nradius = 0.1\nclose_to_my_service = Constraint(\n\"location\", ConstraintType(\"distance\", (agent_location, radius))\n)\npersonality_filters = [\nConstraint(\"genus\", ConstraintType(\"==\", \"vehicle\")),\nConstraint(\n\"classification\", ConstraintType(\"==\", \"mobility.railway.train\")\n),\n]\nservice_key_filters = [\nConstraint(\"test\", ConstraintType(\"==\", \"test\")),\n]\ncloseness_query = Query(\n[close_to_my_service] + personality_filters + service_key_filters\n)\nmessage = OefSearchMessage(\nperformative=OefSearchMessage.Performative.SEARCH_SERVICES,\nquery=closeness_query,\n)\n

In case of error, you will receive a message with OefSearchMessage.Performative.OEF_ERROR. In case of successful search you will receive a message with performative OefSearchMessage.Performative.SEARCH_RESULT and the list of matched agents addresses.

"},{"location":"aea-framework-documentation/simple-oef-usage/#generic-command","title":"Generic Command","text":"

To send a generic command request to the SOEF use the following (here on the example of setting a declared name):

import urllib\nAGENT_GENERIC_COMMAND_MODEL = DataModel(\n\"generic_command\",\n[\nAttribute(\"command\", str, True, \"Command name to execute.\"),\nAttribute(\"parameters\", str, False, \"Url encoded parameters string.\"),\n],\n\"A data model to describe the generic soef command.\",\n)\ndeclared_name = \"new_declared_name\"\nservice_description = Description(\n{\n\"command\": \"set_declared_name\",\n\"parameters\": urllib.parse.urlencode({\"name\": declared_name}),\n},\ndata_model=AGENT_GENERIC_COMMAND_MODEL,\n)\nmessage = OefSearchMessage(\nperformative=OefSearchMessage.Performative.REGISTER_SERVICE,\nservice_description=service_description,\n)\n
"},{"location":"aea-framework-documentation/simple-oef/","title":"Simple-OEF: Agent Search and Discovery","text":"

The full documentation is available here.

"},{"location":"aea-framework-documentation/skill-guide/","title":"Build your First Skill - Search & Discovery","text":"

This guide will take you through the development of your first skill. It will teach you, how to connect the AEA to the digital world, register the AEA and search for other AEAs.

Although one can imagine scenarios where a single AEA pursues its goals in isolation without interacting with other AEAs, there is no doubt that by working together, AEAs can achieve much more. To do so, an AEA must be seen and found by other AEAs so that they can trade and do other useful things. Fetch.ai\u2019s search-and-discovery mechanism, the simple OEF (or SOEF, for short) lets your agents register, be discovered, and find other agents. You can then negotiate using the AEA framework\u2019s peer-to-peer network (ACN) and trade. This guide covers getting your AEA connected to the SOEF, and describing your AEA to make itself visible.

Registering your AEA with the SOEF involves setting a name, a genus (a high-level description of what the agent represents, e.g. vehicle, building or service), a classification (for example infrastructure.railway.train) and other descriptors to further fine-tune the kind of service your AEA offers (for example, the agent's position, whether it buys or sells, and other descriptive items).

The more you describe your AEA, the easier it is for others to find it using specific filters.

"},{"location":"aea-framework-documentation/skill-guide/#dependencies-required","title":"Dependencies (Required)","text":"

Follow the Preliminaries and Installation sections from the AEA quick start.

"},{"location":"aea-framework-documentation/skill-guide/#step-1-setup","title":"Step 1: Setup","text":"

We will first create an AEA and add a scaffold skill, which we call my_search.

aea create my_aea && cd my_aea\naea scaffold skill my_search\n

In the following steps, we replace the scaffolded Behaviour and Handler in my_aea/skills/my_search with our implementation. We will build a simple skill which lets the AEA send a search query to the SOEF search node and process the resulting response.

"},{"location":"aea-framework-documentation/skill-guide/#step-2-develop-a-behaviour","title":"Step 2: Develop a Behaviour","text":"

A Behaviour class contains the business logic specific to actions initiated by the AEA rather than reactions to other events.

In this example, we implement a simple search behaviour. Each time, act() gets called by the main agent loop, we will send a search request to the SOEF search node via the P2P communication network.

from typing import cast\nfrom aea.helpers.search.models import Constraint, ConstraintType, Location, Query\nfrom aea.skills.behaviours import TickerBehaviour\nfrom packages.fetchai.protocols.oef_search.message import OefSearchMessage\nfrom packages.fetchai.skills.my_search.dialogues import OefSearchDialogues\nDEFAULT_LOCATION = {\"longitude\": 0.1270, \"latitude\": 51.5194}\nDEFAULT_SEARCH_QUERY = {\n\"search_key\": \"seller_service\",\n\"search_value\": \"generic_service\",\n\"constraint_type\": \"==\",\n}\nDEFAULT_SEARCH_RADIUS = 5.0\nclass MySearchBehaviour(TickerBehaviour):\n\"\"\"This class provides a simple search behaviour.\"\"\"\ndef __init__(self, **kwargs):\n\"\"\"Initialize the search behaviour.\"\"\"\nsearch_query = kwargs.pop(\"search_query\", DEFAULT_SEARCH_QUERY)\nlocation = kwargs.pop(\"location\", DEFAULT_LOCATION)\nagent_location = Location(latitude=location[\"latitude\"], longitude=location[\"longitude\"])\nradius = kwargs.pop(\"search_radius\", DEFAULT_SEARCH_RADIUS)\nclose_to_my_service = Constraint(\n\"location\", ConstraintType(\"distance\", (agent_location, radius))\n)\nservice_key_filter = Constraint(\nsearch_query[\"search_key\"],\nConstraintType(\nsearch_query[\"constraint_type\"], search_query[\"search_value\"],\n),\n)\nself.query = Query([close_to_my_service, service_key_filter])\nsuper().__init__(**kwargs)\nself.sent_search_count = 0\ndef setup(self) -> None:\n\"\"\"\n        Implement the setup.\n        :return: None\n        \"\"\"\nself.context.logger.info(\n\"setting up MySearchBehaviour\"\n)\ndef act(self) -> None:\n\"\"\"\n        Implement the act.\n        :return: None\n        \"\"\"\nself.sent_search_count += 1\noef_search_dialogues = cast(\nOefSearchDialogues, self.context.oef_search_dialogues\n)\nself.context.logger.info(\n\"sending search request to OEF search node, search_count={}\".format(\nself.sent_search_count\n)\n)\nsearch_request, _ = oef_search_dialogues.create(\ncounterparty=self.context.search_service_address,\nperformative=OefSearchMessage.Performative.SEARCH_SERVICES,\nquery=self.query,\n)\nself.context.outbox.put_message(message=search_request)\ndef teardown(self) -> None:\n\"\"\"\n        Implement the task teardown.\n        :return: None\n        \"\"\"\nself.context.logger.info(\n\"tearing down MySearchBehaviour\"\n)\n

Searches are proactive and, as such, well placed in a Behaviour. Specifically, we subclass the TickerBehaviour as it allows us to repeatedly search at a defined tick interval.

We place this code in my_aea/skills/my_search/behaviours.py. Ensure you replace the fetchai author in this line from packages.fetchai.skills.my_search.dialogues import OefSearchDialogues with your author handle (run aea init to set or check the author name).

Note

The import paths to agent packages, for example packages.fetchai.skills.my_search.dialogues above, are not actual paths. Package files always reside in your AEA's folder, either under a specific package directory (e.g. connection, protocol, skill) if the package is custom-built, or under vendor if it is pulled from the registry. These paths are virtual and created automatically when an AEA is run. See this page for more details.

"},{"location":"aea-framework-documentation/skill-guide/#step-3-develop-a-handler","title":"Step 3: Develop a Handler","text":"

So far, we have tasked the AEA with sending search requests to the SOEF search node. However, we have no way of handling the responses sent to the AEA by the SOEF search node at the moment. The AEA would simply respond to the SOEF search node via the default error skill which sends all unrecognised envelopes back to the sender.

Let us now implement a Handler to deal with the incoming search responses.

from typing import Optional, cast\nfrom aea.protocols.base import Message\nfrom aea.skills.base import Handler\nfrom packages.fetchai.protocols.oef_search.message import OefSearchMessage\nfrom packages.fetchai.skills.my_search.dialogues import (\nOefSearchDialogue,\nOefSearchDialogues,\n)\nclass MySearchHandler(Handler):\n\"\"\"This class provides a simple search handler.\"\"\"\nSUPPORTED_PROTOCOL = OefSearchMessage.protocol_id\ndef __init__(self, **kwargs):\n\"\"\"Initialize the handler.\"\"\"\nsuper().__init__(**kwargs)\nself.received_search_count = 0\ndef setup(self) -> None:\n\"\"\"Set up the handler.\"\"\"\nself.context.logger.info(\n\"setting up MySearchHandler\"\n)\ndef handle(self, message: Message) -> None:\n\"\"\"\n        Implement the reaction to a message.\n        :param message: the message\n        :return: None\n        \"\"\"\noef_search_msg = cast(OefSearchMessage, message)\n# recover dialogue\noef_search_dialogues = cast(\nOefSearchDialogues, self.context.oef_search_dialogues\n)\noef_search_dialogue = cast(\nOptional[OefSearchDialogue], oef_search_dialogues.update(oef_search_msg)\n)\nif oef_search_dialogue is None:\nself._handle_unidentified_dialogue(oef_search_msg)\nreturn\n# handle message\nif oef_search_msg.performative is OefSearchMessage.Performative.OEF_ERROR:\nself._handle_error(oef_search_msg, oef_search_dialogue)\nelif oef_search_msg.performative is OefSearchMessage.Performative.SEARCH_RESULT:\nself._handle_search(oef_search_msg, oef_search_dialogue)\nelse:\nself._handle_invalid(oef_search_msg, oef_search_dialogue)\ndef _handle_unidentified_dialogue(self, oef_search_msg: OefSearchMessage) -> None:\n\"\"\"\n        Handle an unidentified dialogue.\n        :param msg: the message\n        \"\"\"\nself.context.logger.info(\n\"received invalid oef_search message={}, unidentified dialogue.\".format(\noef_search_msg\n)\n)\ndef _handle_error(\nself, oef_search_msg: OefSearchMessage, oef_search_dialogue: OefSearchDialogue\n) -> None:\n\"\"\"\n        Handle an oef search message.\n        :param oef_search_msg: the oef search message\n        :param oef_search_dialogue: the dialogue\n        :return: None\n        \"\"\"\nself.context.logger.info(\n\"received oef_search error message={} in dialogue={}.\".format(\noef_search_msg, oef_search_dialogue\n)\n)\ndef _handle_search(\nself, oef_search_msg: OefSearchMessage, oef_search_dialogue: OefSearchDialogue\n) -> None:\n\"\"\"\n        Handle the search response.\n        :param agents: the agents returned by the search\n        :return: None\n        \"\"\"\nself.received_search_count += 1\nnb_agents_found = len(oef_search_msg.agents)\nself.context.logger.info(\n\"found number of agents={}, received search count={}\".format(\nnb_agents_found, self.received_search_count\n)\n)\nself.context.logger.info(\n\"number of search requests sent={} vs. number of search responses received={}\".format(\nself.context.behaviours.my_search_behaviour.sent_search_count,\nself.received_search_count,\n)\n)\ndef _handle_invalid(\nself, oef_search_msg: OefSearchMessage, oef_search_dialogue: OefSearchDialogue\n) -> None:\n\"\"\"\n        Handle an oef search message.\n        :param oef_search_msg: the oef search message\n        :param oef_search_dialogue: the dialogue\n        :return: None\n        \"\"\"\nself.context.logger.warning(\n\"cannot handle oef_search message of performative={} in dialogue={}.\".format(\noef_search_msg.performative, oef_search_dialogue,\n)\n)\ndef teardown(self) -> None:\n\"\"\"\n        Teardown the handler.\n        :return: None\n        \"\"\"\nself.context.logger.info(\n\"tearing down MySearchHandler\"\n)\n

We create a handler which is registered for the oef_search protocol. Whenever it receives a search result, we log the number of agents returned by the search - the agents matching the search query - and update the counter of received searches.

We also implement a trivial check on the difference between the amount of search requests sent and responses received.

Note, how the handler simply reacts to incoming events (i.e. messages). It could initiate further actions, however, they are still reactions to the upstream search event.

Also note, how we have access to other objects in the skill via self.context, the SkillContext.

We place this code in my_aea/skills/my_search/handlers.py. Ensure you replace the fetchai author in this line from packages.fetchai.skills.my_search.dialogues import ( with your author handle (run aea init to set or check the author name).

"},{"location":"aea-framework-documentation/skill-guide/#step-4-add-dialogues-model","title":"Step 4: Add Dialogues Model","text":"

We have implemented a behaviour and a handler. We now implement a Model, in particular we implement the Dialogue and Dialogues classes. These ensure that the message flow satisfies the fetchai/oef_search:1.0.0 protocol and keep track of the individual messages being sent and received.

from aea.protocols.base import Message\nfrom aea.protocols.dialogue.base import Dialogue as BaseDialogue\nfrom aea.skills.base import Address, Model\nfrom packages.fetchai.protocols.oef_search.dialogues import (\nOefSearchDialogue as BaseOefSearchDialogue,\n)\nfrom packages.fetchai.protocols.oef_search.dialogues import (\nOefSearchDialogues as BaseOefSearchDialogues,\n)\nOefSearchDialogue = BaseOefSearchDialogue\nclass OefSearchDialogues(Model, BaseOefSearchDialogues):\n\"\"\"This class keeps track of all oef_search dialogues.\"\"\"\ndef __init__(self, **kwargs) -> None:\n\"\"\"\n        Initialize dialogues.\n        :param agent_address: the address of the agent for whom dialogues are maintained\n        :return: None\n        \"\"\"\nModel.__init__(self, **kwargs)\ndef role_from_first_message(  # pylint: disable=unused-argument\nmessage: Message, receiver_address: Address\n) -> BaseDialogue.Role:\n\"\"\"Infer the role of the agent from an incoming/outgoing first message\n            :param message: an incoming/outgoing first message\n            :param receiver_address: the address of the receiving agent\n            :return: The role of the agent\n            \"\"\"\nreturn BaseOefSearchDialogue.Role.AGENT\nBaseOefSearchDialogues.__init__(\nself,\nself_address=str(self.skill_id),\nrole_from_first_message=role_from_first_message,\n)\n

We add this code in the file my_aea/skills/my_search/my_model.py, replacing its original content. We then rename my_aea/skills/my_search/my_model.py to my_aea/skills/my_search/dialogues.py.

"},{"location":"aea-framework-documentation/skill-guide/#step-5-create-the-configuration-file","title":"Step 5: Create the Configuration File","text":"

Based on our skill components above, we create the following configuration file.

name: my_search\nauthor: fetchai\nversion: 0.1.0\ntype: skill\ndescription: A simple search skill utilising the SOEF search node.\nlicense: Apache-2.0\naea_version: '>=1.0.0, <2.0.0'\nfingerprint: {}\nfingerprint_ignore_patterns: []\nconnections: []\ncontracts: []\nprotocols:\n- fetchai/oef_search:1.1.7\nskills: []\nbehaviours:\nmy_search_behaviour:\nargs:\nlocation:\nlatitude: 51.5194\nlongitude: 0.127\nsearch_query:\nconstraint_type: ==\nsearch_key: seller_service\nsearch_value: generic_service\nsearch_radius: 5.0\ntick_interval: 5\nclass_name: MySearchBehaviour\nhandlers:\nmy_search_handler:\nargs: {}\nclass_name: MySearchHandler\nmodels:\noef_search_dialogues:\nargs: {}\nclass_name: OefSearchDialogues\ndependencies:\naea-ledger-fetchai:\nversion: <2.0.0,>=1.0.0\nis_abstract: false\n

Ensure, you replace the author field with your author name! (Run aea init to set or check the author name.)

Importantly, the keys my_search_behaviour and my_search_handler are used in the above handler to access these skill components at runtime via the context. We also set the tick_interval of the TickerBehaviour to 5 seconds.

We place this code in my_aea/skills/my_search/skill.yaml.

Similarly, we replace my_aea/skills/my_search/__init__.py as follows:

# -*- coding: utf-8 -*-\n# ------------------------------------------------------------------------------\n#\n#   Copyright 2018-2019 Fetch.AI Limited\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#       http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n#\n# ------------------------------------------------------------------------------\n\"\"\"This module contains the implementation of the error skill.\"\"\"\nfrom aea.configurations.base import PublicId\nPUBLIC_ID = PublicId.from_str(\"fetchai/my_search:0.1.0\")\n

Again, ensure the author field matches your own.

"},{"location":"aea-framework-documentation/skill-guide/#step-6-update-fingerprint","title":"Step 6: Update Fingerprint","text":"

To run an AEA with new or modified code, you need to update the fingerprint of the new/modified components. In this case, we need to fingerprint our skill:

aea fingerprint skill fetchai/my_search:0.1.0\n

Ensure, you use the correct author name to reference your skill (here we use fetchai as the author.)

"},{"location":"aea-framework-documentation/skill-guide/#step-7-add-the-oef-protocol-and-connection","title":"Step 7: Add the OEF Protocol and Connection","text":"

Our AEA does not have the OEF protocol yet so let's add it.

aea add protocol fetchai/oef_search:1.1.7\n

This adds the protocol to our AEA and makes it available on the path packages.fetchai.protocols....

At this point we need to add the SOEF and P2P connections to allow the AEA to communicate with the SOEF node and other AEAs, install the AEA's dependencies, and configure the AEA:

aea add connection fetchai/soef:0.27.6\naea add connection fetchai/p2p_libp2p:0.27.5\naea install\naea build\naea config set agent.default_connection fetchai/p2p_libp2p:0.27.5\naea config set --type dict agent.default_routing \\\n'{\n  \"fetchai/oef_search:1.1.7\": \"fetchai/soef:0.27.6\"\n}'\n

The last command will ensure that search requests are processed by the correct connection.

"},{"location":"aea-framework-documentation/skill-guide/#step-8-run-a-service-provider-aea","title":"Step 8: Run a Service Provider AEA","text":"

In order for this AEA to find another AEA when searching, the second AEA (let's call it the service provider AEA) must exist and have been registered with the SOEF.

From a different terminal window, we fetch a finished service provider AEA and install its Python dependencies:

aea fetch fetchai/simple_service_registration:0.32.5 && cd simple_service_registration && aea install && aea build\n

This AEA will simply register a location service on the SOEF search node so we can search for it.

We first create the private key for the service provider AEA based on the network you want to transact. To generate and add a private-public key pair for Fetch.ai Dorado use:

aea generate-key fetchai\naea add-key fetchai fetchai_private_key.txt\n

Next, create a private key used to secure the AEA's communications:

aea generate-key fetchai fetchai_connection_private_key.txt\naea add-key fetchai fetchai_connection_private_key.txt --connection\n

Finally, certify the key for use by the connections that request that:

aea issue-certificates\n

Then we run the AEA:

aea run\n

Once you see a message of the form To join its network use multiaddr: ['SOME_ADDRESS'] take note of the address. (Alternatively, use aea get-multiaddress fetchai -c -i fetchai/p2p_libp2p:0.27.5 -u public_uri to retrieve the address.) This is the entry peer address for the local agent communication network created by the simple_service_registration (service provider) AEA.

Click here to see full code and guide for this AEA:

We use a TickerBehaviour to update the service registration at regular intervals. The following code is placed in behaviours.py.

from typing import Any, Optional, cast\nfrom aea.helpers.search.models import Description\nfrom aea.skills.behaviours import TickerBehaviour\nfrom packages.fetchai.protocols.oef_search.message import OefSearchMessage\nfrom packages.fetchai.skills.simple_service_registration.dialogues import (\nOefSearchDialogues,\n)\nfrom packages.fetchai.skills.simple_service_registration.strategy import Strategy\nDEFAULT_MAX_SOEF_REGISTRATION_RETRIES = 5\nDEFAULT_SERVICES_INTERVAL = 30.0\nclass ServiceRegistrationBehaviour(TickerBehaviour):\n\"\"\"This class implements a behaviour.\"\"\"\ndef __init__(self, **kwargs: Any) -> None:\n\"\"\"Initialise the behaviour.\"\"\"\nservices_interval = kwargs.pop(\n\"services_interval\", DEFAULT_SERVICES_INTERVAL\n)  # type: int\nself._max_soef_registration_retries = kwargs.pop(\n\"max_soef_registration_retries\", DEFAULT_MAX_SOEF_REGISTRATION_RETRIES\n)  # type: int\nsuper().__init__(tick_interval=services_interval, **kwargs)\nself.failed_registration_msg = None  # type: Optional[OefSearchMessage]\nself._nb_retries = 0\ndef setup(self) -> None:\n\"\"\"\n        Implement the setup.\n        :return: None\n        \"\"\"\nself._register_agent()\ndef act(self) -> None:\n\"\"\"\n        Implement the act.\n        :return: None\n        \"\"\"\nself._retry_failed_registration()\ndef teardown(self) -> None:\n\"\"\"\n        Implement the task teardown.\n        :return: None\n        \"\"\"\nself._unregister_service()\nself._unregister_agent()\ndef _retry_failed_registration(self) -> None:\n\"\"\"\n        Retry a failed registration.\n        :return: None\n        \"\"\"\nif self.failed_registration_msg is not None:\nself._nb_retries += 1\nif self._nb_retries > self._max_soef_registration_retries:\nself.context.is_active = False\nreturn\noef_search_dialogues = cast(\nOefSearchDialogues, self.context.oef_search_dialogues\n)\noef_search_msg, _ = oef_search_dialogues.create(\ncounterparty=self.failed_registration_msg.to,\nperformative=self.failed_registration_msg.performative,\nservice_description=self.failed_registration_msg.service_description,\n)\nself.context.outbox.put_message(message=oef_search_msg)\nself.context.logger.info(\nf\"Retrying registration on SOEF. Retry {self._nb_retries} out of {self._max_soef_registration_retries}.\"\n)\nself.failed_registration_msg = None\ndef _register(self, description: Description, logger_msg: str) -> None:\n\"\"\"\n        Register something on the SOEF.\n        :param description: the description of what is being registered\n        :param logger_msg: the logger message to print after the registration\n        :return: None\n        \"\"\"\noef_search_dialogues = cast(\nOefSearchDialogues, self.context.oef_search_dialogues\n)\noef_search_msg, _ = oef_search_dialogues.create(\ncounterparty=self.context.search_service_address,\nperformative=OefSearchMessage.Performative.REGISTER_SERVICE,\nservice_description=description,\n)\nself.context.outbox.put_message(message=oef_search_msg)\nself.context.logger.info(logger_msg)\ndef _register_agent(self) -> None:\n\"\"\"\n        Register the agent's location.\n        :return: None\n        \"\"\"\nstrategy = cast(Strategy, self.context.strategy)\ndescription = strategy.get_location_description()\nself._register(description, \"registering agent on SOEF.\")\ndef register_service(self) -> None:\n\"\"\"\n        Register the agent's service.\n        :return: None\n        \"\"\"\nstrategy = cast(Strategy, self.context.strategy)\ndescription = strategy.get_register_service_description()\nself._register(description, \"registering agent's service on the SOEF.\")\ndef register_genus(self) -> None:\n\"\"\"\n        Register the agent's personality genus.\n        :return: None\n        \"\"\"\nstrategy = cast(Strategy, self.context.strategy)\ndescription = strategy.get_register_personality_description()\nself._register(\ndescription, \"registering agent's personality genus on the SOEF.\"\n)\ndef register_classification(self) -> None:\n\"\"\"\n        Register the agent's personality classification.\n        :return: None\n        \"\"\"\nstrategy = cast(Strategy, self.context.strategy)\ndescription = strategy.get_register_classification_description()\nself._register(\ndescription, \"registering agent's personality classification on the SOEF.\"\n)\ndef _unregister_service(self) -> None:\n\"\"\"\n        Unregister service from the SOEF.\n        :return: None\n        \"\"\"\nstrategy = cast(Strategy, self.context.strategy)\ndescription = strategy.get_unregister_service_description()\noef_search_dialogues = cast(\nOefSearchDialogues, self.context.oef_search_dialogues\n)\noef_search_msg, _ = oef_search_dialogues.create(\ncounterparty=self.context.search_service_address,\nperformative=OefSearchMessage.Performative.UNREGISTER_SERVICE,\nservice_description=description,\n)\nself.context.outbox.put_message(message=oef_search_msg)\nself.context.logger.info(\"unregistering service from SOEF.\")\ndef _unregister_agent(self) -> None:\n\"\"\"\n        Unregister agent from the SOEF.\n        :return: None\n        \"\"\"\nstrategy = cast(Strategy, self.context.strategy)\ndescription = strategy.get_location_description()\noef_search_dialogues = cast(\nOefSearchDialogues, self.context.oef_search_dialogues\n)\noef_search_msg, _ = oef_search_dialogues.create(\ncounterparty=self.context.search_service_address,\nperformative=OefSearchMessage.Performative.UNREGISTER_SERVICE,\nservice_description=description,\n)\nself.context.outbox.put_message(message=oef_search_msg)\nself.context.logger.info(\"unregistering agent from SOEF.\")\n

We create a Model type strategy class and place it in strategy.py. We use a generic data model to register the service. As part of the registration we register a location and a key pair describing our service.

from typing import Any\nfrom aea.exceptions import enforce\nfrom aea.helpers.search.generic import (\nAGENT_LOCATION_MODEL,\nAGENT_PERSONALITY_MODEL,\nAGENT_REMOVE_SERVICE_MODEL,\nAGENT_SET_SERVICE_MODEL,\n)\nfrom aea.helpers.search.models import Description, Location\nfrom aea.skills.base import Model\nDEFAULT_LOCATION = {\"longitude\": 0.1270, \"latitude\": 51.5194}\nDEFAULT_SERVICE_DATA = {\"key\": \"seller_service\", \"value\": \"generic_service\"}\nDEFAULT_PERSONALITY_DATA = {\"piece\": \"genus\", \"value\": \"data\"}\nDEFAULT_CLASSIFICATION = {\"piece\": \"classification\", \"value\": \"seller\"}\nclass Strategy(Model):\n\"\"\"This class defines a strategy for the agent.\"\"\"\ndef __init__(self, **kwargs: Any) -> None:\n\"\"\"\n        Initialize the strategy of the agent.\n        :return: None\n        \"\"\"\nlocation = kwargs.pop(\"location\", DEFAULT_LOCATION)\nself._agent_location = {\n\"location\": Location(\nlatitude=location[\"latitude\"], longitude=location[\"longitude\"]\n)\n}\nself._set_personality_data = kwargs.pop(\n\"personality_data\", DEFAULT_PERSONALITY_DATA\n)\nenforce(\nlen(self._set_personality_data) == 2\nand \"piece\" in self._set_personality_data\nand \"value\" in self._set_personality_data,\n\"personality_data must contain keys `key` and `value`\",\n)\nself._set_classification = kwargs.pop(\"classification\", DEFAULT_CLASSIFICATION)\nenforce(\nlen(self._set_classification) == 2\nand \"piece\" in self._set_classification\nand \"value\" in self._set_classification,\n\"classification must contain keys `key` and `value`\",\n)\nself._set_service_data = kwargs.pop(\"service_data\", DEFAULT_SERVICE_DATA)\nenforce(\nlen(self._set_service_data) == 2\nand \"key\" in self._set_service_data\nand \"value\" in self._set_service_data,\n\"service_data must contain keys `key` and `value`\",\n)\nself._remove_service_data = {\"key\": self._set_service_data[\"key\"]}\nsuper().__init__(**kwargs)\ndef get_location_description(self) -> Description:\n\"\"\"\n        Get the location description.\n        :return: a description of the agent's location\n        \"\"\"\ndescription = Description(\nself._agent_location, data_model=AGENT_LOCATION_MODEL,\n)\nreturn description\ndef get_register_service_description(self) -> Description:\n\"\"\"\n        Get the register service description.\n        :return: a description of the offered services\n        \"\"\"\ndescription = Description(\nself._set_service_data, data_model=AGENT_SET_SERVICE_MODEL,\n)\nreturn description\ndef get_register_personality_description(self) -> Description:\n\"\"\"\n        Get the register personality description.\n        :return: a description of the personality\n        \"\"\"\ndescription = Description(\nself._set_personality_data, data_model=AGENT_PERSONALITY_MODEL,\n)\nreturn description\ndef get_register_classification_description(self) -> Description:\n\"\"\"\n        Get the register classification description.\n        :return: a description of the classification\n        \"\"\"\ndescription = Description(\nself._set_classification, data_model=AGENT_PERSONALITY_MODEL,\n)\nreturn description\ndef get_unregister_service_description(self) -> Description:\n\"\"\"\n        Get the unregister service description.\n        :return: a description of the to be removed service\n        \"\"\"\ndescription = Description(\nself._remove_service_data, data_model=AGENT_REMOVE_SERVICE_MODEL,\n)\nreturn description\n

We create a Model type dialogue class and place it in dialogues.py. These classes ensure that the message flow satisfies the fetchai/oef_search:1.0.0 protocol and keep track of the individual messages being sent and received.

from typing import Any\nfrom aea.protocols.base import Address, Message\nfrom aea.protocols.dialogue.base import Dialogue as BaseDialogue\nfrom aea.skills.base import Model\nfrom packages.fetchai.protocols.oef_search.dialogues import (\nOefSearchDialogue as BaseOefSearchDialogue,\n)\nfrom packages.fetchai.protocols.oef_search.dialogues import (\nOefSearchDialogues as BaseOefSearchDialogues,\n)\nOefSearchDialogue = BaseOefSearchDialogue\nclass OefSearchDialogues(Model, BaseOefSearchDialogues):\n\"\"\"This class keeps track of all oef_search dialogues.\"\"\"\ndef __init__(self, **kwargs: Any) -> None:\n\"\"\"\n        Initialize dialogues.\n        :param agent_address: the address of the agent for whom dialogues are maintained\n        :return: None\n        \"\"\"\nModel.__init__(self, **kwargs)\ndef role_from_first_message(  # pylint: disable=unused-argument\nmessage: Message, receiver_address: Address\n) -> BaseDialogue.Role:\n\"\"\"Infer the role of the agent from an incoming/outgoing first message\n            :param message: an incoming/outgoing first message\n            :param receiver_address: the address of the receiving agent\n            :return: The role of the agent\n            \"\"\"\nreturn BaseOefSearchDialogue.Role.AGENT\nBaseOefSearchDialogues.__init__(\nself,\nself_address=str(self.skill_id),\nrole_from_first_message=role_from_first_message,\n)\n

Finally, we have a handler, placed in handlers.py. The handler deals with handling any error messages which might occur during service registration:

from typing import Optional, cast\nfrom aea.configurations.base import PublicId\nfrom aea.protocols.base import Message\nfrom aea.skills.base import Handler\nfrom packages.fetchai.protocols.oef_search.message import OefSearchMessage\nfrom packages.fetchai.skills.simple_service_registration.behaviours import (\nServiceRegistrationBehaviour,\n)\nfrom packages.fetchai.skills.simple_service_registration.dialogues import (\nOefSearchDialogue,\nOefSearchDialogues,\n)\nclass OefSearchHandler(Handler):\n\"\"\"This class implements an OEF search handler.\"\"\"\nSUPPORTED_PROTOCOL = OefSearchMessage.protocol_id  # type: Optional[PublicId]\ndef setup(self) -> None:\n\"\"\"Call to setup the handler.\"\"\"\ndef handle(self, message: Message) -> None:\n\"\"\"\n        Implement the reaction to a message.\n        :param message: the message\n        :return: None\n        \"\"\"\noef_search_msg = cast(OefSearchMessage, message)\n# recover dialogue\noef_search_dialogues = cast(\nOefSearchDialogues, self.context.oef_search_dialogues\n)\noef_search_dialogue = cast(\nOptional[OefSearchDialogue], oef_search_dialogues.update(oef_search_msg)\n)\nif oef_search_dialogue is None:\nself._handle_unidentified_dialogue(oef_search_msg)\nreturn\n# handle message\nif oef_search_msg.performative == OefSearchMessage.Performative.SUCCESS:\nself._handle_success(oef_search_msg, oef_search_dialogue)\nelif oef_search_msg.performative == OefSearchMessage.Performative.OEF_ERROR:\nself._handle_error(oef_search_msg, oef_search_dialogue)\nelse:\nself._handle_invalid(oef_search_msg, oef_search_dialogue)\ndef teardown(self) -> None:\n\"\"\"\n        Implement the handler teardown.\n        :return: None\n        \"\"\"\ndef _handle_unidentified_dialogue(self, oef_search_msg: OefSearchMessage) -> None:\n\"\"\"\n        Handle an unidentified dialogue.\n        :param msg: the message\n        \"\"\"\nself.context.logger.info(\n\"received invalid oef_search message={}, unidentified dialogue.\".format(\noef_search_msg\n)\n)\ndef _handle_success(\nself,\noef_search_success_msg: OefSearchMessage,\noef_search_dialogue: OefSearchDialogue,\n) -> None:\n\"\"\"\n        Handle an oef search message.\n        :param oef_search_success_msg: the oef search message\n        :param oef_search_dialogue: the dialogue\n        :return: None\n        \"\"\"\nself.context.logger.info(\n\"received oef_search success message={} in dialogue={}.\".format(\noef_search_success_msg, oef_search_dialogue\n)\n)\ntarget_message = cast(\nOefSearchMessage,\noef_search_dialogue.get_message_by_id(oef_search_success_msg.target),\n)\nif (\ntarget_message.performative\n== OefSearchMessage.Performative.REGISTER_SERVICE\n):\ndescription = target_message.service_description\ndata_model_name = description.data_model.name\nregistration_behaviour = cast(\nServiceRegistrationBehaviour, self.context.behaviours.service,\n)\nif \"location_agent\" in data_model_name:\nregistration_behaviour.register_service()\nelif \"set_service_key\" in data_model_name:\nregistration_behaviour.register_genus()\nelif (\n\"personality_agent\" in data_model_name\nand description.values[\"piece\"] == \"genus\"\n):\nregistration_behaviour.register_classification()\nelif (\n\"personality_agent\" in data_model_name\nand description.values[\"piece\"] == \"classification\"\n):\nself.context.logger.info(\n\"the agent, with its genus and classification, and its service are successfully registered on the SOEF.\"\n)\nelse:\nself.context.logger.warning(\nf\"received soef SUCCESS message as a reply to the following unexpected message: {target_message}\"\n)\ndef _handle_error(\nself,\noef_search_error_msg: OefSearchMessage,\noef_search_dialogue: OefSearchDialogue,\n) -> None:\n\"\"\"\n        Handle an oef search message.\n        :param oef_search_error_msg: the oef search message\n        :param oef_search_dialogue: the dialogue\n        :return: None\n        \"\"\"\nself.context.logger.info(\n\"received oef_search error message={} in dialogue={}.\".format(\noef_search_error_msg, oef_search_dialogue\n)\n)\ntarget_message = cast(\nOefSearchMessage,\noef_search_dialogue.get_message_by_id(oef_search_error_msg.target),\n)\nif (\ntarget_message.performative\n== OefSearchMessage.Performative.REGISTER_SERVICE\n):\nregistration_behaviour = cast(\nServiceRegistrationBehaviour, self.context.behaviours.service,\n)\nregistration_behaviour.failed_registration_msg = target_message\ndef _handle_invalid(\nself, oef_search_msg: OefSearchMessage, oef_search_dialogue: OefSearchDialogue\n) -> None:\n\"\"\"\n        Handle an oef search message.\n        :param oef_search_msg: the oef search message\n        :param oef_search_dialogue: the dialogue\n        :return: None\n        \"\"\"\nself.context.logger.warning(\n\"cannot handle oef_search message of performative={} in dialogue={}.\".format(\noef_search_msg.performative, oef_search_dialogue,\n)\n)\n

The associated skill.yaml is:

name: simple_service_registration\nauthor: fetchai\nversion: 0.20.0\ntype: skill\ndescription: The simple service registration skills is a skill to register a service.\nlicense: Apache-2.0\naea_version: '>=1.0.0, <2.0.0'\nfingerprint:\nREADME.md: QmUgCcR7sDBQeeCBRKwDT7tPBTi3t4zSibyEqR3xdQUKmh\n__init__.py: QmZd48HmYDr7FMxNaVeGfWRvVtieEdEV78hd7h7roTceP2\nbehaviours.py: QmQHf6QL5aBtLJ34D2tdcbjJLbzom9gaA3HWgRn3rWyigM\ndialogues.py: QmTT9dvFhWt6qvxjwBfMFDTrgEtgWbvgANYafyRg2BXwcR\nhandlers.py: QmZqPt8toGbJgTT6NZBLxjkusrQCZ8GmUEwcmqZ1sd7DpG\nstrategy.py: QmVXfQpk4cjDw576H2ELE12tEiN5brPkwvffvcTeMbsugA\nfingerprint_ignore_patterns: []\nconnections: []\ncontracts: []\nprotocols:\n- fetchai/oef_search:1.1.7\nskills: []\nbehaviours:\nservice:\nargs:\nmax_soef_registration_retries: 5\nservices_interval: 30\nclass_name: ServiceRegistrationBehaviour\nhandlers:\noef_search:\nargs: {}\nclass_name: OefSearchHandler\nmodels:\noef_search_dialogues:\nargs: {}\nclass_name: OefSearchDialogues\nstrategy:\nargs:\nclassification:\npiece: classification\nvalue: seller\nlocation:\nlatitude: 51.5194\nlongitude: 0.127\npersonality_data:\npiece: genus\nvalue: data\nservice_data:\nkey: seller_service\nvalue: generic_service\nclass_name: Strategy\ndependencies: {}\nis_abstract: false\n
"},{"location":"aea-framework-documentation/skill-guide/#step-9-run-the-search-aea","title":"Step 9: Run the Search AEA","text":"

First, create the private key for the search AEA based on the network you want to transact. To generate and add a private-public key pair for Fetch.ai Dorado use:

aea generate-key fetchai\naea add-key fetchai fetchai_private_key.txt\n

Next, create a private key used to secure the AEA's communications:

aea generate-key fetchai fetchai_connection_private_key.txt\naea add-key fetchai fetchai_connection_private_key.txt --connection\n

Finally, certify the key for use by the connections that request that:

aea issue-certificates\n

Then, in the search AEA, run this command (replace SOME_ADDRESS with the correct value as described above):

aea config set --type dict vendor.fetchai.connections.p2p_libp2p.config \\\n'{\n  \"delegate_uri\": \"127.0.0.1:11001\",\n  \"entry_peers\": [\"/dns4/127.0.0.1/tcp/9000/p2p/16Uiu2HAm1uJpFsqSgHStJdtTBPpDme1fo8uFEvvY182D2y89jQuj\"],\n  \"local_uri\": \"127.0.0.1:9001\",\n  \"log_file\": \"libp2p_node.log\",\n  \"public_uri\": \"127.0.0.1:9001\"\n}'\n

This allows the search AEA to connect to the same local agent communication network as the service registration AEA.

We can then launch our AEA.

aea run\n

We can see that the AEA sends search requests to the SOEF search node and receives search responses from the SOEF search node. The search response returns one or more agents (the service provider and potentially other agents which match the query).

We stop the AEA with CTRL + C.

"},{"location":"aea-framework-documentation/skill-guide/#next-steps","title":"Next Steps","text":""},{"location":"aea-framework-documentation/skill-guide/#recommended","title":"Recommended","text":"

We recommend you continue with the next step in the 'Getting Started' series:

  • Core components (Part 2)
"},{"location":"aea-framework-documentation/skill-guide/#relevant-deep-dives","title":"Relevant Deep-Dives","text":"

This guide goes through a more elaborate scenario than the one on this page, where after finding each other, the two AEAs negotiate and trade via a ledger.

"},{"location":"aea-framework-documentation/skill-testing/","title":"Testing Skills","text":"

In this guide, we describe some of the tools the framework offers for testing skills.

"},{"location":"aea-framework-documentation/skill-testing/#the-baseskilltestcase-class","title":"The BaseSkillTestCase Class","text":"

The framework offers a BaseSkillTestCase class which you can subclass and write your test cases with.

Let us assume you want to test the my_behaviour behaviour of a CustomSkill skill you have developed.

You can create a TestMyBehaviour class which inherits BaseSkillTestCase as below:

import asyncio\nfrom asyncio import Queue\nfrom pathlib import Path\nfrom types import SimpleNamespace\nfrom typing import cast\nfrom aea.configurations.constants import DEFAULT_LEDGER\nfrom aea.context.base import AgentContext\nfrom aea.crypto.ledger_apis import DEFAULT_CURRENCY_DENOMINATIONS\nfrom aea.identity.base import Identity\nfrom aea.multiplexer import AsyncMultiplexer, OutBox, Multiplexer\nfrom aea.skills.tasks import TaskManager\nfrom aea.test_tools.test_skill import BaseSkillTestCase\nclass TestMyBehaviour(BaseSkillTestCase):\n\"\"\"Test my_behaviours of the custom skill.\"\"\"\npath_to_skill = Path(\"path_to_this_skill\")\n
"},{"location":"aea-framework-documentation/skill-testing/#specifying-skill-path","title":"Specifying Skill Path","text":"

You must then specify the path to your skill directory via path_to_skill to allow the skill to be loaded and tested. This must be the directory in which skill.yaml of your skill resides.

"},{"location":"aea-framework-documentation/skill-testing/#setting-up-each-test","title":"Setting up Each Test","text":"

You can add a setup() class method to set the environment up for each of your tests. This code will be executed before every test method. If you do include this method, you must call the setup() method of the BaseSkillTestCase class via super().setup().

@classmethod\ndef setup(cls):\n\"\"\"Setup the test class.\"\"\"\nsuper().setup()\ncls.my_behaviour = cast(\nMyBehaviour, cls._skill.skill_context.behaviours.my_behaviour\n)\n

In the above, we make the my_behaviour behaviour object accessible for every test.

"},{"location":"aea-framework-documentation/skill-testing/#skill-and-skill-context","title":"Skill and Skill Context","text":"

The skill object itself is exposed via a property. So you can access the skill object by self.skill and by extension all of its attributes. This crucially includes the complete skill_context. This means that for example, every component of the skill (e.g. behaviours, handlers, models) can be accessed via the skill context.

In the above code snippet, my_behavior is accessed and exposed as a class attribute. Note accessing the skill context is slightly different in the above because it is a class method. If this was a test method, you could access the behaviour via self.skill.skill_context.behaviours.my_behaviour.

"},{"location":"aea-framework-documentation/skill-testing/#dummy-agent-context","title":"Dummy Agent Context","text":"

The loaded skill is also fed a dummy agent_context complete with an identity, outbox, decision_maker_queue and so on, to allow the skill to be properly loaded and have access to everything it requires to function. The agent_context object fed to the skill is shown below:

_multiplexer = AsyncMultiplexer()\n_multiplexer._out_queue = (asyncio.Queue())\nagent_context = AgentContext(\nidentity=Identity(\"test_agent_name\", \"test_agent_address\", \"test_agent_public_key\"),\nconnection_status=_multiplexer.connection_status,\noutbox=OutBox(cast(Multiplexer, cls._multiplexer)),\ndecision_maker_message_queue=Queue(),\ndecision_maker_handler_context=SimpleNamespace(),\ntask_manager=TaskManager(),\ndefault_ledger_id=DEFAULT_LEDGER,\ncurrency_denominations={},\ndefault_connection=None,\ndefault_routing={},\nsearch_service_address=\"dummy_search_service_address\",\ndecision_maker_address=\"dummy_decision_maker_address\",\ndata_dir=\".\"\n)\n
"},{"location":"aea-framework-documentation/skill-testing/#some-useful-skill-attributes","title":"Some Useful Skill Attributes","text":"

Some of the useful objects you can access in your test class for the loaded skill are below:

  • self.skill.skill_context.agent_address: this is the agent identity the skill uses and is set to \"test_agent_address\".
  • self.skill.skill_context.search_service_address: this is the address of the search service and is set to \"dummy_search_service_address\".
  • self.skill.skill_context.skill_id: this is the id of the skill.
  • self.skill.skill_context.decision_maker_address: this is the address of the decision maker and is set to \"dummy_decision_maker_address\".
"},{"location":"aea-framework-documentation/skill-testing/#some-useful-baseskilltestcase-methods","title":"Some Useful BaseSkillTestCase Methods","text":"

There are a number of methods that BaseSkillTestCase offers to make testing skills easier. Some of these are mentioned below. For the rest, consult the API for BaseSkillTestCase:

  • self.get_quantity_in_outbox(): gives you the number of messages which are in the outbox. After running a part of the skill which is expected to send messages, you can use this method to assert the correct number of messages are indeed sent.
  • self.get_message_from_outbox(): gives you the last message in the outbox. Together with the above, you can use this method to grab the last message sent by the skill code you tested and check this is indeed the expected message.
  • self.message_has_attributes(actual_message: Message, message_type: Type[Message], **kwargs,): you can use this method in tandem with the above method to check that a message has the attributes you expect it to have. You have to supply it with the actual message (e.g. using self.get_message_from_outbox()), specify its expected type (e.g. FipaMessage), and any other attribute you expect the message to have (e.g. message_id is 1) may be provided via keyword arguments.
  • self.build_incoming_message: this is an especially useful method to test handlers. Since handlers handle incoming messages, you can create an incoming message using this method to feed it to the handler and test its execution.
"},{"location":"aea-framework-documentation/skill-testing/#checking-logger-output","title":"Checking Logger Output","text":"

You can check the output of your skill's logger by mocking it using unittest.mock before executing a part of your skill as such:

import logging\nfrom unittest import mock\nwith mock.patch.object(self.my_behaviour.context.logger, \"log\") as mock_logger:\nself.my_behaviour.act()\nmock_logger.assert_any_call(logging.INFO, \"some_logger_message\")\n

In the above, we mock the logger before running my_behaviour's act() method and check that the string \"some_logger_message\" is indeed passed to the logger.

"},{"location":"aea-framework-documentation/skill-testing/#next-steps","title":"Next Steps","text":"

You can consult the fetchai/generic_buyer and fetchai/generic_seller skills and their associated tests here to study how BaseSkillTestCase can help you in testing your skills.

You can also refer to the API to study the different methods BaseSkillTestCase makes available to make testing your skills easier.

"},{"location":"aea-framework-documentation/skill/","title":"Skills","text":"

Skills are the core focus of the framework's extensibility as they implement business logic to deliver economic value for the AEA. They are self-contained capabilities that AEAs can dynamically take on board, in order to expand their effectiveness in different situations.

A skill encapsulates implementations of the three abstract base classes Handler, Behaviour, Model, and is closely related with the abstract base class Task:

  • Handler: each skill has zero, one or more Handler objects, each responsible for the registered messaging protocol. Handlers implement AEAs' reactive behaviour. If the AEA understands the protocol referenced in a received Envelope, the Handler reacts appropriately to the corresponding message. Each Handler is responsible for only one protocol. A Handler is also capable of dealing with internal messages (see next section).
  • Behaviour: zero, one or more Behaviours encapsulate actions which further the AEAs goal and are initiated by internals of the AEA, rather than external events. Behaviours implement AEAs' pro-activeness. The framework provides a number of abstract base classes implementing different types of behaviours (e.g. cyclic/one-shot/finite-state-machine/etc.).
  • Model: zero, one or more Models that inherit from the Model class. Models encapsulate custom objects which are made accessible to any part of a skill via the SkillContext.
  • Task: zero, one or more Tasks encapsulate background work internal to the AEA. Task differs from the other three in that it is not a part of skills, but Tasks are declared in or from skills if a packaging approach for AEA creation is used.

A skill can read (parts of) the state of the AEA (as summarised in the AgentContext), and propose actions to the AEA according to its specific logic. As such, more than one skill could exist per protocol, competing with each other in suggesting to the AEA the best course of actions to take. In technical terms this means skills are horizontally arranged.

For instance, an AEA who is trading goods, could subscribe to more than one skill, where each skill corresponds to a different trading strategy. The skills could then read the preference and ownership state of the AEA, and independently suggest profitable transactions.

The framework places no limits on the complexity of skills. They can implement simple (e.g. if-this-then-that) or complex (e.g. a deep learning model or reinforcement learning agent).

The framework provides one default skill, called error. Additional skills can be added as packages.

"},{"location":"aea-framework-documentation/skill/#independence-of-skills","title":"Independence of Skills","text":"

Skills are horizontally layered, that is they run independently of each other. They also cannot access each other's state.

Two skills can communicate with each other in two ways. The skill context provides access via self.context.shared_state to a key-value store which allows skills to share state. A skill can also define as a callback another skill in a message to the decision maker.

"},{"location":"aea-framework-documentation/skill/#context","title":"Context","text":"

The skill has a SkillContext object which is shared by all Handler, Behaviour, and Model objects. The skill context also has a link to the AgentContext. The AgentContext provides read access to AEA specific information like the public key and address of the AEA, its preferences and ownership state. It also provides access to the OutBox.

This means it is possible to, at any point, grab the context and have access to the code in other parts of the skill and the AEA.

For example, in the ErrorHandler(Handler) class, the code often grabs a reference to its context and by doing so can access initialised and running framework objects such as an OutBox for putting messages into.

self.context.outbox.put_message(message=reply)\n

Moreover, you can read/write to the agent context namespace by accessing the attribute SkillContext.namespace.

Importantly, however, a skill does not have access to the context of another skill or protected AEA components like the DecisionMaker.

"},{"location":"aea-framework-documentation/skill/#what-to-code","title":"What to Code","text":"

Each of the skill classes has three methods that must be implemented. All of them include a setup() and teardown() method which the developer must implement.

Then there is a specific method that the framework requires for each class.

"},{"location":"aea-framework-documentation/skill/#handlerspy","title":"handlers.py","text":"

There can be none, one or more Handler class per skill.

Handler classes can receive Message objects of one protocol type only. However, Handler classes can send Envelope objects of any type of protocol they require.

  • handle(self, message: Message): is where the skill receives a Message of the specified protocol and decides what to do with it.

A handler can be registered in one way:

  • By declaring it in the skill configuration file skill.yaml (see below).

It is possible to register new handlers dynamically by enqueuing new Handler instances in the queue context.new_handlers, e.g. in a skill component we can write:

self.context.new_handlers.put(MyHandler(name=\"my_handler\", skill_context=self.context))\n
"},{"location":"aea-framework-documentation/skill/#behaviourspy","title":"behaviours.py","text":"

Conceptually, a Behaviour class contains the business logic specific to initial actions initiated by the AEA rather than reactions to other events.

There can be one or more Behaviour classes per skill. The developer must create a subclass from the abstract class Behaviour to create a new Behaviour.

  • act(self): is how the framework calls the Behaviour code.

A behaviour can be registered in two ways:

  • By declaring it in the skill configuration file skill.yaml (see below)
  • In any part of the code of the skill, by enqueuing new Behaviour instances in the queue context.new_behaviours. In that case, setupis not called by the framework, as the behaviour will be added after the AEA setup is complete.

The framework supports different types of behaviours:

  • OneShotBehaviour: this behaviour is executed only once.
  • TickerBehaviour: the act() method is called every tick_interval. E.g. if the TickerBehaviour subclass is instantiated

There is another category of behaviours, called CompositeBehaviour:

  • SequenceBehaviour: a sequence of Behaviour classes, executed one after the other.
  • FSMBehaviour: a state machine of State behaviours. A state is in charge of scheduling the next state.

If your behaviour fits one of the above, we suggest subclassing your behaviour class with that behaviour class. Otherwise, you can always subclass the general-purpose Behaviour class.

Follows an example of a custom behaviour:

from aea.skills.behaviours import OneShotBehaviour\nclass HelloWorldBehaviour(OneShotBehaviour):\ndef setup(self):\n\"\"\"This method is called once, when the behaviour gets loaded.\"\"\"\ndef act(self):\n\"\"\"This methods is called in every iteration of the agent main loop.\"\"\"\nprint(\"Hello, World!\")\ndef teardown(self):\n\"\"\"This method is called once, when the behaviour is teared down.\"\"\"\n

If we want to register this behaviour dynamically, in any part of the skill code (i.e. wherever the skill context is available), we can write:

self.context.new_behaviours.put(HelloWorldBehaviour(name=\"hello_world\", skill_context=self.context))\n

Or, equivalently to the previous two code blocks:

def hello():\nprint(\"Hello, World!\")\nself.context.new_behaviours.put(OneShotBehaviour(act=hello, name=\"hello_world\", skill_context=self.context))\n

The callable passed to the act parameter is equivalent to the implementation of the act method described above.

The framework is then in charge of registering the behaviour and scheduling it for execution.

"},{"location":"aea-framework-documentation/skill/#taskspy","title":"tasks.py","text":"

Conceptually, a Task is where the developer codes any internal tasks the AEA requires.

There can be one or more Task classes per skill. The developer subclasses abstract class Task to create a new Task.

  • execute(self): is how the framework calls a Task.

The Task class implements the functor pattern. An instance of the Task class can be invoked as if it were an ordinary function. Once completed, it will store the result in the property result. Raises error if the task has not been executed yet, or an error occurred during computation.

We suggest using the task_manager, accessible through the skill context, to manage long-running tasks. The task manager uses multiprocessing to schedule tasks, so be aware that the changes on the task object will not be updated.

Here's an example:

In tasks.py:

from aea.skills.tasks import Task\ndef nth_prime_number(n: int) -> int:\n\"\"\"A naive algorithm to find the n_th prime number.\"\"\"\nassert n > 0\nprimes = [2]\nnum = 3\nwhile len(primes) < n:\nfor p in primes:\nif num % p == 0:\nbreak\nelse:\nprimes.append(num)\nnum += 2\nreturn primes[-1]\nclass LongTask(Task):\ndef setup(self):\n\"\"\"Set the task up before execution.\"\"\"\ndef execute(self, n: int):\nreturn nth_prime_number(n)\ndef teardown(self):\n\"\"\"Clean the task up after execution.\"\"\"\n

In behaviours.py:

from aea.skills.behaviours import TickerBehaviour\nfrom packages.my_author_name.skills.my_skill.tasks import LongTask\nclass MyBehaviour(TickerBehaviour):\ndef setup(self):\n\"\"\"Setup behaviour.\"\"\"\nmy_task = LongTask()\ntask_id = self.context.task_manager.enqueue_task(my_task, args=(10000, ))\nself.async_result = self.context.task_manager.get_task_result(task_id)  # type: multiprocessing.pool.AsyncResult\ndef act(self):\n\"\"\"Act implementation.\"\"\"\nif self.async_result.ready() is False:\nprint(\"The task is not finished yet.\")\nelse:\ncompleted_task = self.async_result.get()  # type: LongTask\nprint(\"The result is:\", completed_task.result)\n# Stop the skill\nself.context.is_active = False\ndef teardown(self):\n\"\"\"Teardown behaviour.\"\"\"\n
"},{"location":"aea-framework-documentation/skill/#models","title":"Models","text":"

The developer might want to add other classes on the context level which are shared equally across the Handler, Behaviour and Task classes. To this end, the developer can subclass an abstract Model. These models are made available on the context level upon initialization of the AEA.

Say, the developer has a class called SomeModel

class SomeModel(Model):\n...\n

Then, an instance of this class is available on the context level like so:

some_model = self.context.some_model\n
"},{"location":"aea-framework-documentation/skill/#skill-configuration","title":"Skill Configuration","text":"

Each skill has a skill.yaml configuration file which lists all Behaviour, Handler, and Task objects pertaining to the skill.

It also details the protocol types used in the skill and points to shared modules, i.e. modules of type Model, which allow custom classes within the skill to be accessible in the skill context.

name: echo\nauthors: fetchai\nversion: 0.1.0\nlicense: Apache-2.0\nbehaviours:\necho:\nclass_name: EchoBehaviour\nargs:\ntick_interval: 1.0\nhandlers:\necho:\nclass_name: EchoHandler\nargs:\nfoo: bar\nmodels: {}\ndependencies: {}\nprotocols:\n- fetchai/default:1.1.7\n
"},{"location":"aea-framework-documentation/skill/#error-skill","title":"Error Skill","text":"

All AEAs have a default error skill that contains error handling code for a number of scenarios:

  • Received envelopes with unsupported protocols
  • Received envelopes with unsupported skills (i.e. protocols for which no handler is registered)
  • Envelopes with decoding errors
  • Invalid messages with respect to the registered protocol

The error skill relies on the fetchai/default:1.0.0 protocol which provides error codes for the above.

"},{"location":"aea-framework-documentation/skill/#custom-error-handler","title":"Custom Error Handler","text":"

The framework implements a default ErrorHandler. You can implement your own and mount it. The easiest way to do this is to run the following command to scaffold a custom ErrorHandler:

aea scaffold error-handler\n

Now you will see a file called error_handler.py in the AEA project root. You can then implement your own custom logic to process messages.

"},{"location":"aea-framework-documentation/standalone-transaction/","title":"Create Stand-Alone Transaction","text":"

In this guide, we will generate some wealth for the Fetch.ai testnet and create a standalone transaction. After the completion of the transaction, we get the transaction digest. With this we can search for the transaction on the block explorer

This guide requires the aea-ledger-fetchai plug-in installed in your Python environment:

pip install aea-ledger-fetchai\n

First, import the python and application specific libraries and set the static variables.

import logging\nfrom aea_ledger_fetchai import FetchAICrypto\nfrom aea.crypto.helpers import create_private_key, try_generate_testnet_wealth\nfrom aea.crypto.ledger_apis import LedgerApis\nfrom aea.crypto.wallet import Wallet\nlogger = logging.getLogger(\"aea\")\nlogging.basicConfig(level=logging.INFO)\nFETCHAI_PRIVATE_KEY_FILE_1 = \"fetchai_private_key_1.txt\"\nFETCHAI_PRIVATE_KEY_FILE_2 = \"fetchai_private_key_2.txt\"\n
"},{"location":"aea-framework-documentation/standalone-transaction/#create-the-private-keys","title":"Create the Private Keys","text":"
    # Create a private keys\ncreate_private_key(\nFetchAICrypto.identifier, private_key_file=FETCHAI_PRIVATE_KEY_FILE_1\n)\ncreate_private_key(\nFetchAICrypto.identifier, private_key_file=FETCHAI_PRIVATE_KEY_FILE_2\n)\n
"},{"location":"aea-framework-documentation/standalone-transaction/#create-the-wallets","title":"Create the Wallets","text":"

Once we created the private keys we need to generate the wallets.

    # Set up the wallets\nwallet_1 = Wallet({FetchAICrypto.identifier: FETCHAI_PRIVATE_KEY_FILE_1})\nwallet_2 = Wallet({FetchAICrypto.identifier: FETCHAI_PRIVATE_KEY_FILE_2})\n
"},{"location":"aea-framework-documentation/standalone-transaction/#generate-wealth","title":"Generate Wealth","text":"

Since we want to send funds from wallet_1 to wallet_2, we need to generate some wealth for the wallet_1. We can do this with the following code

    # Generate some wealth\ntry_generate_testnet_wealth(\nFetchAICrypto.identifier, wallet_1.addresses[FetchAICrypto.identifier]\n)\n
"},{"location":"aea-framework-documentation/standalone-transaction/#send-transaction","title":"Send Transaction","text":"

Finally, we create a transaction that sends the funds to the wallet_2

    # Create the transaction and send it to the ledger.\ntx_nonce = LedgerApis.generate_tx_nonce(\nFetchAICrypto.identifier,\nwallet_2.addresses.get(FetchAICrypto.identifier),\nwallet_1.addresses.get(FetchAICrypto.identifier),\n)\ntransaction = LedgerApis.get_transfer_transaction(\nidentifier=FetchAICrypto.identifier,\nsender_address=wallet_1.addresses.get(FetchAICrypto.identifier),\ndestination_address=wallet_2.addresses.get(FetchAICrypto.identifier),\namount=1,\ntx_fee=1,\ntx_nonce=tx_nonce,\n)\nsigned_transaction = wallet_1.sign_transaction(\nFetchAICrypto.identifier, transaction\n)\ntransaction_digest = LedgerApis.send_signed_transaction(\nFetchAICrypto.identifier, signed_transaction\n)\nlogger.info(\"Transaction complete.\")\nlogger.info(\"The transaction digest is {}\".format(transaction_digest))\n
Stand-alone transaction full code:
import logging\nfrom aea_ledger_fetchai import FetchAICrypto\nfrom aea.crypto.helpers import create_private_key, try_generate_testnet_wealth\nfrom aea.crypto.ledger_apis import LedgerApis\nfrom aea.crypto.wallet import Wallet\nlogger = logging.getLogger(\"aea\")\nlogging.basicConfig(level=logging.INFO)\nFETCHAI_PRIVATE_KEY_FILE_1 = \"fetchai_private_key_1.txt\"\nFETCHAI_PRIVATE_KEY_FILE_2 = \"fetchai_private_key_2.txt\"\ndef run():\n\"\"\"Run demo.\"\"\"\n# Create a private keys\ncreate_private_key(\nFetchAICrypto.identifier, private_key_file=FETCHAI_PRIVATE_KEY_FILE_1\n)\ncreate_private_key(\nFetchAICrypto.identifier, private_key_file=FETCHAI_PRIVATE_KEY_FILE_2\n)\n# Set up the wallets\nwallet_1 = Wallet({FetchAICrypto.identifier: FETCHAI_PRIVATE_KEY_FILE_1})\nwallet_2 = Wallet({FetchAICrypto.identifier: FETCHAI_PRIVATE_KEY_FILE_2})\n# Generate some wealth\ntry_generate_testnet_wealth(\nFetchAICrypto.identifier, wallet_1.addresses[FetchAICrypto.identifier]\n)\nlogger.info(\n\"Sending amount to {}\".format(wallet_2.addresses.get(FetchAICrypto.identifier))\n)\n# Create the transaction and send it to the ledger.\ntx_nonce = LedgerApis.generate_tx_nonce(\nFetchAICrypto.identifier,\nwallet_2.addresses.get(FetchAICrypto.identifier),\nwallet_1.addresses.get(FetchAICrypto.identifier),\n)\ntransaction = LedgerApis.get_transfer_transaction(\nidentifier=FetchAICrypto.identifier,\nsender_address=wallet_1.addresses.get(FetchAICrypto.identifier),\ndestination_address=wallet_2.addresses.get(FetchAICrypto.identifier),\namount=1,\ntx_fee=1,\ntx_nonce=tx_nonce,\n)\nsigned_transaction = wallet_1.sign_transaction(\nFetchAICrypto.identifier, transaction\n)\ntransaction_digest = LedgerApis.send_signed_transaction(\nFetchAICrypto.identifier, signed_transaction\n)\nlogger.info(\"Transaction complete.\")\nlogger.info(\"The transaction digest is {}\".format(transaction_digest))\nif __name__ == \"__main__\":\nrun()\n
"},{"location":"aea-framework-documentation/step-one/","title":"Ways to Build an AEA","text":"

There are a number of ways to build an AEA:

  • To start with, we recommended you build an AEA project step-by-step with the CLI tool as demonstrated in the quick start guide and described here.
  • Using the CLI aea fetch command, pull in an already built project and run as is or extend it to your needs.
  • The last option is to build an AEA programmatically as described here.

Sometimes, an AEA is more than is required for the task at hand. In particular, an AEA is much more than just an agent. In those cases, we suggest you have a look at the following two guides:

  • the AEA vs Agents guide shows the difference between an agent and an AEA in code,
  • the Use multiplexer standalone guide shows how to use the multiplexer on its own to receive and send envelopes.
"},{"location":"aea-framework-documentation/tac-skills-contract/","title":"TAC Skills Ledger-Based","text":"

The AEA TAC - trading agent competition - skills demonstrate an interaction between multiple AEAs in a game.

There are two types of AEAs:

  • The tac_controller which coordinates the game.
  • The tac_participant AEAs which compete in the game. The tac_participant AEAs trade tokens with each other to maximize their utility.
"},{"location":"aea-framework-documentation/tac-skills-contract/#discussion","title":"Discussion","text":"

This demo shows how agents negotiate autonomously with each other while they pursue their goals by participating in the Trading Agents Competition (TAC). The demo can be run against Fetchai or Ethereum ledger. Transactions are validated on an ERC1155 smart contract on the Fetchai Dorado or a local Ganache Ethereum testnet.

In the following video we discuss the framework and TAC in more detail:

"},{"location":"aea-framework-documentation/tac-skills-contract/#communication","title":"Communication","text":"

There are two types of interactions:

  • between the controller and participants (game management communication)
  • between the participants (negotiations)
"},{"location":"aea-framework-documentation/tac-skills-contract/#registration-communication","title":"Registration Communication","text":"

This diagram shows the communication between the various entities during the registration phase.

    sequenceDiagram\n        participant Agent_2\n        participant Agent_1\n        participant Search\n        participant Controller\n\n        activate Search\n        activate Controller\n\n        Controller->>Search: register_service\n        activate Agent_1\n        Agent_1->>Search: search\n        Search-->>Agent_1: controller\n        Agent_1->>Controller: register\n        activate Agent_2\n        Agent_2->>Search: search\n        Search-->>Agent_2: controller\n        Agent_2->>Controller: register\n        Controller->>Agent_1: game_data\n        Controller->>Agent_2: game_data\n\n        deactivate Agent_1\n        deactivate Agent_2\n        deactivate Search\n        deactivate Controller
"},{"location":"aea-framework-documentation/tac-skills-contract/#transaction-communication","title":"Transaction Communication","text":"

This diagram shows the communication between two AEAs and a controller. In this case, we have a Seller_Agent which is set up as a seller (and registers itself as such with the controller during the registration phase). We also have the Searching_Agent which is set up to search for sellers.

    sequenceDiagram\n        participant Buyer_Agent\n        participant Seller_Agent\n        participant Search\n        participant Controller\n\n        activate Buyer_Agent\n        activate Seller_Agent\n        activate Search\n        activate Controller\n\n        Seller_Agent->>Search: register_service\n        Buyer_Agent->>Search: search\n        Search-->>Buyer_Agent: list_of_agents\n        Buyer_Agent->>Seller_Agent: call_for_proposal\n        Seller_Agent->>Buyer_Agent: proposal\n        Buyer_Agent->>Seller_Agent: accept\n        Seller_Agent->>Buyer_Agent: match_accept\n        Seller_Agent->>Controller: transaction\n        Controller->>Controller: transaction_execution\n        Controller->>Seller_Agent: confirm_transaction\n        Controller->>Buyer_Agent: confirm_transaction\n\n        deactivate Buyer_Agent\n        deactivate Seller_Agent\n        deactivate Search\n        deactivate Controller

In the above case, the proposal received contains a set of goods to sell and an associated price. The buyer AEA needs to determine if this is a good deal for them, and if so, it accepts.

There is an equivalent diagram for seller AEAs set up to search for buyers and their interaction with AEAs which are registered as buyers. In that scenario, the proposal will instead be a list of goods that the buyer wishes to buy and the price it is willing to pay for them.

"},{"location":"aea-framework-documentation/tac-skills-contract/#preparation-instructions","title":"Preparation Instructions","text":""},{"location":"aea-framework-documentation/tac-skills-contract/#dependencies","title":"Dependencies","text":"

Follow the Preliminaries and Installation sections from the AEA quick start.

"},{"location":"aea-framework-documentation/tac-skills-contract/#demo-instructions-fetchai","title":"Demo Instructions (Fetchai)","text":"

Follow this instruction to run TAC against the fetch.ai Dorado testnet.

"},{"location":"aea-framework-documentation/tac-skills-contract/#fetch-tac-controller-aea","title":"Fetch TAC Controller AEA","text":"

In the root directory, fetch the controller AEA:

aea fetch fetchai/tac_controller_contract:0.32.5\ncd tac_controller_contract\naea install\naea build\n
Alternatively, create from scratch:

The following steps create the controller from scratch:

aea create tac_controller_contract\ncd tac_controller_contract\naea add connection fetchai/p2p_libp2p:0.27.5\naea add connection fetchai/soef:0.27.6\naea add connection fetchai/ledger:0.21.5\naea add skill fetchai/tac_control_contract:0.27.6\naea config set --type dict agent.dependencies \\\n'{\n  \"aea-ledger-fetchai\": {\"version\": \"<2.0.0,>=1.0.0\"},\n  \"aea-ledger-ethereum\": {\"version\": \"<2.0.0,>=1.0.0\"}\n}'\naea config set agent.default_connection fetchai/p2p_libp2p:0.27.5\naea config set agent.default_ledger fetchai\naea config set vendor.fetchai.connections.soef.config.chain_identifier fetchai_v2_misc\naea config set --type bool vendor.fetchai.skills.tac_control.is_abstract true\naea config set --type dict agent.default_routing \\\n'{\n  \"fetchai/contract_api:1.1.7\": \"fetchai/ledger:0.21.5\",\n  \"fetchai/ledger_api:1.1.7\": \"fetchai/ledger:0.21.5\",\n  \"fetchai/oef_search:1.1.7\": \"fetchai/soef:0.27.6\"\n}'\naea config set --type list vendor.fetchai.connections.p2p_libp2p.cert_requests \\\n'[{\"identifier\": \"acn\", \"ledger_id\": \"fetchai\", \"not_after\": \"2023-01-01\", \"not_before\": \"2022-01-01\", \"public_key\": \"fetchai\", \"save_path\": \".certs/conn_cert.txt\"}]'\naea install\naea build\n
"},{"location":"aea-framework-documentation/tac-skills-contract/#fetch-the-tac-participant-aeas","title":"Fetch the TAC Participant AEAs","text":"

In separate terminals, in the root directory, fetch at least two participants:

aea fetch fetchai/tac_participant_contract:0.22.5 --alias tac_participant_one\ncd tac_participant_one\naea install\naea build\ncd ..\naea fetch fetchai/tac_participant_contract:0.22.5 --alias tac_participant_two\ncd tac_participant_two\naea install\naea build\n
Alternatively, create from scratch:

In a separate terminal, in the root directory, create at least two tac participant AEAs:

aea create tac_participant_one\naea create tac_participant_two\n

Build participant one:

cd tac_participant_one\naea add connection fetchai/p2p_libp2p:0.27.5\naea add connection fetchai/soef:0.27.6\naea add connection fetchai/ledger:0.21.5\naea add skill fetchai/tac_participation:0.25.6\naea add skill fetchai/tac_negotiation:0.29.6\naea config set --type dict agent.dependencies \\\n'{\n  \"aea-ledger-fetchai\": {\"version\": \"<2.0.0,>=1.0.0\"},\n  \"aea-ledger-ethereum\": {\"version\": \"<2.0.0,>=1.0.0\"}\n}'\naea config set agent.default_connection fetchai/p2p_libp2p:0.27.5\naea config set agent.default_ledger fetchai\naea config set vendor.fetchai.connections.soef.config.chain_identifier fetchai_v2_misc\naea config set vendor.fetchai.skills.tac_participation.models.game.args.is_using_contract 'True' --type bool\naea config set vendor.fetchai.skills.tac_negotiation.models.strategy.args.is_contract_tx 'True' --type bool\naea config set --type dict agent.default_routing \\\n'{\n  \"fetchai/contract_api:1.1.7\": \"fetchai/ledger:0.21.5\",\n  \"fetchai/ledger_api:1.1.7\": \"fetchai/ledger:0.21.5\",\n  \"fetchai/oef_search:1.1.7\": \"fetchai/soef:0.27.6\"\n}'\naea config set --type dict agent.decision_maker_handler \\\n'{\n  \"dotted_path\": \"aea.decision_maker.gop:DecisionMakerHandler\",\n  \"file_path\": null\n}'\naea config set --type list vendor.fetchai.connections.p2p_libp2p.cert_requests \\\n'''[{\"identifier\": \"acn\", \"ledger_id\": \"fetchai\", \"message_format\": \"'{public_key}'\", \"not_after\": \"2023-01-01\", \"not_before\": \"2022-01-01\", \"public_key\": \"fetchai\", \"save_path\": \".certs/conn_cert.txt\"}]'''\naea install\naea build\n

Then, build participant two:

cd tac_participant_two\naea add connection fetchai/p2p_libp2p:0.27.5\naea add connection fetchai/soef:0.27.6\naea add connection fetchai/ledger:0.21.5\naea add skill fetchai/tac_participation:0.25.6\naea add skill fetchai/tac_negotiation:0.29.6\naea config set --type dict agent.dependencies \\\n'{\n  \"aea-ledger-fetchai\": {\"version\": \"<2.0.0,>=1.0.0\"},\n  \"aea-ledger-ethereum\": {\"version\": \"<2.0.0,>=1.0.0\"}\n}'\naea config set agent.default_connection fetchai/p2p_libp2p:0.27.5\naea config set agent.default_ledger fetchai\naea config set vendor.fetchai.connections.soef.config.chain_identifier fetchai_v2_misc\naea config set vendor.fetchai.skills.tac_participation.models.game.args.is_using_contract 'True' --type bool\naea config set vendor.fetchai.skills.tac_negotiation.models.strategy.args.is_contract_tx 'True' --type bool\naea config set --type dict agent.default_routing \\\n'{\n  \"fetchai/contract_api:1.1.7\": \"fetchai/ledger:0.21.5\",\n  \"fetchai/ledger_api:1.1.7\": \"fetchai/ledger:0.21.5\",\n  \"fetchai/oef_search:1.1.7\": \"fetchai/soef:0.27.6\"\n}'\naea config set --type dict agent.decision_maker_handler \\\n'{\n  \"dotted_path\": \"aea.decision_maker.gop:DecisionMakerHandler\",\n  \"file_path\": null\n}'\naea config set --type list vendor.fetchai.connections.p2p_libp2p.cert_requests \\\n'''[{\"identifier\": \"acn\", \"ledger_id\": \"fetchai\", \"message_format\": \"'{public_key}'\", \"not_after\": \"2023-01-01\", \"not_before\": \"2022-01-01\", \"public_key\": \"fetchai\", \"save_path\": \".certs/conn_cert.txt\"}]'''\naea install\naea build\n
"},{"location":"aea-framework-documentation/tac-skills-contract/#add-keys-for-all-aeas","title":"Add Keys for All AEAs","text":"

For every AEA in the competition (controller and participants):

First generate and add a private key:

aea generate-key fetchai\naea add-key fetchai fetchai_private_key.txt\n

Then create and add a separate private key for secure communication:

aea generate-key fetchai fetchai_connection_private_key.txt\naea add-key fetchai fetchai_connection_private_key.txt --connection\n

Finally, certify the key for use by the connections that request that:

aea issue-certificates\n
"},{"location":"aea-framework-documentation/tac-skills-contract/#update-the-game-parameters-in-the-controller","title":"Update the Game Parameters in the Controller","text":"

In the tac controller project, get and set the registration start time (set it to at least 5 minutes in the future):

aea config get vendor.fetchai.skills.tac_control_contract.models.parameters.args.registration_start_time\naea config set vendor.fetchai.skills.tac_control_contract.models.parameters.args.registration_start_time '01 01 2020  00:01'\n

To set the registration time, you may find handy the following command:

aea config set vendor.fetchai.skills.tac_control_contract.models.parameters.args.registration_start_time \"$(date -d \"5 minutes\" +'%d %m %Y %H:%M')\"\n
"},{"location":"aea-framework-documentation/tac-skills-contract/#update-the-connection-parameters","title":"Update the Connection Parameters","text":"

Update the connection parameters of the TAC participants to allow them to connect to the same local agent communication network as the TAC controller.

First, retrieve controller's local ACN address by running the following in the controller agent's project terminal:

aea get-multiaddress fetchai -c -i fetchai/p2p_libp2p:0.27.5 -u public_uri\n

Then, in participant one, run this command (replace SOME_ADDRESS with the value you retrieved above):

aea config set --type dict vendor.fetchai.connections.p2p_libp2p.config \\\n'{\n  \"delegate_uri\": \"127.0.0.1:11001\",\n  \"entry_peers\": [\"SOME_ADDRESS\"],\n  \"local_uri\": \"127.0.0.1:9001\",\n  \"log_file\": \"libp2p_node.log\",\n  \"public_uri\": \"127.0.0.1:9001\"\n}'\n

Do the same in participant two (beware of the different port numbers):

aea config set --type dict vendor.fetchai.connections.p2p_libp2p.config \\\n'{\n  \"delegate_uri\": \"127.0.0.1:11002\",\n  \"entry_peers\": [\"SOME_ADDRESS\"],\n  \"local_uri\": \"127.0.0.1:9002\",\n  \"log_file\": \"libp2p_node.log\",\n  \"public_uri\": \"127.0.0.1:9002\"\n}'\n
"},{"location":"aea-framework-documentation/tac-skills-contract/#fund-agents-accounts","title":"Fund Agents' Accounts","text":"

Retrieve the address of each agent (in each terminal):

aea get-address fetchai\n

Go to the Dorado block explorer and request some test tokens via Get Funds.

To check the wealth of an AEA, use:

aea get-wealth fetchai\n
"},{"location":"aea-framework-documentation/tac-skills-contract/#run-the-aeas","title":"Run the AEAs","text":"

First, launch the tac_contract_controller then the participants by executing the following from their respective terminals:

aea run\n

The CLI tool supports launching several agents at once. For example, assuming you followed the tutorial, you can launch both TAC participant agents as follows from the root directory (ensure you run the controller agent first as above):

aea launch tac_participant_one tac_participant_two\n

You may want to try --multithreaded option in order to run the agents in the same process.

"},{"location":"aea-framework-documentation/tac-skills-contract/#cleaning-up","title":"Cleaning up","text":"

When you're finished, delete your AEAs:

aea delete tac_controller_contract\naea delete tac_participant_one\naea delete tac_participant_two\n
"},{"location":"aea-framework-documentation/tac-skills-contract/#demo-instructions-ethereum","title":"Demo Instructions (Ethereum)","text":"

Follow this instruction to run TAC against a local Ganache Ethereum test-net.

"},{"location":"aea-framework-documentation/tac-skills-contract/#create-tac-controller-aea","title":"Create TAC Controller AEA","text":"

In the root directory, fetch the controller AEA:

aea fetch fetchai/tac_controller_contract:0.32.5\ncd tac_controller_contract\naea install\naea build\n
Alternatively, create from scratch:

The following steps create the controller from scratch:

aea create tac_controller_contract\ncd tac_controller_contract\naea add connection fetchai/p2p_libp2p:0.27.5\naea add connection fetchai/soef:0.27.6\naea add connection fetchai/ledger:0.21.5\naea add skill fetchai/tac_control_contract:0.27.6\naea config set --type dict agent.dependencies \\\n'{\n  \"aea-ledger-fetchai\": {\"version\": \"<2.0.0,>=1.0.0\"},\n  \"aea-ledger-ethereum\": {\"version\": \"<2.0.0,>=1.0.0\"}\n}'\naea config set agent.default_connection fetchai/p2p_libp2p:0.27.5\naea config set agent.default_ledger ethereum\naea config set vendor.fetchai.connections.soef.config.chain_identifier ethereum\naea config set --type bool vendor.fetchai.skills.tac_control.is_abstract true\naea config set --type dict agent.default_routing \\\n'{\n  \"fetchai/contract_api:1.1.7\": \"fetchai/ledger:0.21.5\",\n  \"fetchai/ledger_api:1.1.7\": \"fetchai/ledger:0.21.5\",\n  \"fetchai/oef_search:1.1.7\": \"fetchai/soef:0.27.6\"\n}'\naea config set --type list vendor.fetchai.connections.p2p_libp2p.cert_requests \\\n'[{\"identifier\": \"acn\", \"ledger_id\": \"ethereum\", \"not_after\": \"2023-01-01\", \"not_before\": \"2022-01-01\", \"public_key\": \"fetchai\", \"save_path\": \".certs/conn_cert.txt\"}]'\naea install\naea build\n
"},{"location":"aea-framework-documentation/tac-skills-contract/#fetch-the-tac-participant-aeas_1","title":"Fetch the TAC Participant AEAs","text":"

In separate terminals, in the root directory, fetch at least two participants:

aea fetch fetchai/tac_participant_contract:0.22.5 --alias tac_participant_one\ncd tac_participant_one\naea install\naea build\ncd ..\naea fetch fetchai/tac_participant_contract:0.22.5 --alias tac_participant_two\ncd tac_participant_two\naea install\naea build\n
Alternatively, create from scratch:

In a separate terminal, in the root directory, create at least two tac participant AEAs:

aea create tac_participant_one\naea create tac_participant_two\n

Build participant one:

cd tac_participant_one\naea add connection fetchai/p2p_libp2p:0.27.5\naea add connection fetchai/soef:0.27.6\naea add connection fetchai/ledger:0.21.5\naea add skill fetchai/tac_participation:0.25.6\naea add skill fetchai/tac_negotiation:0.29.6\naea config set --type dict agent.dependencies \\\n'{\n  \"aea-ledger-fetchai\": {\"version\": \"<2.0.0,>=1.0.0\"},\n  \"aea-ledger-ethereum\": {\"version\": \"<2.0.0,>=1.0.0\"}\n}'\naea config set agent.default_connection fetchai/p2p_libp2p:0.27.5\naea config set agent.default_ledger ethereum\naea config set vendor.fetchai.connections.soef.config.chain_identifier ethereum\naea config set vendor.fetchai.skills.tac_participation.models.game.args.is_using_contract 'True' --type bool\naea config set vendor.fetchai.skills.tac_negotiation.models.strategy.args.is_contract_tx 'True' --type bool\naea config set --type dict agent.default_routing \\\n'{\n  \"fetchai/contract_api:1.1.7\": \"fetchai/ledger:0.21.5\",\n  \"fetchai/ledger_api:1.1.7\": \"fetchai/ledger:0.21.5\",\n  \"fetchai/oef_search:1.1.7\": \"fetchai/soef:0.27.6\"\n}'\naea config set --type dict agent.decision_maker_handler \\\n'{\n  \"dotted_path\": \"aea.decision_maker.gop:DecisionMakerHandler\",\n  \"file_path\": null\n}'\naea config set --type list vendor.fetchai.connections.p2p_libp2p.cert_requests \\\n'''[{\"identifier\": \"acn\", \"ledger_id\": \"ethereum\", \"message_format\": \"'{public_key}'\", \"not_after\": \"2023-01-01\", \"not_before\": \"2022-01-01\", \"public_key\": \"fetchai\", \"save_path\": \".certs/conn_cert.txt\"}]'''\naea install\naea build\n

Then, build participant two:

cd tac_participant_two\naea add connection fetchai/p2p_libp2p:0.27.5\naea add connection fetchai/soef:0.27.6\naea add connection fetchai/ledger:0.21.5\naea add skill fetchai/tac_participation:0.25.6\naea add skill fetchai/tac_negotiation:0.29.6\naea config set --type dict agent.dependencies \\\n'{\n  \"aea-ledger-fetchai\": {\"version\": \"<2.0.0,>=1.0.0\"},\n  \"aea-ledger-ethereum\": {\"version\": \"<2.0.0,>=1.0.0\"}\n}'\naea config set agent.default_connection fetchai/p2p_libp2p:0.27.5\naea config set agent.default_ledger ethereum\naea config set vendor.fetchai.connections.soef.config.chain_identifier ethereum\naea config set vendor.fetchai.skills.tac_participation.models.game.args.is_using_contract 'True' --type bool\naea config set vendor.fetchai.skills.tac_negotiation.models.strategy.args.is_contract_tx 'True' --type bool\naea config set --type dict agent.default_routing \\\n'{\n  \"fetchai/contract_api:1.1.7\": \"fetchai/ledger:0.21.5\",\n  \"fetchai/ledger_api:1.1.7\": \"fetchai/ledger:0.21.5\",\n  \"fetchai/oef_search:1.1.7\": \"fetchai/soef:0.27.6\"\n}'\naea config set --type dict agent.decision_maker_handler \\\n'{\n  \"dotted_path\": \"aea.decision_maker.gop:DecisionMakerHandler\",\n  \"file_path\": null\n}'\naea config set --type list vendor.fetchai.connections.p2p_libp2p.cert_requests \\\n'''[{\"identifier\": \"acn\", \"ledger_id\": \"ethereum\", \"message_format\": \"'{public_key}'\", \"not_after\": \"2023-01-01\", \"not_before\": \"2022-01-01\", \"public_key\": \"fetchai\", \"save_path\": \".certs/conn_cert.txt\"}]'''\naea install\naea build\n
"},{"location":"aea-framework-documentation/tac-skills-contract/#configure-the-agents-to-use-ethereum","title":"Configure the Agents to Use Ethereum","text":"

Run the following in every AEA's terminal:

aea config set agent.default_ledger ethereum\njson=$(printf '[{\"identifier\": \"acn\", \"ledger_id\": \"ethereum\", \"not_after\": \"2023-01-01\", \"not_before\": \"2022-01-01\", \"public_key\": \"fetchai\", \"message_format\": \"{public_key}\", \"save_path\": \".certs/conn_cert.txt\"}]')\naea config set --type list vendor.fetchai.connections.p2p_libp2p.cert_requests \"$json\"\naea config set vendor.fetchai.connections.soef.config.chain_identifier ethereum\n
"},{"location":"aea-framework-documentation/tac-skills-contract/#add-keys-for-all-aeas_1","title":"Add Keys for All AEAs","text":"

For every AEA in the competition (controller and participants):

First generate and add a private key:

aea generate-key ethereum\naea add-key ethereum ethereum_private_key.txt\n

Then create and add a separate private key for secure communication:

aea generate-key fetchai fetchai_connection_private_key.txt\naea add-key fetchai fetchai_connection_private_key.txt --connection\n

Finally, certify the key for use by the connections that request that:

aea issue-certificates\n
"},{"location":"aea-framework-documentation/tac-skills-contract/#update-the-game-parameters-in-the-controller_1","title":"Update the Game Parameters in the Controller","text":"

In the tac controller project, get and set the registration start time (set it to at least 5 minutes in the future):

aea config get vendor.fetchai.skills.tac_control_contract.models.parameters.args.registration_start_time\naea config set vendor.fetchai.skills.tac_control_contract.models.parameters.args.registration_start_time '01 01 2020  00:01'\n

To set the registration time, you may find handy the following command:

aea config set vendor.fetchai.skills.tac_control_contract.models.parameters.args.registration_start_time \"$(date -d \"5 minutes\" +'%d %m %Y %H:%M')\"\n
"},{"location":"aea-framework-documentation/tac-skills-contract/#update-the-connection-parameters_1","title":"Update the Connection Parameters","text":"

Update the connection parameters of the TAC participants to allow them to connect to the same local agent communication network as the TAC controller.

First, retrieve controller's local ACN address by running the following in the controller agent's project terminal:

aea get-multiaddress fetchai -c -i fetchai/p2p_libp2p:0.27.5 -u public_uri\n

Then, in participant one, run this command (replace SOME_ADDRESS with the value you retrieved above):

aea config set --type dict vendor.fetchai.connections.p2p_libp2p.config \\\n'{\n  \"delegate_uri\": \"127.0.0.1:11001\",\n  \"entry_peers\": [\"SOME_ADDRESS\"],\n  \"local_uri\": \"127.0.0.1:9001\",\n  \"log_file\": \"libp2p_node.log\",\n  \"public_uri\": \"127.0.0.1:9001\"\n}'\n

Do the same in participant two (beware of the different port numbers):

aea config set --type dict vendor.fetchai.connections.p2p_libp2p.config \\\n'{\n  \"delegate_uri\": \"127.0.0.1:11002\",\n  \"entry_peers\": [\"SOME_ADDRESS\"],\n  \"local_uri\": \"127.0.0.1:9002\",\n  \"log_file\": \"libp2p_node.log\",\n  \"public_uri\": \"127.0.0.1:9002\"\n}'\n
"},{"location":"aea-framework-documentation/tac-skills-contract/#fund-agents-accounts_1","title":"Fund Agents' Accounts","text":"

Run a local Ganache Ethereum test-net with funds for the addresses of the three AEAs in this demo:

docker run -p 8545:8545 trufflesuite/ganache-cli:latest --verbose --gasPrice=0 --gasLimit=0x1fffffffffffff --account=\"$(cat tac_controller_contract/ethereum_private_key.txt),1000000000000000000000\" --account=\"$(cat tac_participant_one/ethereum_private_key.txt),1000000000000000000000\" --account=\"$(cat tac_participant_two/ethereum_private_key.txt),1000000000000000000000\"\n

To check the wealth of an AEA, use:

aea get-wealth ethereum\n

You should get 1000000000000000000000.

"},{"location":"aea-framework-documentation/tac-skills-contract/#run-the-aeas_1","title":"Run the AEAs","text":"

First, launch the tac_contract_controller then the participants by executing the following from their respective terminals:

aea run\n

The CLI tool supports launching several agents at once. For example, assuming you followed the tutorial, you can launch both TAC participant agents as follows from the root directory (ensure you run the controller agent first as above):

aea launch tac_participant_one tac_participant_two\n

You may want to try --multithreaded option in order to run the agents in the same process.

"},{"location":"aea-framework-documentation/tac-skills-contract/#cleaning-up_1","title":"Cleaning up","text":"

When you're finished, delete your AEAs:

aea delete tac_controller_contract\naea delete tac_participant_one\naea delete tac_participant_two\n
"},{"location":"aea-framework-documentation/tac-skills/","title":"TAC Skills","text":"

The AEA TAC - trading agent competition - skills demonstrate an interaction between multiple AEAs in a game.

There are two types of AEAs:

  • The tac_controller which coordinates the game.
  • The tac_participant AEAs which compete in the game. The tac_participant AEAs trade tokens with each other to maximize their utility.
"},{"location":"aea-framework-documentation/tac-skills/#discussion","title":"Discussion","text":"

The scope of this specific demo is to demonstrate how the agents negotiate autonomously with each other while they pursue their goals by playing a game of TAC. Another AEA has the role of the controller, responsible for calculating the revenue for each participant and checking if the transaction messages are valid. Transactions are settled with the controller agent rather than against a public ledger.

"},{"location":"aea-framework-documentation/tac-skills/#communication","title":"Communication","text":"

There are two types of interactions:

  • between the participants and the controller, the game communication
  • between the participants, the negotiation
"},{"location":"aea-framework-documentation/tac-skills/#registration-communication","title":"Registration Communication","text":"

This diagram shows the communication between the various entities during the registration phase.

    sequenceDiagram\n        participant Agent_2\n        participant Agent_1\n        participant Search\n        participant Controller\n\n        activate Search\n        activate Controller\n\n        Controller->>Search: register_service\n        activate Agent_1\n        Agent_1->>Search: search\n        Search-->>Agent_1: controller\n        Agent_1->>Controller: register\n        activate Agent_2\n        Agent_2->>Search: search\n        Search-->>Agent_2: controller\n        Agent_2->>Controller: register\n        Controller->>Controller: start_game\n        Controller->>Agent_1: game_data\n        Controller->>Agent_2: game_data\n\n        deactivate Agent_1\n        deactivate Agent_2\n        deactivate Search\n        deactivate Controller
"},{"location":"aea-framework-documentation/tac-skills/#transaction-communication","title":"Transaction Communication","text":"

This diagram shows the communication between two AEAs and the controller. In this case, we have an AEA in the role of the seller, referred to as Seller_Agent. We also have an AEA in the role of the buyer, referred to as Buyer_Agent. During a given TAC, an AEA can be in both roles simultaneously in different bilateral interactions.

    sequenceDiagram\n        participant Buyer_Agent\n        participant Seller_Agent\n        participant Search\n        participant Controller\n\n        activate Buyer_Agent\n        activate Seller_Agent\n        activate Search\n        activate Controller\n\n        Seller_Agent->>Search: register_service\n        Buyer_Agent->>Search: search\n        Search-->>Buyer_Agent: list_of_agents\n        Buyer_Agent->>Seller_Agent: call_for_proposal\n        Seller_Agent->>Buyer_Agent: proposal\n        Buyer_Agent->>Seller_Agent: accept\n        Seller_Agent->>Buyer_Agent: match_accept\n        Seller_Agent->>Controller: transaction\n        Controller->>Controller: transaction_execution\n        Controller->>Seller_Agent: confirm_transaction\n        Controller->>Buyer_Agent: confirm_transaction\n\n        deactivate Buyer_Agent\n        deactivate Seller_Agent\n        deactivate Search\n        deactivate Controller

In the above case, the proposal received contains a set of good which the seller wishes to sell and a cost of them. The buyer AEA needs to determine if this is a good deal for them and if so, it accepts.

There is an equivalent diagram for seller AEAs set up to search for buyers and their interaction with AEAs which are registered as buyers. In that scenario, the proposal will instead, be a list of goods that the buyer wishes to buy and the price it is willing to pay for them.

"},{"location":"aea-framework-documentation/tac-skills/#option-1-aea-manager-approach","title":"Option 1: AEA Manager Approach","text":"

Follow this approach when using the AEA Manager Desktop app. Otherwise, skip and follow the CLI approach below.

"},{"location":"aea-framework-documentation/tac-skills/#preparation-instructions","title":"Preparation Instructions","text":"

Install the AEA Manager.

"},{"location":"aea-framework-documentation/tac-skills/#demo-instructions","title":"Demo Instructions","text":"

The following steps assume you have launched the AEA Manager Desktop app.

  1. Add a new AEA called controller with public id fetchai/tac_controller:0.26.0.

  2. Add another new AEA called participant_1 with public id fetchai/tac_participant:0.28.0.

  3. Add another new AEA called participant_2 with public id fetchai/tac_participant:0.28.0.

  4. Navigate to the settings of controller and under components > skill > fetchai/fetchai/tac_controller:0.22.0 > models > parameters > args update registration_start_time to the time you want TAC to begin (e.g. 2 minutes in the future)

  5. Run the controller AEA. Navigate to its logs and copy the multiaddress displayed. Stop the controller.

  6. Navigate to the settings of participant_1 and under components > connection > fetchai/p2p_libp2p:0.22.0 update as follows (make sure to replace the placeholder with the multiaddress):

    {\n\"delegate_uri\": \"127.0.0.1:11001\",\n  \"entry_peers\": [\"REPLACE_WITH_MULTI_ADDRESS_HERE\"],\n  \"local_uri\": \"127.0.0.1:9001\",\n  \"log_file\": \"libp2p_node.log\",\n  \"public_uri\": \"127.0.0.1:9001\"\n}\n
  7. Navigate to the settings of participant_2 and under components > connection > fetchai/p2p_libp2p:0.22.0 update as follows (make sure to replace the placeholder with the multiaddress):

    {\n\"delegate_uri\": \"127.0.0.1:11002\",\n  \"entry_peers\": [\"REPLACE_WITH_MULTI_ADDRESS_HERE\"],\n  \"local_uri\": \"127.0.0.1:9002\",\n  \"log_file\": \"libp2p_node.log\",\n  \"public_uri\": \"127.0.0.1:9002\"\n}\n
  8. You may add more participants by repeating steps 3 (with an updated name) and 6 (bumping the port numbers. See the difference between steps 5 and 6).

  9. Run the controller, then participant_1 and participant_2 (and any other participants you added).

In the controller's log, you should see the details of the transactions participants submit as well as changes in their scores and holdings. In participants' logs, you should see the agents trading.

"},{"location":"aea-framework-documentation/tac-skills/#option-2-cli-approach","title":"Option 2: CLI Approach","text":"

Follow this approach when using the aea CLI.

"},{"location":"aea-framework-documentation/tac-skills/#preparation-instructions_1","title":"Preparation Instructions","text":""},{"location":"aea-framework-documentation/tac-skills/#dependencies","title":"Dependencies","text":"

Follow the Preliminaries and Installation sections from the AEA quick start.

"},{"location":"aea-framework-documentation/tac-skills/#demo-instructions_1","title":"Demo Instructions","text":""},{"location":"aea-framework-documentation/tac-skills/#create-tac-controller-aea","title":"Create TAC Controller AEA","text":"

In the root directory, fetch the controller AEA:

aea fetch fetchai/tac_controller:0.30.5\ncd tac_controller\naea install\naea build\n
Alternatively, create from scratch:

The following steps create the controller from scratch:

aea create tac_controller\ncd tac_controller\naea add connection fetchai/p2p_libp2p:0.27.5\naea add connection fetchai/soef:0.27.6\naea add connection fetchai/ledger:0.21.5\naea add skill fetchai/tac_control:0.25.6\naea config set --type dict agent.dependencies \\\n'{\n  \"aea-ledger-fetchai\": {\"version\": \"<2.0.0,>=1.0.0\"}\n}'\naea config set agent.default_connection fetchai/p2p_libp2p:0.27.5\naea config set agent.default_ledger fetchai\naea config set --type dict agent.default_routing \\\n'{\n  \"fetchai/oef_search:1.1.7\": \"fetchai/soef:0.27.6\"\n}'\naea install\naea build\n
"},{"location":"aea-framework-documentation/tac-skills/#create-the-tac-participant-aeas","title":"Create the TAC Participant AEAs","text":"

In a separate terminal, in the root directory, fetch at least two participants:

aea fetch fetchai/tac_participant:0.32.5 --alias tac_participant_one\ncd tac_participant_one\naea install\naea build\ncd ..\naea fetch fetchai/tac_participant:0.32.5 --alias tac_participant_two\ncd tac_participant_two\naea build\n
Alternatively, create from scratch:

In a separate terminal, in the root directory, create at least two tac participant AEAs:

aea create tac_participant_one\naea create tac_participant_two\n

Build participant one:

cd tac_participant_one\naea add connection fetchai/p2p_libp2p:0.27.5\naea add connection fetchai/soef:0.27.6\naea add connection fetchai/ledger:0.21.5\naea add skill fetchai/tac_participation:0.25.6\naea add skill fetchai/tac_negotiation:0.29.6\naea config set --type dict agent.dependencies \\\n'{\n  \"aea-ledger-fetchai\": {\"version\": \"<2.0.0,>=1.0.0\"}\n}'\naea config set agent.default_connection fetchai/p2p_libp2p:0.27.5\naea config set agent.default_ledger fetchai\naea config set --type dict agent.default_routing \\\n'{\n  \"fetchai/ledger_api:1.1.7\": \"fetchai/ledger:0.21.5\",\n  \"fetchai/oef_search:1.1.7\": \"fetchai/soef:0.27.6\"\n}'\naea config set --type dict agent.decision_maker_handler \\\n'{\n  \"dotted_path\": \"aea.decision_maker.gop:DecisionMakerHandler\",\n  \"file_path\": null\n}'\naea install\naea build\n

Then, build participant two:

cd tac_participant_two\naea add connection fetchai/p2p_libp2p:0.27.5\naea add connection fetchai/soef:0.27.6\naea add connection fetchai/ledger:0.21.5\naea add skill fetchai/tac_participation:0.25.6\naea add skill fetchai/tac_negotiation:0.29.6\naea config set --type dict agent.dependencies \\\n'{\n  \"aea-ledger-fetchai\": {\"version\": \"<2.0.0,>=1.0.0\"}\n}'\naea config set agent.default_connection fetchai/p2p_libp2p:0.27.5\naea config set agent.default_ledger fetchai\naea config set --type dict agent.default_routing \\\n'{\n  \"fetchai/ledger_api:1.1.7\": \"fetchai/ledger:0.21.5\",\n  \"fetchai/oef_search:1.1.7\": \"fetchai/soef:0.27.6\"\n}'\naea config set --type dict agent.decision_maker_handler \\\n'{\n  \"dotted_path\": \"aea.decision_maker.gop:DecisionMakerHandler\",\n  \"file_path\": null\n}'\naea install\naea build\n
"},{"location":"aea-framework-documentation/tac-skills/#add-keys-for-all-aeas","title":"Add Keys for All AEAs","text":"

Create the private key for the AEA for Fetch.ai Dorado:

aea generate-key fetchai\naea add-key fetchai fetchai_private_key.txt\n

Next, create a private key used to secure the AEA's communications:

aea generate-key fetchai fetchai_connection_private_key.txt\naea add-key fetchai fetchai_connection_private_key.txt --connection\n

Finally, certify the key for use by the connections that request that:

aea issue-certificates\n
"},{"location":"aea-framework-documentation/tac-skills/#update-the-game-parameters-in-the-controller","title":"Update the Game Parameters in the Controller","text":"

Navigate to the tac controller project, then use the command line to get and set the start time (set it to at least two minutes in the future):

aea config get vendor.fetchai.skills.tac_control.models.parameters.args.registration_start_time\naea config set vendor.fetchai.skills.tac_control.models.parameters.args.registration_start_time '01 01 2020  00:01'\n

To set the registration time, you may find handy the following command:

aea config set vendor.fetchai.skills.tac_control.models.parameters.args.registration_start_time \"$(date -d \"2 minutes\" +'%d %m %Y %H:%M')\"\n
"},{"location":"aea-framework-documentation/tac-skills/#update-the-connection-parameters","title":"Update the Connection Parameters","text":"

Briefly run the controller AEA:

aea run\n

Once you see a message of the form To join its network use multiaddr 'SOME_ADDRESS' take note of the address. (Alternatively, use aea get-multiaddress fetchai -c -i fetchai/p2p_libp2p:0.27.5 -u public_uri to retrieve the address.)

Then, in the participant one, run this command (replace SOME_ADDRESS with the correct value as described above):

aea config set --type dict vendor.fetchai.connections.p2p_libp2p.config \\\n'{\n  \"delegate_uri\": \"127.0.0.1:11001\",\n  \"entry_peers\": [\"SOME_ADDRESS\"],\n  \"local_uri\": \"127.0.0.1:9001\",\n  \"log_file\": \"libp2p_node.log\",\n  \"public_uri\": \"127.0.0.1:9001\"\n}'\n

Do the same in participant two (beware of the different port numbers):

aea config set --type dict vendor.fetchai.connections.p2p_libp2p.config \\\n'{\n  \"delegate_uri\": \"127.0.0.1:11002\",\n  \"entry_peers\": [\"SOME_ADDRESS\"],\n  \"local_uri\": \"127.0.0.1:9002\",\n  \"log_file\": \"libp2p_node.log\",\n  \"public_uri\": \"127.0.0.1:9002\"\n}'\n

This allows the TAC participants to connect to the same local agent communication network as the TAC controller.

"},{"location":"aea-framework-documentation/tac-skills/#run-the-aeas","title":"Run the AEAs","text":"

First, launch the tac_controller:

aea run\n

The CLI tool supports the launch of several agents at once.

For example, assuming you followed the tutorial, you can launch both the TAC agents as follows from the root directory:

aea launch tac_participant_one tac_participant_two\n

You may want to try --multithreaded option in order to run the agents in the same process.

"},{"location":"aea-framework-documentation/tac-skills/#cleaning-up","title":"Cleaning up","text":"

When you're finished, delete your AEAs:

aea delete tac_controller\naea delete tac_participant_one\naea delete tac_participant_two\n
"},{"location":"aea-framework-documentation/tac/","title":"TAC External App","text":"

Note

This app is no longer maintained.

The original TAC has its own repo.

Follow the instructions below to build and run the TAC demo.

"},{"location":"aea-framework-documentation/tac/#requirements","title":"Requirements","text":"

Make sure you are running Docker and Docker Compose.

"},{"location":"aea-framework-documentation/tac/#quick-start","title":"Quick Start","text":"

Clone the repo to include submodules.

git clone git@github.com:fetchai/agents-tac.git --recursive && cd agents-tac\n

Check you have pipenv.

which pipenv\n

If you don't have it, install it. Instructions are here.

Create and launch a virtual environment.

pipenv --python 3.7 && pipenv shell\n

Install the dependencies.

pipenv install\n

Install the package.

python setup.py install\n

Run the launch script. This may take a while.

python scripts/launch.py\n

The Visdom server is now running.

The controller GUI at http://localhost:8097 provides real time insights.

In the Environment tab, make sure you have the tac_controller environment selected.

"},{"location":"aea-framework-documentation/tac/#alternative-build-and-run","title":"Alternative Build and Run","text":"

In a new terminal window, clone the repo, build the sandbox, and launch it.

git clone git@github.com:fetchai/agents-tac.git --recursive && cd agents-tac\npipenv --python 3.7 && pipenv shell\npython setup.py install\ncd sandbox && docker-compose build\ndocker-compose up\n

In a new terminal window, enter the virtual environment, and connect a template agent to the sandbox.

pipenv shell\npython templates/v1/basic.py --name my_agent --dashboard\n

Click through to the controller GUI.

"},{"location":"aea-framework-documentation/tac/#possible-gotchas","title":"Possible Gotchas","text":"

Stop all running containers before restart.

docker stop $(docker ps -q)\n

To remove all images, run the following command.

# mac\ndocker ps -q | xargs docker stop ; docker system prune -a\n
"},{"location":"aea-framework-documentation/thermometer-skills/","title":"Thermometer Skills","text":"

The AEA thermometer skills demonstrate an interaction between two AEAs, one purchasing temperature data from the other.

  • The provider of thermometer data (the thermometer).
  • The buyer of thermometer data (the thermometer_client).
"},{"location":"aea-framework-documentation/thermometer-skills/#discussion","title":"Discussion","text":"

This demo aims to demonstrate how to create a very simple AEA with the usage of the AEA framework and a thermometer sensor. The thermometer AEA will read data from the sensor each time a client requests the data and will deliver it to the client upon payment. To keep the demo simple, we avoided the usage of a database since this would increase the complexity. As a result, the AEA can provide only one reading from the sensor. This demo does not utilise a smart contract. As a result, the ledger interaction is only for completing a transaction.

"},{"location":"aea-framework-documentation/thermometer-skills/#communication","title":"Communication","text":"

This diagram shows the communication between the various entities as data is successfully sold by the thermometer AEA to the client AEA.

    sequenceDiagram\n        participant Search\n        participant Client_AEA\n        participant Thermometer_AEA\n        participant Blockchain\n\n        activate Client_AEA\n        activate Search\n        activate Thermometer_AEA\n        activate Blockchain\n\n        Thermometer_AEA->>Search: register_service\n        Client_AEA->>Search: search\n        Search-->>Client_AEA: list_of_agents\n        Client_AEA->>Thermometer_AEA: call_for_proposal\n        Thermometer_AEA->>Client_AEA: propose\n        Client_AEA->>Thermometer_AEA: accept\n        Thermometer_AEA->>Client_AEA: match_accept\n        Client_AEA->>Blockchain: transfer_funds\n        Client_AEA->>Thermometer_AEA: send_transaction_hash\n        Thermometer_AEA->>Blockchain: check_transaction_status\n        Thermometer_AEA->>Client_AEA: send_data\n\n        deactivate Client_AEA\n        deactivate Search\n        deactivate Thermometer_AEA\n        deactivate Blockchain
"},{"location":"aea-framework-documentation/thermometer-skills/#option-1-aea-manager-approach","title":"Option 1: AEA Manager Approach","text":"

Follow this approach when using the AEA Manager Desktop app. Otherwise, skip and follow the CLI approach below.

"},{"location":"aea-framework-documentation/thermometer-skills/#preparation-instructions","title":"Preparation Instructions","text":"

Install the AEA Manager.

"},{"location":"aea-framework-documentation/thermometer-skills/#demo-instructions","title":"Demo Instructions","text":"

The following steps assume you have launched the AEA Manager Desktop app.

  1. Add a new AEA called my_thermometer_aea with public id fetchai/thermometer_aea:0.30.5.

  2. Add another new AEA called my_thermometer_client with public id fetchai/thermometer_client:0.32.5.

  3. Copy the address from the my_thermometer_client into your clip board. Then go to the Dorado block explorer and request some test tokens via Get Funds.

  4. Run the my_thermometer_aea AEA. Navigate to its logs and copy the multiaddress displayed.

  5. Navigate to the settings of the my_thermometer_client and under components > connection > fetchai/p2p_libp2p:0.22.0 update as follows (make sure to replace the placeholder with the multiaddress):

    {\n\"delegate_uri\": \"127.0.0.1:11001\",\n  \"entry_peers\": [\"REPLACE_WITH_MULTI_ADDRESS_HERE\"],\n  \"local_uri\": \"127.0.0.1:9001\",\n  \"log_file\": \"libp2p_node.log\",\n  \"public_uri\": \"127.0.0.1:9001\"\n}\n
  6. Run the my_thermometer_client.

In the AEA's logs, you should see the agent trading successfully.

"},{"location":"aea-framework-documentation/thermometer-skills/#option-2-cli-approach","title":"Option 2: CLI Approach","text":"

Follow this approach when using the aea CLI.

"},{"location":"aea-framework-documentation/thermometer-skills/#preparation-instructions_1","title":"Preparation Instructions","text":""},{"location":"aea-framework-documentation/thermometer-skills/#dependencies","title":"Dependencies","text":"

Follow the Preliminaries and Installation sections from the AEA quick start.

"},{"location":"aea-framework-documentation/thermometer-skills/#demo-instructions_1","title":"Demo Instructions","text":"

A demo to run the thermometer scenario with a true ledger transaction This demo assumes the buyer trusts the seller AEA to send the data upon successful payment.

"},{"location":"aea-framework-documentation/thermometer-skills/#create-thermometer-aea","title":"Create Thermometer AEA","text":"

First, fetch the thermometer AEA:

aea fetch fetchai/thermometer_aea:0.30.5 --alias my_thermometer_aea\ncd my_thermometer_aea\naea install\naea build\n
Alternatively, create from scratch:

The following steps create the thermometer AEA from scratch:

aea create my_thermometer_aea\ncd my_thermometer_aea\naea add connection fetchai/p2p_libp2p:0.27.5\naea add connection fetchai/soef:0.27.6\naea add connection fetchai/ledger:0.21.5\naea add skill fetchai/thermometer:0.27.6\naea install\naea build\naea config set agent.default_connection fetchai/p2p_libp2p:0.27.5\naea config set --type dict agent.default_routing \\\n'{\n  \"fetchai/ledger_api:1.1.7\": \"fetchai/ledger:0.21.5\",\n  \"fetchai/oef_search:1.1.7\": \"fetchai/soef:0.27.6\"\n}'\n
"},{"location":"aea-framework-documentation/thermometer-skills/#create-thermometer-client","title":"Create Thermometer Client","text":"

Then, fetch the thermometer client AEA:

aea fetch fetchai/thermometer_client:0.32.5 --alias my_thermometer_client\ncd my_thermometer_client\naea install\naea build\n
Alternatively, create from scratch:

The following steps create the thermometer client from scratch:

aea create my_thermometer_client\ncd my_thermometer_client\naea add connection fetchai/p2p_libp2p:0.27.5\naea add connection fetchai/soef:0.27.6\naea add connection fetchai/ledger:0.21.5\naea add skill fetchai/thermometer_client:0.26.6\naea install\naea build\naea config set agent.default_connection fetchai/p2p_libp2p:0.27.5\naea config set --type dict agent.default_routing \\\n'{\n  \"fetchai/ledger_api:1.1.7\": \"fetchai/ledger:0.21.5\",\n  \"fetchai/oef_search:1.1.7\": \"fetchai/soef:0.27.6\"\n}'\n
"},{"location":"aea-framework-documentation/thermometer-skills/#add-keys-for-the-thermometer-aea","title":"Add Keys for the Thermometer AEA","text":"

First, create the private key for the thermometer AEA based on the network you want to transact. To generate and add a private-public key pair for Fetch.ai Dorado use:

aea generate-key fetchai\naea add-key fetchai fetchai_private_key.txt\n

Next, create a private key used to secure the AEA's communications:

aea generate-key fetchai fetchai_connection_private_key.txt\naea add-key fetchai fetchai_connection_private_key.txt --connection\n

Finally, certify the key for use by the connections that request that:

aea issue-certificates\n
"},{"location":"aea-framework-documentation/thermometer-skills/#add-keys-and-generate-wealth-for-the-thermometer-client-aea","title":"Add Keys and Generate Wealth for the Thermometer Client AEA","text":"

The thermometer client needs to have some wealth to purchase the thermometer information.

First, create the private key for the thermometer client AEA based on the network you want to transact. To generate and add a private-public key pair for Fetch.ai use:

aea generate-key fetchai\naea add-key fetchai fetchai_private_key.txt\n

Then, create some wealth for your thermometer client based on the network you want to transact with. On the Fetch.ai testnet network:

aea generate-wealth fetchai\n

Next, create a private key used to secure the AEA's communications:

aea generate-key fetchai fetchai_connection_private_key.txt\naea add-key fetchai fetchai_connection_private_key.txt --connection\n

Finally, certify the key for use by the connections that request that:

aea issue-certificates\n
"},{"location":"aea-framework-documentation/thermometer-skills/#run-both-aeas","title":"Run both AEAs","text":"

Run both AEAs from their respective terminals.

First, run the thermometer AEA:

aea run\n

Once you see a message of the form To join its network use multiaddr 'SOME_ADDRESS' take note of the address. (Alternatively, use aea get-multiaddress fetchai -c -i fetchai/p2p_libp2p:0.27.5 -u public_uri to retrieve the address.) This is the entry peer address for the local agent communication network created by the thermometer AEA.

Then, in the thermometer client, run this command (replace SOME_ADDRESS with the correct value as described above):

aea config set --type dict vendor.fetchai.connections.p2p_libp2p.config \\\n'{\n  \"delegate_uri\": \"127.0.0.1:11001\",\n  \"entry_peers\": [\"SOME_ADDRESS\"],\n  \"local_uri\": \"127.0.0.1:9001\",\n  \"log_file\": \"libp2p_node.log\",\n  \"public_uri\": \"127.0.0.1:9001\"\n}'\n

This allows the thermometer client to connect to the same local agent communication network as the thermometer AEA.

Then run the thermometer client AEA:

aea run\n

You can see that the AEAs find each other, negotiate and eventually trade.

"},{"location":"aea-framework-documentation/thermometer-skills/#cleaning-up","title":"Cleaning up","text":"

When you're done, go up a level and delete the AEAs.

cd ..\naea delete my_thermometer_aea\naea delete my_thermometer_client\n
"},{"location":"aea-framework-documentation/trust/","title":"Trust Minimisation","text":"

AEA applications have different requirements for trustlessness or trust minimisation.

For example, using the AEA weather skills demo without ledger payments means that the client has to trust the weather station to send the weather data it purchased and that this data is in fact valid. Similarly, the weather station must trust that the client somehow sends the payment amount to which they agreed.

A step-up, if you run the weather skills demo with a ledger (e.g. Fetch.ai or Ethereum) then the client must still trust that the weather station sends valid data. However, all payment transactions are executed via the public ledger. This means the weather station no longer needs to trust the client for payment and can verify whether the transactions take place on the public ledger.

We can further minimise trust requirements by incorporating a third party as an arbitrator or escrow implemented in a smart contract to further reduce trust requirements. However, in the current weather skills demo, there are limits to trustlessness as the station ultimately offers unverifiable data.

Another example of minimising trust, is applications with (non-fungible) token transactions involving atomic swaps where trustlessness is clearly satisfied (e.g. in the TAC demo).

"},{"location":"aea-framework-documentation/upgrading/","title":"Upgrading","text":"

This page provides some tips on how to upgrade AEA projects between different versions of the AEA framework. For full release notes check the AEA repo.

The primary tool for upgrading AEA projects is the aea upgrade command in the CLI.

Below we describe the additional manual steps required to upgrade between different versions:

"},{"location":"aea-framework-documentation/upgrading/#v122-to-v123","title":"v1.2.2 to v1.2.3","text":"

Ensure you update the plugins to their latest version (all plugins are changed in this release)

Update the packages to the latest versions (especially protocols).

Update development environment

"},{"location":"aea-framework-documentation/upgrading/#v120-to-v122","title":"v1.2.0 to v1.2.2","text":"

Ensure you update the plugins to their latest version (all plugins are changed in this release)

Update the packages to the latest versions (especially protocols).

Regenerate your own written protocols (protocol generator was updated)

"},{"location":"aea-framework-documentation/upgrading/#v111-to-v120","title":"v1.1.1 to v1.2.0","text":"

Ensure you update the plugins to their latest version (fetchai and cosmos plugins are changed in this release)

Update the packages to the latest versions (especially p2p_libp2p related packages are updated)

Check packages\u2019 and agents\u2019 configurations are correct (e.g. the fetchai test-net name is changed for the Dorado network)

"},{"location":"aea-framework-documentation/upgrading/#v110-to-v111","title":"v1.1.0 to v1.1.1","text":"

No backwards incompatible changes.

Upgrade fetchai/p2p_libp2p connection package to the latest version which fixes a slow DHT lookup problem

We advise everyone to upgrade their fetchai packages and plugins to get the latest fixes.

"},{"location":"aea-framework-documentation/upgrading/#v102-to-v110","title":"v1.0.2 to v1.1.0","text":"

No backwards incompatible changes.

We advise everyone to upgrade their fetchai packages and plugins to get the latest fixes.

"},{"location":"aea-framework-documentation/upgrading/#v101-to-v102","title":"v1.0.1 to v1.0.2","text":"

No backwards incompatible changes.

We advise everyone to upgrade their fetchai packages and plugins to get the latest fixes.

"},{"location":"aea-framework-documentation/upgrading/#v100-to-v101","title":"v1.0.0 to v1.0.1","text":"

No backwards incompatible changes.

We advise everyone to upgrade their fetchai packages to get the latest fixes.

"},{"location":"aea-framework-documentation/upgrading/#v100rc2-to-v100","title":"v1.0.0rc2 to v1.0.0","text":"

No backwards incompatible changes to component development.

We advise everyone to upgrade to v1 as soon as possible. When upgrading from versions below v1.0.0rc1 first upgrade to the first release candidate, then to v1.

"},{"location":"aea-framework-documentation/upgrading/#v100rc1-to-v100rc2","title":"v1.0.0rc1 to v1.0.0rc2","text":"

No backwards incompatible changes to component development.

Various configuration changes introduced in v1.0.0rc1 are now enforced strictly.

"},{"location":"aea-framework-documentation/upgrading/#v0111-to-v100rc1","title":"v0.11.1 to v1.0.0rc1","text":"

No backwards incompatible changes to component development.

The aea-config.yaml now requires the field required_ledgers which must specify all ledgers for which private keys are required to run the agent. Please add it to your project.

The registry_path field has been removed from the aea-config.yaml. Please remove it from your project.

All packages provided by author fetchai must be upgraded.

"},{"location":"aea-framework-documentation/upgrading/#v0110-to-v0111","title":"v0.11.0 to v0.11.1","text":"

No backwards incompatible changes.

"},{"location":"aea-framework-documentation/upgrading/#v0101-to-v0110","title":"v0.10.1 to v0.11.0","text":"

Take special care when upgrading to v0.11.0. We introduced several breaking changes in preparation for v1!

"},{"location":"aea-framework-documentation/upgrading/#cli-gui","title":"CLI GUI","text":"

We removed the CLI GUI. It was not used by anyone as far as we know and needs to be significantly improved. Soon we will release the AEA Manager App to make up for this.

"},{"location":"aea-framework-documentation/upgrading/#message-routing","title":"Message Routing","text":"

Routing has been completely revised and simplified. The new message routing logic is described here.

When upgrading take the following steps:

  • For agent-to-agent communication: ensure the default routing and default connection are correctly defined and that the dialogues used specify the agent's address as the self_address. This is most likely already the case. Only in some edge cases will you need to use an EnvelopeContext to target a connection different from the one specified in the default_routing map.

  • For component-to-component communication: there is now only one single way to route component to component (skill to skill, skill to connection, connection to skill) messages, this is by specifying the component id in string form in the sender/to field. The EnvelopeContext can no longer be used, messages are routed based on their target (to field). Ensure that dialogues in skills set the skill_id as the self_address (in connections they need to set the connection_id).

"},{"location":"aea-framework-documentation/upgrading/#agent-configuration-and-ledger-plugins","title":"Agent Configuration and Ledger Plugins","text":"

Agent configuration files have a new optional field, dependencies, analogous to dependencies field in other AEA packages. The default value is the empty object {}. The field will be made mandatory in the next release.

Crypto modules have been extracted and released as independent plug-ins, released on PyPI. In particular:

  • Fetch.ai crypto classes have been released in the aea-ledger-fetchai package;
  • Ethereum crypto classes have been released in the aea-ledger-ethereum package;
  • Cosmos crypto classes have been released in the aea-ledger-cosmos package.

If an AEA project, or an AEA package, makes use of crypto functionalities, it will be needed to add the above packages as PyPI dependencies with version specifiers ranging from the latest minor and the latest minor + 1 (excluded). E.g. if the latest version if 0.1.0, the version specifier should be <0.2.0,>=0.1.0:

dependencies:\naea-ledger-cosmos:\nversion: <2.0.0,>=1.0.0\naea-ledger-ethereum:\nversion: <2.0.0,>=1.0.0\naea-ledger-fetchai:\nversion: <2.0.0,>=1.0.0\n

The version specifier sets are important, as these plug-ins, at version 0.1.0, depend on a specific range of the aea package.

Then, running aea install inside the AEA project should install them in the current Python environment.

For more, read the guide on ledger plugins.

"},{"location":"aea-framework-documentation/upgrading/#v0100-to-v0101","title":"v0.10.0 to v0.10.1","text":"

No backwards incompatible changes for skill and connection development.

"},{"location":"aea-framework-documentation/upgrading/#v092-to-v0100","title":"v0.9.2 to v0.10.0","text":"

Skill development sees no backward incompatible changes.

Connection development requires updating the keyword arguments of the constructor: the new data_dir argument must be defined.

Protocol specifications now need to contain a protocol_specification_id in addition to the public id. The protocol_specification_id is used for identifying Envelopes during transport. By being able to set the id independently of the protocol id, backwards compatibility in the specification (and therefore wire format) can be maintained even when the Python implementation changes.

Please update to the latest packages by running aea upgrade and then re-generating your own protocols.

"},{"location":"aea-framework-documentation/upgrading/#v091-to-v092","title":"v0.9.1 to v0.9.2","text":"

No backwards incompatible changes for skill and connection development.

"},{"location":"aea-framework-documentation/upgrading/#v090-to-v091","title":"v0.9.0 to v0.9.1","text":"

No backwards incompatible changes for skill and connection development.

"},{"location":"aea-framework-documentation/upgrading/#v080-to-v090","title":"v0.8.0 to v0.9.0","text":"

This release introduces proof of representation in the ACN. You will need to upgrade to the latest fetchai/p2p_libp2p or fetchai/p2p_libp2p_client connection and then use two key pairs, one for your AEA's decision maker and one for the connection.

Please update to the latest packages by running aea upgrade.

"},{"location":"aea-framework-documentation/upgrading/#v075-to-v080","title":"v0.7.5 to v0.8.0","text":"

Minimal backwards incompatible changes for skill and connection development:

  • The semantics of the <, <=, > and >= relations in ConstraintTypes are simplified.
  • Protocols now need to correctly define terminal states. Regenerate your protocol to identify if your protocol's dialogue rules are valid.

Please update to the latest packages by running aea upgrade.

"},{"location":"aea-framework-documentation/upgrading/#v074-to-v075","title":"v0.7.4 to v0.7.5","text":"

No backwards incompatible changes for skill and connection development.

"},{"location":"aea-framework-documentation/upgrading/#v073-to-v074","title":"v0.7.3 to v0.7.4","text":"

No backwards incompatible changes for skill and connection development.

"},{"location":"aea-framework-documentation/upgrading/#v072-to-v073","title":"v0.7.2 to v0.7.3","text":"

No backwards incompatible changes for skill and connection development.

"},{"location":"aea-framework-documentation/upgrading/#v071-to-v072","title":"v0.7.1 to v0.7.2","text":"

No backwards incompatible changes for skill and connection development.

"},{"location":"aea-framework-documentation/upgrading/#v070-to-v071","title":"v0.7.0 to v0.7.1","text":"

To improve performance, in particular optimize memory usage, we refactored the Message and Dialogue classes. This means all protocols need to be bumped to the latest version or regenerated using the aea generate protocol command in the CLI.

"},{"location":"aea-framework-documentation/upgrading/#v063-to-v070","title":"v0.6.3 to v0.7.0","text":"

Multiple breaking changes require action in this order:

  • Custom configuration overrides in aea-config.yaml are now identified via public_id rather than author, name and version individually. Please replace the three fields with the equivalent public_id.

  • Run aea upgrade command to upgrade your project's dependencies. Note, you still do have to manually update the public ids under default_routing and default_connection in aea-config.yaml as well as the public ids in the non-vendor packages.

  • Previously, connection fetchai/stub, skill fetchai/error and protocols fetchai/default, fetchai/signing and fetchai/state_update where part of the AEA distribution. Now they need to be fetched from registry. If you create a new project with aea create then this happens automatically. For existing projects, add the dependencies explicitly if not already present. You also must update the import paths as follows:

    • aea.connections.stub > packages.fetchai.connections.stub
    • aea.protocols.default > packages.fetchai.protocols.default
    • aea.protocols.signing > packages.fetchai.protocols.signing
    • aea.protocols.state_update > packages.fetchai.protocols.state_update
    • aea.skills.error > packages.fetchai.skills.error
  • If you use custom protocols, regenerate them.

  • In your own skills' __init__.py files add the public id (updating the string as appropriate):

from aea.configurations.base import PublicId\nPUBLIC_ID = PublicId.from_str(\"author/name:0.1.0\")\n
  • The fetchai/http protocol's bodyy field has been renamed to body.

  • Skills can now specify connections as dependencies in the configuration YAML.

"},{"location":"aea-framework-documentation/upgrading/#v062-to-v063","title":"v0.6.2 to v0.6.3","text":"

A new upgrade command is introduced to upgrade agent projects and components to their latest versions on the registry. To use the command first upgrade the AEA PyPI package to the latest version, then enter your project and run aea upgrade. The project's vendor dependencies will be updated where possible.

"},{"location":"aea-framework-documentation/upgrading/#v061-to-v062","title":"v0.6.1 to v0.6.2","text":"

No public APIs have been changed.

"},{"location":"aea-framework-documentation/upgrading/#v060-to-v061","title":"v0.6.0 to v0.6.1","text":"

The soef connection and oef_search protocol have backward incompatible changes.

"},{"location":"aea-framework-documentation/upgrading/#v054-to-v060","title":"v0.5.4 to v0.6.0","text":""},{"location":"aea-framework-documentation/upgrading/#dialogue-and-dialogues-api-updates","title":"Dialogue and Dialogues API Updates","text":"

The dialogue and dialogues APIs have changed significantly. The constructor is different for both classes and there are now four primary methods for the developer:

  • Dialogues.create: this method is used to create a new dialogue and message:
cfp_msg, fipa_dialogue = fipa_dialogues.create(\ncounterparty=opponent_address,\nperformative=FipaMessage.Performative.CFP,\nquery=query,\n)\n

The method will raise if the provided arguments are inconsistent.

  • Dialogues.create_with_message: this method is used to create a new dialogue from a message:
fipa_dialogue = fipa_dialogues.create_with_message(\ncounterparty=opponent_address,\ninitial_message=cfp_msg\n)\n

The method will raise if the provided arguments are inconsistent.

  • Dialogues.update: this method is used to handle messages passed by the framework:
fipa_dialogue = fipa_dialogues.update(\nmessage=cfp_msg\n)\n

The method will return a valid dialogue if it is a valid message, otherwise it will return None.

  • Dialogue.reply: this method is used to reply within a dialogue:
proposal_msg = fipa_dialogue.reply(\nperformative=FipaMessage.Performative.PROPOSE,\ntarget_message=cfp_msg,\nproposal=proposal,\n)\n

The method will raise if the provided arguments are inconsistent.

The new methods significantly reduce the lines of code needed to maintain a dialogue. They also make it easier for the developer to construct valid dialogues and messages.

"},{"location":"aea-framework-documentation/upgrading/#fetchaicrypto-default-crypto","title":"FetchAICrypto - Default Crypto","text":"

The FetchAICrypto has been upgraded to the default crypto. Update your default_ledger to fetchai.

"},{"location":"aea-framework-documentation/upgrading/#private-key-file-naming","title":"Private Key File Naming","text":"

The private key files are now consistently named with the ledger_id followed by _private_key.txt (e.g. fetchai_private_key.txt). Rename your existing files to match this pattern.

"},{"location":"aea-framework-documentation/upgrading/#type-in-package-yaml","title":"Type in Package YAML","text":"

The package YAML files now contain a type field. This must be added for the loading mechanism to work properly.

"},{"location":"aea-framework-documentation/upgrading/#moved-address-type","title":"Moved Address Type","text":"

The address type has moved to aea.common. The import paths must be updated.

"},{"location":"aea-framework-documentation/upgrading/#v053-to-v054","title":"v0.5.3 to v0.5.4","text":"

The contract base class was slightly modified. If you have implemented your own contract package you need to update it accordingly.

The dialogue reference nonce is now randomly generated. This can result in previously working but buggy implementations (which relied on the order of dialogue reference nonces) to now fail.

"},{"location":"aea-framework-documentation/upgrading/#v052-to-v053","title":"v0.5.2 to v0.5.3","text":"

Connection states and logger usage in connections where updated. If you have implemented your own connection package you need to update it accordingly.

Additional dialogue consistency checks where enabled. This can result in previously working but buggy implementations to now fail.

"},{"location":"aea-framework-documentation/upgrading/#v051-to-052","title":"v0.5.1 to 0.5.2","text":"

No public APIs have been changed.

"},{"location":"aea-framework-documentation/upgrading/#v050-to-051","title":"v0.5.0 to 0.5.1","text":"

No public APIs have been changed.

"},{"location":"aea-framework-documentation/upgrading/#v041-to-050","title":"v0.4.1 to 0.5.0","text":"

A number of breaking changes where introduced which make backwards compatibility of skills rare.

  • Ledger APIs LedgerApis have been removed from the AEA constructor and skill context. LedgerApis are now exposed in the LedgerConnection (fetchai/ledger). To communicate with the LedgerApis use the fetchai/ledger_api protocol. This allows for more flexibility (anyone can add another LedgerAPI to the registry and execute it with the connection) and removes dependencies from the core framework.
  • Skills can now depend on other skills. As a result, skills have a new required configuration field in skill.yaml files, by default empty: skills: [].
"},{"location":"aea-framework-documentation/upgrading/#v040-to-v041","title":"v0.4.0 to v0.4.1","text":"

There are no upgrade requirements if you use the CLI based approach to AEA development.

Connections are now added via Resources to the AEA, not the AEA constructor directly. For programmatic usage remove the list of connections from the AEA constructor and instead add the connections to resources.

"},{"location":"aea-framework-documentation/upgrading/#v033-to-v040","title":"v0.3.3 to v0.4.0","text":"
  • Message sending in the skills has been updated. In the past you had to construct messages, then serialize them and place them in an envelope:

    cfp_msg = FipaMessage(...)\nself.context.outbox.put_message(\nto=opponent_addr,\nsender=self.context.agent_address,\nprotocol_id=FipaMessage.protocol_id,\nmessage=FipaSerializer().encode(cfp_msg),\n)\n# or\ncfp_msg = FipaMessage(...)\nenvelope = Envelope(\nto=opponent_addr,\nsender=self.context.agent_address,\nprotocol_id=FipaMessage.protocol_id,\nmessage=FipaSerializer().encode(cfp_msg),\n)\nself.context.outbox.put(envelope)\n

    Now this has been simplified to:

    cfp_msg = FipaMessage(...)\ncfp_msg.counterparty = opponent_addr\nself.context.outbox.put_message(message=cfp_msg)\n

    You must update your skills as the old implementation is no longer supported.

  • Connection constructors have been simplified. In the past you had to implement both the __init__ as well as the from_config methods of a Connection. Now you only have to implement the __init__ method which by default at load time now receives the following keyword arguments: configuration: ConnectionConfig, identity: Identity, crypto_store: CryptoStore. See for example in the scaffold connection:

    class MyScaffoldConnection(Connection):\n\"\"\"Proxy to the functionality of the SDK or API.\"\"\"\nconnection_id = PublicId.from_str(\"fetchai/scaffold:0.1.0\")\ndef __init__(\nself,\nconfiguration: ConnectionConfig,\nidentity: Identity,\ncrypto_store: CryptoStore,\n):\n\"\"\"\n        Initialize a connection to an SDK or API.\n        :param configuration: the connection configuration.\n        :param crypto_store: object to access the connection crypto objects.\n        :param identity: the identity object.\n        \"\"\"\nsuper().__init__(\nconfiguration=configuration, crypto_store=crypto_store, identity=identity\n)\n

    As a result of this feature, you are now able to pass key-pairs to your connections via the CryptoStore.

    You must update your connections as the old implementation is no longer supported.

"},{"location":"aea-framework-documentation/wealth/","title":"Generating Wealth","text":"

To fund an AEA for testing on a test-net you need to request some test tokens from a faucet.

First, make sure you have installed the crypto plugin of the target test-net. E.g. for Fetch.AI:

pip install aea-ledger-fetchai\n

And for Ethereum:

pip install aea-ledger-ethereum\n

Add a private key to the agent

aea generate-key fetchai\naea add-key fetchai fetchai_private_key.txt\n

or

aea generate-key ethereum\naea add-key ethereum ethereum_private_key.txt\n

Note

If you already have keys in your project, the commands prompt you to confirm whether to replace the existing keys.

"},{"location":"aea-framework-documentation/wealth/#using-a-faucet-website","title":"Using a Faucet Website","text":"

First, print the address:

aea get-address fetchai\n

or

aea get-address ethereum\n

This will print the address to the console. Copy the address into the clipboard and request test tokens from the faucet here for Fetch.ai or here for Ethereum. It will take a while for the tokens to become available.

Second, after some time, check the wealth associated with the address:

aea get-wealth fetchai\n

or

aea get-wealth ethereum\n
"},{"location":"aea-framework-documentation/wealth/#using-the-cli","title":"Using the CLI","text":"

Simply generate wealth via the CLI:

aea generate-wealth fetchai\n

or

aea generate-wealth ethereum\n

Note

This approach can be unreliable for non-fetchai test nets.

"},{"location":"aea-framework-documentation/weather-skills/","title":"Weather Skills","text":"

The AEA weather skills demonstrate an interaction between two AEAs.

  • The provider of weather data (the weather_station).
  • The buyer of weather data (the weather_client).
"},{"location":"aea-framework-documentation/weather-skills/#discussion","title":"Discussion","text":"

The scope of the specific demo is to demonstrate how to create a simple AEA with the usage of the AEA framework and a database. The weather_station AEA will read data from the database, that is populated with readings from a weather station, based on the requested dates and will deliver the data to the client upon payment. This demo does not utilize a smart contract. As a result, we interact with a ledger only to complete a transaction.

You can use this AEA as an example of how to read data from a database and advertise these to possible clients.

"},{"location":"aea-framework-documentation/weather-skills/#communication","title":"Communication","text":"

This diagram shows the communication between the various entities as data is successfully sold by the weather station AEA to the client.

    sequenceDiagram\n        participant Search\n        participant Client_AEA\n        participant Weather_AEA\n        participant Blockchain\n\n        activate Client_AEA\n        activate Search\n        activate Weather_AEA\n        activate Blockchain\n\n        Weather_AEA->>Search: register_service\n        Client_AEA->>Search: search\n        Search-->>Client_AEA: list_of_agents\n        Client_AEA->>Weather_AEA: call_for_proposal\n        Weather_AEA->>Client_AEA: propose\n        Client_AEA->>Weather_AEA: accept\n        Weather_AEA->>Client_AEA: match_accept\n        Client_AEA->>Blockchain: transfer_funds\n        Client_AEA->>Weather_AEA: send_transaction_hash\n        Weather_AEA->>Blockchain: check_transaction_status\n        Weather_AEA->>Client_AEA: send_data\n\n        deactivate Client_AEA\n        deactivate Search\n        deactivate Weather_AEA\n        deactivate Blockchain  
"},{"location":"aea-framework-documentation/weather-skills/#option-1-aea-manager-approach","title":"Option 1: AEA Manager Approach","text":"

Follow this approach when using the AEA Manager Desktop app. Otherwise, skip and follow the CLI approach below.

"},{"location":"aea-framework-documentation/weather-skills/#preparation-instructions","title":"Preparation Instructions","text":"

Install the AEA Manager.

"},{"location":"aea-framework-documentation/weather-skills/#demo-instructions","title":"Demo Instructions","text":"

The following steps assume you have launched the AEA Manager Desktop app.

  1. Add a new AEA called my_weather_station with public id fetchai/weather_station:0.32.5.

  2. Add another new AEA called my_weather_client with public id fetchai/weather_client:0.33.5.

  3. Copy the address from the my_weather_client into your clip board. Then go to the Dorado block explorer and request some test tokens via Get Funds.

  4. Run the my_weather_station AEA. Navigate to its logs and copy the multiaddress displayed.

  5. Navigate to the settings of the my_weather_client and under components > connection > fetchai/p2p_libp2p:0.22.0 update as follows (make sure to replace the placeholder with the multiaddress):

    {\n\"delegate_uri\": \"127.0.0.1:11001\",\n  \"entry_peers\": [\"REPLACE_WITH_MULTI_ADDRESS_HERE\"],\n  \"local_uri\": \"127.0.0.1:9001\",\n  \"log_file\": \"libp2p_node.log\",\n  \"public_uri\": \"127.0.0.1:9001\"\n}\n
  6. Run the my_weather_client.

In the AEA's logs, you should see the agent trading successfully.

"},{"location":"aea-framework-documentation/weather-skills/#option-2-cli-approach","title":"Option 2: CLI Approach","text":"

Follow this approach when using the aea CLI.

"},{"location":"aea-framework-documentation/weather-skills/#preparation-instructions_1","title":"Preparation Instructions","text":""},{"location":"aea-framework-documentation/weather-skills/#dependencies","title":"Dependencies","text":"

Follow the Preliminaries and Installation sections from the AEA quick start.

"},{"location":"aea-framework-documentation/weather-skills/#demo-instructions_1","title":"Demo Instructions","text":"

A demo to run the same scenario but with a true ledger transaction on Fetch.ai testnet or Ethereum ropsten network. This demo assumes the buyer trusts the seller AEA to send the data upon successful payment.

"},{"location":"aea-framework-documentation/weather-skills/#create-the-weather-station","title":"Create the Weather Station","text":"

First, fetch the AEA that will provide weather measurements:

aea fetch fetchai/weather_station:0.32.5 --alias my_weather_station\ncd my_weather_station\naea install\naea build\n
Alternatively, create from scratch:

The following steps create the weather station from scratch:

aea create my_weather_station\ncd my_weather_station\naea add connection fetchai/p2p_libp2p:0.27.5\naea add connection fetchai/soef:0.27.6\naea add connection fetchai/ledger:0.21.5\naea add skill fetchai/weather_station:0.27.6\naea config set --type dict agent.dependencies \\\n'{\n  \"aea-ledger-fetchai\": {\"version\": \"<2.0.0,>=1.0.0\"}\n}'\naea config set agent.default_connection fetchai/p2p_libp2p:0.27.5\naea config set --type dict agent.default_routing \\\n'{\n  \"fetchai/ledger_api:1.1.7\": \"fetchai/ledger:0.21.5\",\n  \"fetchai/oef_search:1.1.7\": \"fetchai/soef:0.27.6\"\n}'\naea install\naea build\n
"},{"location":"aea-framework-documentation/weather-skills/#create-the-weather-client","title":"Create the Weather Client","text":"

In another terminal, fetch the AEA that will query the weather station:

aea fetch fetchai/weather_client:0.33.5 --alias my_weather_client\ncd my_weather_client\naea install\naea build\n
Alternatively, create from scratch:

The following steps create the weather client from scratch:

aea create my_weather_client\ncd my_weather_client\naea add connection fetchai/p2p_libp2p:0.27.5\naea add connection fetchai/soef:0.27.6\naea add connection fetchai/ledger:0.21.5\naea add skill fetchai/weather_client:0.26.6\naea config set --type dict agent.dependencies \\\n'{\n  \"aea-ledger-fetchai\": {\"version\": \"<2.0.0,>=1.0.0\"}\n}'\naea config set agent.default_connection fetchai/p2p_libp2p:0.27.5\naea config set --type dict agent.default_routing \\\n'{\n  \"fetchai/ledger_api:1.1.7\": \"fetchai/ledger:0.21.5\",\n  \"fetchai/oef_search:1.1.7\": \"fetchai/soef:0.27.6\"\n}'\naea install\naea build\n
"},{"location":"aea-framework-documentation/weather-skills/#add-keys-for-the-weather-station-aea","title":"Add Keys for the Weather Station AEA","text":"

First, create the private key for the weather station AEA based on the network you want to transact. To generate and add a private-public key pair for Fetch.ai Dorado use:

aea generate-key fetchai\naea add-key fetchai fetchai_private_key.txt\n

Next, create a private key used to secure the AEA's communications:

aea generate-key fetchai fetchai_connection_private_key.txt\naea add-key fetchai fetchai_connection_private_key.txt --connection\n

Finally, certify the key for use by the connections that request that:

aea issue-certificates\n
"},{"location":"aea-framework-documentation/weather-skills/#add-keys-and-generate-wealth-for-the-weather-client-aea","title":"Add Keys and Generate Wealth for the Weather Client AEA","text":"

The weather client needs to have some wealth to purchase the service from the weather station.

First, create the private key for the weather client AEA based on the network you want to transact. To generate and add a private-public key pair for Fetch.ai Dorado use:

aea generate-key fetchai\naea add-key fetchai fetchai_private_key.txt\n

Then, create some wealth for your weather client based on the network you want to transact with. On the Fetch.ai Dorado network:

aea generate-wealth fetchai\n

Next, create a private key used to secure the AEA's communications:

aea generate-key fetchai fetchai_connection_private_key.txt\naea add-key fetchai fetchai_connection_private_key.txt --connection\n

Finally, certify the key for use by the connections that request that:

aea issue-certificates\n
"},{"location":"aea-framework-documentation/weather-skills/#run-the-aeas","title":"Run the AEAs","text":"

Run both AEAs from their respective terminals.

First, run the weather station AEA:

aea run\n

Once you see a message of the form To join its network use multiaddr 'SOME_ADDRESS' take note of the address. (Alternatively, use aea get-multiaddress fetchai -c -i fetchai/p2p_libp2p:0.27.5 -u public_uri to retrieve the address.) This is the entry peer address for the local agent communication network created by the weather station.

Then, in the weather client, run this command (replace SOME_ADDRESS with the correct value as described above):

aea config set --type dict vendor.fetchai.connections.p2p_libp2p.config \\\n'{\n  \"delegate_uri\": \"127.0.0.1:11001\",\n  \"entry_peers\": [\"SOME_ADDRESS\"],\n  \"local_uri\": \"127.0.0.1:9001\",\n  \"log_file\": \"libp2p_node.log\",\n  \"public_uri\": \"127.0.0.1:9001\"\n}'\n

This allows the weather client to connect to the same local agent communication network as the weather station.

Then run the weather client AEA:

aea run\n

You will see that the AEAs negotiate and then transact using the selected ledger.

"},{"location":"aea-framework-documentation/weather-skills/#cleaning-up","title":"Cleaning up","text":"

When you're done, go up a level and delete the AEAs.

cd ..\naea delete my_weather_station\naea delete my_weather_client\n
"},{"location":"aea-framework-documentation/api/abstract_agent/","title":"AbstractAgent","text":""},{"location":"aea-framework-documentation/api/abstract_agent/#aeaabstract_agent","title":"aea.abstract_agent","text":"

This module contains the interface definition of the abstract agent.

"},{"location":"aea-framework-documentation/api/abstract_agent/#abstractagent-objects","title":"AbstractAgent Objects","text":"
class AbstractAgent(ABC)\n

This class provides an abstract base interface for an agent.

"},{"location":"aea-framework-documentation/api/abstract_agent/#name","title":"name","text":"
@property\n@abstractmethod\ndef name() -> str\n

Get agent's name.

"},{"location":"aea-framework-documentation/api/abstract_agent/#storage_uri","title":"storage_uri","text":"
@property\n@abstractmethod\ndef storage_uri() -> Optional[str]\n

Return storage uri.

"},{"location":"aea-framework-documentation/api/abstract_agent/#start","title":"start","text":"
@abstractmethod\ndef start() -> None\n

Start the agent.

Returns:

None

"},{"location":"aea-framework-documentation/api/abstract_agent/#stop","title":"stop","text":"
@abstractmethod\ndef stop() -> None\n

Stop the agent.

Returns:

None

"},{"location":"aea-framework-documentation/api/abstract_agent/#setup","title":"setup","text":"
@abstractmethod\ndef setup() -> None\n

Set up the agent.

Returns:

None

"},{"location":"aea-framework-documentation/api/abstract_agent/#act","title":"act","text":"
@abstractmethod\ndef act() -> None\n

Perform actions on period.

Returns:

None

"},{"location":"aea-framework-documentation/api/abstract_agent/#handle_envelope","title":"handle_envelope","text":"
@abstractmethod\ndef handle_envelope(envelope: Envelope) -> None\n

Handle an envelope.

Arguments:

  • envelope: the envelope to handle.

Returns:

None

"},{"location":"aea-framework-documentation/api/abstract_agent/#get_periodic_tasks","title":"get_periodic_tasks","text":"
@abstractmethod\ndef get_periodic_tasks(\n) -> Dict[Callable, Tuple[float, Optional[datetime.datetime]]]\n

Get all periodic tasks for agent.

Returns:

dict of callable with period specified

"},{"location":"aea-framework-documentation/api/abstract_agent/#get_message_handlers","title":"get_message_handlers","text":"
@abstractmethod\ndef get_message_handlers() -> List[Tuple[Callable[[Any], None], Callable]]\n

Get handlers with message getters.

Returns:

List of tuples of callables: handler and coroutine to get a message

"},{"location":"aea-framework-documentation/api/abstract_agent/#exception_handler","title":"exception_handler","text":"
@abstractmethod\ndef exception_handler(exception: Exception,\nfunction: Callable) -> Optional[bool]\n

Handle exception raised during agent main loop execution.

Arguments:

  • exception: exception raised
  • function: a callable exception raised in.

Returns:

skip exception if True, otherwise re-raise it

"},{"location":"aea-framework-documentation/api/abstract_agent/#teardown","title":"teardown","text":"
@abstractmethod\ndef teardown() -> None\n

Tear down the agent.

Returns:

None

"},{"location":"aea-framework-documentation/api/aea/","title":"AEA","text":""},{"location":"aea-framework-documentation/api/aea/#aeaaea","title":"aea.aea","text":"

This module contains the implementation of an autonomous economic agent (AEA).

"},{"location":"aea-framework-documentation/api/aea/#aea-objects","title":"AEA Objects","text":"
class AEA(Agent)\n

This class implements an autonomous economic agent.

"},{"location":"aea-framework-documentation/api/aea/#__init__","title":"__init__","text":"
def __init__(\nidentity: Identity,\nwallet: Wallet,\nresources: Resources,\ndata_dir: str,\nloop: Optional[AbstractEventLoop] = None,\nperiod: float = 0.05,\nexecution_timeout: float = 0,\nmax_reactions: int = 20,\nerror_handler_class: Optional[Type[AbstractErrorHandler]] = None,\nerror_handler_config: Optional[Dict[str, Any]] = None,\ndecision_maker_handler_class: Optional[\nType[DecisionMakerHandler]] = None,\ndecision_maker_handler_config: Optional[Dict[str, Any]] = None,\nskill_exception_policy: ExceptionPolicyEnum = ExceptionPolicyEnum.\npropagate,\nconnection_exception_policy: ExceptionPolicyEnum = ExceptionPolicyEnum.\npropagate,\nloop_mode: Optional[str] = None,\nruntime_mode: Optional[str] = None,\ndefault_ledger: Optional[str] = None,\ncurrency_denominations: Optional[Dict[str, str]] = None,\ndefault_connection: Optional[PublicId] = None,\ndefault_routing: Optional[Dict[PublicId, PublicId]] = None,\nconnection_ids: Optional[Collection[PublicId]] = None,\nsearch_service_address: str = DEFAULT_SEARCH_SERVICE_ADDRESS,\nstorage_uri: Optional[str] = None,\ntask_manager_mode: Optional[str] = None,\n**kwargs: Any) -> None\n

Instantiate the agent.

Arguments:

  • identity: the identity of the agent
  • wallet: the wallet of the agent.
  • resources: the resources (protocols and skills) of the agent.
  • data_dir: directory where to put local files.
  • loop: the event loop to run the connections.
  • period: period to call agent's act
  • execution_timeout: amount of time to limit single act/handle to execute.
  • max_reactions: the processing rate of envelopes per tick (i.e. single loop).
  • error_handler_class: the class implementing the error handler
  • error_handler_config: the configuration of the error handler
  • decision_maker_handler_class: the class implementing the decision maker handler to be used.
  • decision_maker_handler_config: the configuration of the decision maker handler
  • skill_exception_policy: the skill exception policy enum
  • connection_exception_policy: the connection exception policy enum
  • loop_mode: loop_mode to choose agent run loop.
  • runtime_mode: runtime mode (async, threaded) to run AEA in.
  • default_ledger: default ledger id
  • currency_denominations: mapping from ledger id to currency denomination
  • default_connection: public id to the default connection
  • default_routing: dictionary for default routing.
  • connection_ids: active connection ids. Default: consider all the ones in the resources.
  • search_service_address: the address of the search service used.
  • storage_uri: optional uri to set generic storage
  • task_manager_mode: task manager mode (threaded) to run tasks with.
  • kwargs: keyword arguments to be attached in the agent context namespace.

"},{"location":"aea-framework-documentation/api/aea/#get_build_dir","title":"get_build_dir","text":"
@classmethod\ndef get_build_dir(cls) -> str\n

Get agent build directory.

"},{"location":"aea-framework-documentation/api/aea/#context","title":"context","text":"
@property\ndef context() -> AgentContext\n

Get (agent) context.

"},{"location":"aea-framework-documentation/api/aea/#resources","title":"resources","text":"
@property\ndef resources() -> Resources\n

Get resources.

"},{"location":"aea-framework-documentation/api/aea/#resources_1","title":"resources","text":"
@resources.setter\ndef resources(resources: \"Resources\") -> None\n

Set resources.

"},{"location":"aea-framework-documentation/api/aea/#filter","title":"filter","text":"
@property\ndef filter() -> Filter\n

Get the filter.

"},{"location":"aea-framework-documentation/api/aea/#active_behaviours","title":"active_behaviours","text":"
@property\ndef active_behaviours() -> List[Behaviour]\n

Get all active behaviours to use in act.

"},{"location":"aea-framework-documentation/api/aea/#setup","title":"setup","text":"
def setup() -> None\n

Set up the agent.

Calls setup() on the resources.

"},{"location":"aea-framework-documentation/api/aea/#act","title":"act","text":"
def act() -> None\n

Perform actions.

Adds new handlers and behaviours for use/execution by the runtime.

"},{"location":"aea-framework-documentation/api/aea/#handle_envelope","title":"handle_envelope","text":"
def handle_envelope(envelope: Envelope) -> None\n

Handle an envelope.

Performs the following:

  • fetching the protocol referenced by the envelope, and
  • handling if the protocol is unsupported, using the error handler, or
  • handling if there is a decoding error, using the error handler, or
  • handling if no active handler is available for the specified protocol, using the error handler, or
  • handling the message recovered from the envelope with all active handlers for the specified protocol.

Arguments:

  • envelope: the envelope to handle.

Returns:

None

"},{"location":"aea-framework-documentation/api/aea/#get_periodic_tasks","title":"get_periodic_tasks","text":"
def get_periodic_tasks(\n) -> Dict[Callable, Tuple[float, Optional[datetime.datetime]]]\n

Get all periodic tasks for agent.

Returns:

dict of callable with period specified

"},{"location":"aea-framework-documentation/api/aea/#get_message_handlers","title":"get_message_handlers","text":"
def get_message_handlers() -> List[Tuple[Callable[[Any], None], Callable]]\n

Get handlers with message getters.

Returns:

List of tuples of callables: handler and coroutine to get a message

"},{"location":"aea-framework-documentation/api/aea/#exception_handler","title":"exception_handler","text":"
def exception_handler(exception: Exception, function: Callable) -> bool\n

Handle exception raised during agent main loop execution.

Arguments:

  • exception: exception raised
  • function: a callable exception raised in.

Returns:

bool, propagate exception if True otherwise skip it.

"},{"location":"aea-framework-documentation/api/aea/#teardown","title":"teardown","text":"
def teardown() -> None\n

Tear down the agent.

Performs the following:

  • tears down the resources.

"},{"location":"aea-framework-documentation/api/aea/#get_task_result","title":"get_task_result","text":"
def get_task_result(task_id: int) -> AsyncResult\n

Get the result from a task.

Arguments:

  • task_id: the id of the task

Returns:

async result for task_id

"},{"location":"aea-framework-documentation/api/aea/#enqueue_task","title":"enqueue_task","text":"
def enqueue_task(func: Callable,\nargs: Sequence = (),\nkwargs: Optional[Dict[str, Any]] = None) -> int\n

Enqueue a task with the task manager.

Arguments:

  • func: the callable instance to be enqueued
  • args: the positional arguments to be passed to the function.
  • kwargs: the keyword arguments to be passed to the function.

Returns:

the task id to get the the result.

"},{"location":"aea-framework-documentation/api/aea_builder/","title":"AEA Builder","text":""},{"location":"aea-framework-documentation/api/aea_builder/#aeaaea_builder","title":"aea.aea_builder","text":"

This module contains utilities for building an AEA.

"},{"location":"aea-framework-documentation/api/aea_builder/#_dependenciesmanager-objects","title":"_DependenciesManager Objects","text":"
class _DependenciesManager()\n

Class to manage dependencies of agent packages.

"},{"location":"aea-framework-documentation/api/aea_builder/#__init__","title":"__init__","text":"
def __init__() -> None\n

Initialize the dependency graph.

"},{"location":"aea-framework-documentation/api/aea_builder/#all_dependencies","title":"all_dependencies","text":"
@property\ndef all_dependencies() -> Set[ComponentId]\n

Get all dependencies.

"},{"location":"aea-framework-documentation/api/aea_builder/#dependencies_highest_version","title":"dependencies_highest_version","text":"
@property\ndef dependencies_highest_version() -> Set[ComponentId]\n

Get the dependencies with highest version.

"},{"location":"aea-framework-documentation/api/aea_builder/#get_components_by_type","title":"get_components_by_type","text":"
def get_components_by_type(\ncomponent_type: ComponentType\n) -> Dict[ComponentId, ComponentConfiguration]\n

Get the components by type.

"},{"location":"aea-framework-documentation/api/aea_builder/#protocols","title":"protocols","text":"
@property\ndef protocols() -> Dict[ComponentId, ProtocolConfig]\n

Get the protocols.

"},{"location":"aea-framework-documentation/api/aea_builder/#connections","title":"connections","text":"
@property\ndef connections() -> Dict[ComponentId, ConnectionConfig]\n

Get the connections.

"},{"location":"aea-framework-documentation/api/aea_builder/#skills","title":"skills","text":"
@property\ndef skills() -> Dict[ComponentId, SkillConfig]\n

Get the skills.

"},{"location":"aea-framework-documentation/api/aea_builder/#contracts","title":"contracts","text":"
@property\ndef contracts() -> Dict[ComponentId, ContractConfig]\n

Get the contracts.

"},{"location":"aea-framework-documentation/api/aea_builder/#add_component","title":"add_component","text":"
def add_component(configuration: ComponentConfiguration) -> None\n

Add a component to the dependency manager.

Arguments:

  • configuration: the component configuration to add.

"},{"location":"aea-framework-documentation/api/aea_builder/#remove_component","title":"remove_component","text":"
def remove_component(component_id: ComponentId) -> None\n

Remove a component.

Arguments:

  • component_id: the component id

Raises:

  • ValueError: if some component depends on this package.

"},{"location":"aea-framework-documentation/api/aea_builder/#pypi_dependencies","title":"pypi_dependencies","text":"
@property\ndef pypi_dependencies() -> Dependencies\n

Get all the PyPI dependencies.

We currently consider only dependency that have the default PyPI index url and that specify only the version field.

Returns:

the merged PyPI dependencies

"},{"location":"aea-framework-documentation/api/aea_builder/#install_dependencies","title":"install_dependencies","text":"
def install_dependencies() -> None\n

Install extra dependencies for components.

"},{"location":"aea-framework-documentation/api/aea_builder/#aeabuilder-objects","title":"AEABuilder Objects","text":"
class AEABuilder(WithLogger)\n

This class helps to build an AEA.

It follows the fluent interface. Every method of the builder returns the instance of the builder itself.

Note: the method 'build()' is guaranteed of being re-entrant with respect to the 'add_component(path)' method. That is, you can invoke the building method many times against the same builder instance, and the returned agent instance will not share the components with other agents, e.g.:

builder = AEABuilder() builder.add_component(...) ...

"},{"location":"aea-framework-documentation/api/aea_builder/#first-call","title":"first call","text":"

my_aea_1 = builder.build()

"},{"location":"aea-framework-documentation/api/aea_builder/#following-agents-will-have-different-components","title":"following agents will have different components.","text":"

my_aea_2 = builder.build() # all good

However, if you manually loaded some of the components and added them with the method 'add_component_instance()', then calling build more than one time is prevented:

builder = AEABuilder() builder.add_component_instance(...) ... # other initialization code

"},{"location":"aea-framework-documentation/api/aea_builder/#first-call_1","title":"first call","text":"

my_aea_1 = builder.build()

"},{"location":"aea-framework-documentation/api/aea_builder/#second-call-to-build-would-raise-a-value-error","title":"second call to build() would raise a Value Error.","text":""},{"location":"aea-framework-documentation/api/aea_builder/#call-reset","title":"call reset","text":"

builder.reset()

"},{"location":"aea-framework-documentation/api/aea_builder/#re-add-the-component-and-private-keys","title":"re-add the component and private keys","text":"

builder.add_component_instance(...) ... # add private keys

"},{"location":"aea-framework-documentation/api/aea_builder/#second-call","title":"second call","text":"

my_aea_2 = builder.builder()

"},{"location":"aea-framework-documentation/api/aea_builder/#__init___1","title":"__init__","text":"
def __init__(with_default_packages: bool = True,\nregistry_dir: str = DEFAULT_REGISTRY_NAME,\nbuild_dir_root: Optional[str] = None) -> None\n

Initialize the builder.

Arguments:

  • with_default_packages: add the default packages.
  • registry_dir: the registry directory.
  • build_dir_root: the root of the build directory.

"},{"location":"aea-framework-documentation/api/aea_builder/#reset","title":"reset","text":"
def reset(is_full_reset: bool = False) -> None\n

Reset the builder.

A full reset causes a reset of all data on the builder. A partial reset only resets: - name, - private keys, and - component instances

Arguments:

  • is_full_reset: whether it is a full reset or not.

"},{"location":"aea-framework-documentation/api/aea_builder/#set_period","title":"set_period","text":"
def set_period(period: Optional[float]) -> \"AEABuilder\"\n

Set agent act period.

Arguments:

  • period: period in seconds

Returns:

self

"},{"location":"aea-framework-documentation/api/aea_builder/#set_execution_timeout","title":"set_execution_timeout","text":"
def set_execution_timeout(execution_timeout: Optional[float]) -> \"AEABuilder\"\n

Set agent execution timeout in seconds.

Arguments:

  • execution_timeout: execution_timeout in seconds

Returns:

self

"},{"location":"aea-framework-documentation/api/aea_builder/#set_max_reactions","title":"set_max_reactions","text":"
def set_max_reactions(max_reactions: Optional[int]) -> \"AEABuilder\"\n

Set agent max reaction in one react.

Arguments:

  • max_reactions: int

Returns:

self

"},{"location":"aea-framework-documentation/api/aea_builder/#set_decision_maker_handler_details","title":"set_decision_maker_handler_details","text":"
def set_decision_maker_handler_details(decision_maker_handler_dotted_path: str,\nfile_path: str,\nconfig: Dict[str, Any]) -> \"AEABuilder\"\n

Set error handler details.

Arguments:

  • decision_maker_handler_dotted_path: the dotted path to the decision maker handler
  • file_path: the file path to the file which contains the decision maker handler
  • config: the configuration passed to the decision maker handler on instantiation

Returns:

self

"},{"location":"aea-framework-documentation/api/aea_builder/#set_error_handler_details","title":"set_error_handler_details","text":"
def set_error_handler_details(error_handler_dotted_path: str, file_path: str,\nconfig: Dict[str, Any]) -> \"AEABuilder\"\n

Set error handler details.

Arguments:

  • error_handler_dotted_path: the dotted path to the error handler
  • file_path: the file path to the file which contains the error handler
  • config: the configuration passed to the error handler on instantiation

Returns:

self

"},{"location":"aea-framework-documentation/api/aea_builder/#set_skill_exception_policy","title":"set_skill_exception_policy","text":"
def set_skill_exception_policy(\nskill_exception_policy: Optional[ExceptionPolicyEnum]) -> \"AEABuilder\"\n

Set skill exception policy.

Arguments:

  • skill_exception_policy: the policy

Returns:

self

"},{"location":"aea-framework-documentation/api/aea_builder/#set_connection_exception_policy","title":"set_connection_exception_policy","text":"
def set_connection_exception_policy(\nconnection_exception_policy: Optional[ExceptionPolicyEnum]\n) -> \"AEABuilder\"\n

Set connection exception policy.

Arguments:

  • connection_exception_policy: the policy

Returns:

self

"},{"location":"aea-framework-documentation/api/aea_builder/#set_default_routing","title":"set_default_routing","text":"
def set_default_routing(\ndefault_routing: Dict[PublicId, PublicId]) -> \"AEABuilder\"\n

Set default routing.

This is a map from public ids (protocols) to public ids (connections).

Arguments:

  • default_routing: the default routing mapping

Returns:

self

"},{"location":"aea-framework-documentation/api/aea_builder/#set_loop_mode","title":"set_loop_mode","text":"
def set_loop_mode(loop_mode: Optional[str]) -> \"AEABuilder\"\n

Set the loop mode.

Arguments:

  • loop_mode: the agent loop mode

Returns:

self

"},{"location":"aea-framework-documentation/api/aea_builder/#set_runtime_mode","title":"set_runtime_mode","text":"
def set_runtime_mode(runtime_mode: Optional[str]) -> \"AEABuilder\"\n

Set the runtime mode.

Arguments:

  • runtime_mode: the agent runtime mode

Returns:

self

"},{"location":"aea-framework-documentation/api/aea_builder/#set_task_manager_mode","title":"set_task_manager_mode","text":"
def set_task_manager_mode(task_manager_mode: Optional[str]) -> \"AEABuilder\"\n

Set the task_manager_mode.

Arguments:

  • task_manager_mode: the agent task_manager_mode

Returns:

self

"},{"location":"aea-framework-documentation/api/aea_builder/#set_storage_uri","title":"set_storage_uri","text":"
def set_storage_uri(storage_uri: Optional[str]) -> \"AEABuilder\"\n

Set the storage uri.

Arguments:

  • storage_uri: storage uri

Returns:

self

"},{"location":"aea-framework-documentation/api/aea_builder/#set_data_dir","title":"set_data_dir","text":"
def set_data_dir(data_dir: Optional[str]) -> \"AEABuilder\"\n

Set the data directory.

Arguments:

  • data_dir: path to directory where to store data.

Returns:

self

"},{"location":"aea-framework-documentation/api/aea_builder/#set_logging_config","title":"set_logging_config","text":"
def set_logging_config(logging_config: Dict) -> \"AEABuilder\"\n

Set the logging configurations.

The dictionary must satisfy the following schema:

https://docs.python.org/3/library/logging.config.html#logging-config-dictschema

Arguments:

  • logging_config: the logging configurations.

Returns:

self

"},{"location":"aea-framework-documentation/api/aea_builder/#set_search_service_address","title":"set_search_service_address","text":"
def set_search_service_address(search_service_address: str) -> \"AEABuilder\"\n

Set the search service address.

Arguments:

  • search_service_address: the search service address

Returns:

self

"},{"location":"aea-framework-documentation/api/aea_builder/#set_name","title":"set_name","text":"
def set_name(name: str) -> \"AEABuilder\"\n

Set the name of the agent.

Arguments:

  • name: the name of the agent.

Returns:

the AEABuilder

"},{"location":"aea-framework-documentation/api/aea_builder/#set_default_connection","title":"set_default_connection","text":"
def set_default_connection(\npublic_id: Optional[PublicId] = None) -> \"AEABuilder\"\n

Set the default connection.

Arguments:

  • public_id: the public id of the default connection package.

Returns:

the AEABuilder

"},{"location":"aea-framework-documentation/api/aea_builder/#add_private_key","title":"add_private_key","text":"
def add_private_key(identifier: str,\nprivate_key_path: Optional[PathLike] = None,\nis_connection: bool = False) -> \"AEABuilder\"\n

Add a private key path.

Arguments:

  • identifier: the identifier for that private key path.
  • private_key_path: an (optional) path to the private key file. If None, the key will be created at build time.
  • is_connection: if the pair is for the connection cryptos

Returns:

the AEABuilder

"},{"location":"aea-framework-documentation/api/aea_builder/#remove_private_key","title":"remove_private_key","text":"
def remove_private_key(identifier: str,\nis_connection: bool = False) -> \"AEABuilder\"\n

Remove a private key path by identifier, if present.

Arguments:

  • identifier: the identifier of the private key.
  • is_connection: if the pair is for the connection cryptos

Returns:

the AEABuilder

"},{"location":"aea-framework-documentation/api/aea_builder/#private_key_paths","title":"private_key_paths","text":"
@property\ndef private_key_paths() -> Dict[str, Optional[str]]\n

Get the private key paths.

"},{"location":"aea-framework-documentation/api/aea_builder/#connection_private_key_paths","title":"connection_private_key_paths","text":"
@property\ndef connection_private_key_paths() -> Dict[str, Optional[str]]\n

Get the connection private key paths.

"},{"location":"aea-framework-documentation/api/aea_builder/#set_default_ledger","title":"set_default_ledger","text":"
def set_default_ledger(identifier: Optional[str]) -> \"AEABuilder\"\n

Set a default ledger API to use.

Arguments:

  • identifier: the identifier of the ledger api

Returns:

the AEABuilder

"},{"location":"aea-framework-documentation/api/aea_builder/#set_required_ledgers","title":"set_required_ledgers","text":"
def set_required_ledgers(\nrequired_ledgers: Optional[List[str]]) -> \"AEABuilder\"\n

Set the required ledger identifiers.

These are the ledgers for which the AEA requires a key pair.

Arguments:

  • required_ledgers: the required ledgers.

Returns:

the AEABuilder.

"},{"location":"aea-framework-documentation/api/aea_builder/#set_build_entrypoint","title":"set_build_entrypoint","text":"
def set_build_entrypoint(build_entrypoint: Optional[str]) -> \"AEABuilder\"\n

Set build entrypoint.

Arguments:

  • build_entrypoint: path to the builder script.

Returns:

the AEABuilder

"},{"location":"aea-framework-documentation/api/aea_builder/#set_currency_denominations","title":"set_currency_denominations","text":"
def set_currency_denominations(\ncurrency_denominations: Dict[str, str]) -> \"AEABuilder\"\n

Set the mapping from ledger ids to currency denominations.

Arguments:

  • currency_denominations: the mapping

Returns:

the AEABuilder

"},{"location":"aea-framework-documentation/api/aea_builder/#add_component_1","title":"add_component","text":"
def add_component(component_type: ComponentType,\ndirectory: PathLike,\nskip_consistency_check: bool = False) -> \"AEABuilder\"\n

Add a component, given its type and the directory.

Arguments:

  • component_type: the component type.
  • directory: the directory path.
  • skip_consistency_check: if True, the consistency check are skipped.

Raises:

  • AEAException: if a component is already registered with the same component id. # noqa: DAR402 | or if there's a missing dependency. # noqa: DAR402

Returns:

the AEABuilder

"},{"location":"aea-framework-documentation/api/aea_builder/#add_component_instance","title":"add_component_instance","text":"
def add_component_instance(component: Component) -> \"AEABuilder\"\n

Add already initialized component object to resources or connections.

Please, pay attention, all dependencies have to be already loaded.

Notice also that this will make the call to 'build()' non re-entrant. You will have to reset() the builder before calling build() again.

Arguments:

  • component: Component instance already initialized.

Returns:

self

"},{"location":"aea-framework-documentation/api/aea_builder/#set_context_namespace","title":"set_context_namespace","text":"
def set_context_namespace(context_namespace: Dict[str, Any]) -> \"AEABuilder\"\n

Set the context namespace.

"},{"location":"aea-framework-documentation/api/aea_builder/#set_agent_pypi_dependencies","title":"set_agent_pypi_dependencies","text":"
def set_agent_pypi_dependencies(dependencies: Dependencies) -> \"AEABuilder\"\n

Set agent PyPI dependencies.

Arguments:

  • dependencies: PyPI dependencies for the agent.

Returns:

the AEABuilder.

"},{"location":"aea-framework-documentation/api/aea_builder/#remove_component_1","title":"remove_component","text":"
def remove_component(component_id: ComponentId) -> \"AEABuilder\"\n

Remove a component.

Arguments:

  • component_id: the public id of the component.

Returns:

the AEABuilder

"},{"location":"aea-framework-documentation/api/aea_builder/#add_protocol","title":"add_protocol","text":"
def add_protocol(directory: PathLike) -> \"AEABuilder\"\n

Add a protocol to the agent.

Arguments:

  • directory: the path to the protocol directory

Returns:

the AEABuilder

"},{"location":"aea-framework-documentation/api/aea_builder/#remove_protocol","title":"remove_protocol","text":"
def remove_protocol(public_id: PublicId) -> \"AEABuilder\"\n

Remove protocol.

Arguments:

  • public_id: the public id of the protocol

Returns:

the AEABuilder

"},{"location":"aea-framework-documentation/api/aea_builder/#add_connection","title":"add_connection","text":"
def add_connection(directory: PathLike) -> \"AEABuilder\"\n

Add a connection to the agent.

Arguments:

  • directory: the path to the connection directory

Returns:

the AEABuilder

"},{"location":"aea-framework-documentation/api/aea_builder/#remove_connection","title":"remove_connection","text":"
def remove_connection(public_id: PublicId) -> \"AEABuilder\"\n

Remove a connection.

Arguments:

  • public_id: the public id of the connection

Returns:

the AEABuilder

"},{"location":"aea-framework-documentation/api/aea_builder/#add_skill","title":"add_skill","text":"
def add_skill(directory: PathLike) -> \"AEABuilder\"\n

Add a skill to the agent.

Arguments:

  • directory: the path to the skill directory

Returns:

the AEABuilder

"},{"location":"aea-framework-documentation/api/aea_builder/#remove_skill","title":"remove_skill","text":"
def remove_skill(public_id: PublicId) -> \"AEABuilder\"\n

Remove protocol.

Arguments:

  • public_id: the public id of the skill

Returns:

the AEABuilder

"},{"location":"aea-framework-documentation/api/aea_builder/#add_contract","title":"add_contract","text":"
def add_contract(directory: PathLike) -> \"AEABuilder\"\n

Add a contract to the agent.

Arguments:

  • directory: the path to the contract directory

Returns:

the AEABuilder

"},{"location":"aea-framework-documentation/api/aea_builder/#remove_contract","title":"remove_contract","text":"
def remove_contract(public_id: PublicId) -> \"AEABuilder\"\n

Remove protocol.

Arguments:

  • public_id: the public id of the contract

Returns:

the AEABuilder

"},{"location":"aea-framework-documentation/api/aea_builder/#call_all_build_entrypoints","title":"call_all_build_entrypoints","text":"
def call_all_build_entrypoints() -> None\n

Call all the build entrypoints.

"},{"location":"aea-framework-documentation/api/aea_builder/#get_build_root_directory","title":"get_build_root_directory","text":"
def get_build_root_directory() -> str\n

Get build directory root.

"},{"location":"aea-framework-documentation/api/aea_builder/#run_build_for_component_configuration","title":"run_build_for_component_configuration","text":"
@classmethod\ndef run_build_for_component_configuration(\ncls,\nconfig: ComponentConfiguration,\nlogger: Optional[logging.Logger] = None) -> None\n

Run a build entrypoint script for component configuration.

"},{"location":"aea-framework-documentation/api/aea_builder/#install_pypi_dependencies","title":"install_pypi_dependencies","text":"
def install_pypi_dependencies() -> None\n

Install components extra dependencies.

"},{"location":"aea-framework-documentation/api/aea_builder/#build","title":"build","text":"
def build(connection_ids: Optional[Collection[PublicId]] = None,\npassword: Optional[str] = None) -> AEA\n

Build the AEA.

This method is re-entrant only if the components have been added through the method 'add_component'. If some of them have been loaded with 'add_component_instance', it can be called only once, and further calls are only possible after a call to 'reset' and re-loading of the components added via 'add_component_instance' and the private keys.

Arguments:

  • connection_ids: select only these connections to run the AEA.
  • password: the password to encrypt/decrypt the private key.

Returns:

the AEA object.

"},{"location":"aea-framework-documentation/api/aea_builder/#get_default_ledger","title":"get_default_ledger","text":"
def get_default_ledger() -> str\n

Return default ledger.

Returns:

the default ledger identifier.

"},{"location":"aea-framework-documentation/api/aea_builder/#get_required_ledgers","title":"get_required_ledgers","text":"
def get_required_ledgers() -> List[str]\n

Get the required ledger identifiers.

These are the ledgers for which the AEA requires a key pair.

Returns:

the list of required ledgers.

"},{"location":"aea-framework-documentation/api/aea_builder/#try_to_load_agent_configuration_file","title":"try_to_load_agent_configuration_file","text":"
@classmethod\ndef try_to_load_agent_configuration_file(\ncls, aea_project_path: Union[str, Path]) -> AgentConfig\n

Try to load the agent configuration file..

"},{"location":"aea-framework-documentation/api/aea_builder/#set_from_configuration","title":"set_from_configuration","text":"
def set_from_configuration(agent_configuration: AgentConfig,\naea_project_path: Path,\nskip_consistency_check: bool = False) -> None\n

Set builder variables from AgentConfig.

Arguments:

  • agent_configuration: AgentConfig to get values from.
  • aea_project_path: PathLike root directory of the agent project.
  • skip_consistency_check: if True, the consistency check are skipped.

"},{"location":"aea-framework-documentation/api/aea_builder/#from_aea_project","title":"from_aea_project","text":"
@classmethod\ndef from_aea_project(cls,\naea_project_path: PathLike,\nskip_consistency_check: bool = False,\npassword: Optional[str] = None) -> \"AEABuilder\"\n

Construct the builder from an AEA project.

  • load agent configuration file
  • set name and default configurations
  • load private keys
  • load ledger API configurations
  • set default ledger
  • load every component

Arguments:

  • aea_project_path: path to the AEA project.
  • skip_consistency_check: if True, the consistency check are skipped.
  • password: the password to encrypt/decrypt private keys.

Returns:

an AEABuilder.

"},{"location":"aea-framework-documentation/api/aea_builder/#get_configuration_file_path","title":"get_configuration_file_path","text":"
@staticmethod\ndef get_configuration_file_path(aea_project_path: Union[Path, str]) -> Path\n

Return path to aea-config file for the given aea project path.

"},{"location":"aea-framework-documentation/api/aea_builder/#make_component_logger","title":"make_component_logger","text":"
def make_component_logger(configuration: ComponentConfiguration,\nagent_name: str) -> Optional[logging.Logger]\n

Make the logger for a component.

Arguments:

  • configuration: the component configuration
  • agent_name: the agent name

Returns:

the logger.

"},{"location":"aea-framework-documentation/api/agent/","title":"Agent","text":""},{"location":"aea-framework-documentation/api/agent/#aeaagent","title":"aea.agent","text":"

This module contains the implementation of a generic agent.

"},{"location":"aea-framework-documentation/api/agent/#agent-objects","title":"Agent Objects","text":"
class Agent(AbstractAgent, WithLogger)\n

This class provides an abstract base class for a generic agent.

"},{"location":"aea-framework-documentation/api/agent/#__init__","title":"__init__","text":"
def __init__(identity: Identity,\nconnections: List[Connection],\nloop: Optional[AbstractEventLoop] = None,\nperiod: float = 1.0,\nloop_mode: Optional[str] = None,\nruntime_mode: Optional[str] = None,\nstorage_uri: Optional[str] = None,\nlogger: Logger = _default_logger,\ntask_manager_mode: Optional[str] = None) -> None\n

Instantiate the agent.

Arguments:

  • identity: the identity of the agent.
  • connections: the list of connections of the agent.
  • loop: the event loop to run the connections.
  • period: period to call agent's act
  • loop_mode: loop_mode to choose agent run loop.
  • runtime_mode: runtime mode to up agent.
  • storage_uri: optional uri to set generic storage
  • task_manager_mode: task manager mode.
  • logger: the logger.
  • task_manager_mode: mode of the task manager.

"},{"location":"aea-framework-documentation/api/agent/#storage_uri","title":"storage_uri","text":"
@property\ndef storage_uri() -> Optional[str]\n

Return storage uri.

"},{"location":"aea-framework-documentation/api/agent/#is_running","title":"is_running","text":"
@property\ndef is_running() -> bool\n

Get running state of the runtime and agent.

"},{"location":"aea-framework-documentation/api/agent/#is_stopped","title":"is_stopped","text":"
@property\ndef is_stopped() -> bool\n

Get running state of the runtime and agent.

"},{"location":"aea-framework-documentation/api/agent/#identity","title":"identity","text":"
@property\ndef identity() -> Identity\n

Get the identity.

"},{"location":"aea-framework-documentation/api/agent/#inbox","title":"inbox","text":"
@property\ndef inbox() -> InBox\n

Get the inbox.

The inbox contains Envelopes from the Multiplexer. The agent can pick these messages for processing.

Returns:

InBox instance

"},{"location":"aea-framework-documentation/api/agent/#outbox","title":"outbox","text":"
@property\ndef outbox() -> OutBox\n

Get the outbox.

The outbox contains Envelopes for the Multiplexer. Envelopes placed in the Outbox are processed by the Multiplexer.

Returns:

OutBox instance

"},{"location":"aea-framework-documentation/api/agent/#name","title":"name","text":"
@property\ndef name() -> str\n

Get the agent name.

"},{"location":"aea-framework-documentation/api/agent/#tick","title":"tick","text":"
@property\ndef tick() -> int\n

Get the tick or agent loop count.

Each agent loop (one call to each one of act(), react(), update()) increments the tick.

Returns:

tick count

"},{"location":"aea-framework-documentation/api/agent/#state","title":"state","text":"
@property\ndef state() -> RuntimeStates\n

Get state of the agent's runtime.

Returns:

RuntimeStates

"},{"location":"aea-framework-documentation/api/agent/#period","title":"period","text":"
@property\ndef period() -> float\n

Get a period to call act.

"},{"location":"aea-framework-documentation/api/agent/#runtime","title":"runtime","text":"
@property\ndef runtime() -> BaseRuntime\n

Get the runtime.

"},{"location":"aea-framework-documentation/api/agent/#setup","title":"setup","text":"
def setup() -> None\n

Set up the agent.

"},{"location":"aea-framework-documentation/api/agent/#start","title":"start","text":"
def start() -> None\n

Start the agent.

Performs the following:

  • calls start() on runtime.
  • waits for runtime to complete running (blocking)

"},{"location":"aea-framework-documentation/api/agent/#handle_envelope","title":"handle_envelope","text":"
def handle_envelope(envelope: Envelope) -> None\n

Handle an envelope.

Arguments:

  • envelope: the envelope to handle.

"},{"location":"aea-framework-documentation/api/agent/#act","title":"act","text":"
def act() -> None\n

Perform actions on period.

"},{"location":"aea-framework-documentation/api/agent/#stop","title":"stop","text":"
def stop() -> None\n

Stop the agent.

Performs the following:

  • calls stop() on runtime
  • waits for runtime to stop (blocking)

"},{"location":"aea-framework-documentation/api/agent/#teardown","title":"teardown","text":"
def teardown() -> None\n

Tear down the agent.

"},{"location":"aea-framework-documentation/api/agent/#get_periodic_tasks","title":"get_periodic_tasks","text":"
def get_periodic_tasks(\n) -> Dict[Callable, Tuple[float, Optional[datetime.datetime]]]\n

Get all periodic tasks for agent.

Returns:

dict of callable with period specified

"},{"location":"aea-framework-documentation/api/agent/#get_message_handlers","title":"get_message_handlers","text":"
def get_message_handlers() -> List[Tuple[Callable[[Any], None], Callable]]\n

Get handlers with message getters.

Returns:

List of tuples of callables: handler and coroutine to get a message

"},{"location":"aea-framework-documentation/api/agent/#exception_handler","title":"exception_handler","text":"
def exception_handler(exception: Exception, function: Callable) -> bool\n

Handle exception raised during agent main loop execution.

Arguments:

  • exception: exception raised
  • function: a callable exception raised in.

Returns:

bool, propagate exception if True otherwise skip it.

"},{"location":"aea-framework-documentation/api/agent_loop/","title":"Agent Loop","text":""},{"location":"aea-framework-documentation/api/agent_loop/#aeaagent_loop","title":"aea.agent_loop","text":"

This module contains the implementation of an agent loop using asyncio.

"},{"location":"aea-framework-documentation/api/agent_loop/#agentloopexception-objects","title":"AgentLoopException Objects","text":"
class AgentLoopException(AEAException)\n

Exception for agent loop runtime errors.

"},{"location":"aea-framework-documentation/api/agent_loop/#agentloopstates-objects","title":"AgentLoopStates Objects","text":"
class AgentLoopStates(Enum)\n

Internal agent loop states.

"},{"location":"aea-framework-documentation/api/agent_loop/#baseagentloop-objects","title":"BaseAgentLoop Objects","text":"
class BaseAgentLoop(Runnable, WithLogger, ABC)\n

Base abstract agent loop class.

"},{"location":"aea-framework-documentation/api/agent_loop/#__init__","title":"__init__","text":"
def __init__(agent: AbstractAgent,\nloop: Optional[AbstractEventLoop] = None,\nthreaded: bool = False) -> None\n

Init loop.

Arguments:

  • agent: Agent or AEA to run.
  • loop: optional asyncio event loop. if not specified a new loop will be created.
  • threaded: if True, run in threaded mode, else async

"},{"location":"aea-framework-documentation/api/agent_loop/#agent","title":"agent","text":"
@property\ndef agent() -> AbstractAgent\n

Get agent.

"},{"location":"aea-framework-documentation/api/agent_loop/#state","title":"state","text":"
@property\ndef state() -> AgentLoopStates\n

Get current main loop state.

"},{"location":"aea-framework-documentation/api/agent_loop/#wait_state","title":"wait_state","text":"
async def wait_state(\nstate_or_states: Union[Any, Sequence[Any]]) -> Tuple[Any, Any]\n

Wait state to be set.

Arguments:

  • state_or_states: state or list of states.

Returns:

tuple of previous state and new state.

"},{"location":"aea-framework-documentation/api/agent_loop/#is_running","title":"is_running","text":"
@property\ndef is_running() -> bool\n

Get running state of the loop.

"},{"location":"aea-framework-documentation/api/agent_loop/#set_loop","title":"set_loop","text":"
def set_loop(loop: AbstractEventLoop) -> None\n

Set event loop and all event loop related objects.

"},{"location":"aea-framework-documentation/api/agent_loop/#run","title":"run","text":"
async def run() -> None\n

Run agent loop.

"},{"location":"aea-framework-documentation/api/agent_loop/#send_to_skill","title":"send_to_skill","text":"
@abstractmethod\ndef send_to_skill(message_or_envelope: Union[Message, Envelope],\ncontext: Optional[EnvelopeContext] = None) -> None\n

Send message or envelope to another skill.

If message passed it will be wrapped into envelope with optional envelope context.

Arguments:

  • message_or_envelope: envelope to send to another skill.
  • context: envelope context

"},{"location":"aea-framework-documentation/api/agent_loop/#skill2skill_queue","title":"skill2skill_queue","text":"
@property\n@abstractmethod\ndef skill2skill_queue() -> Queue\n

Get skill to skill message queue.

"},{"location":"aea-framework-documentation/api/agent_loop/#asyncagentloop-objects","title":"AsyncAgentLoop Objects","text":"
class AsyncAgentLoop(BaseAgentLoop)\n

Asyncio based agent loop suitable only for AEA.

"},{"location":"aea-framework-documentation/api/agent_loop/#__init___1","title":"__init__","text":"
def __init__(agent: AbstractAgent,\nloop: AbstractEventLoop = None,\nthreaded: bool = False) -> None\n

Init agent loop.

Arguments:

  • agent: AEA instance
  • loop: asyncio loop to use. optional
  • threaded: is a new thread to be started for the agent loop

"},{"location":"aea-framework-documentation/api/agent_loop/#skill2skill_queue_1","title":"skill2skill_queue","text":"
@property\ndef skill2skill_queue() -> Queue\n

Get skill to skill message queue.

"},{"location":"aea-framework-documentation/api/agent_loop/#send_to_skill_1","title":"send_to_skill","text":"
def send_to_skill(message_or_envelope: Union[Message, Envelope],\ncontext: Optional[EnvelopeContext] = None) -> None\n

Send message or envelope to another skill.

If message passed it will be wrapped into envelope with optional envelope context.

Arguments:

  • message_or_envelope: envelope to send to another skill.
  • context: envelope context
"},{"location":"aea-framework-documentation/api/common/","title":"Common","text":""},{"location":"aea-framework-documentation/api/common/#aeacommon","title":"aea.common","text":"

This module contains the common types and interfaces used in the aea framework.

"},{"location":"aea-framework-documentation/api/exceptions/","title":"Exceptions","text":""},{"location":"aea-framework-documentation/api/exceptions/#aeaexceptions","title":"aea.exceptions","text":"

Exceptions for the AEA package.

"},{"location":"aea-framework-documentation/api/exceptions/#aeaexception-objects","title":"AEAException Objects","text":"
class AEAException(Exception)\n

User-defined exception for the AEA framework.

"},{"location":"aea-framework-documentation/api/exceptions/#aeapackageloadingerror-objects","title":"AEAPackageLoadingError Objects","text":"
class AEAPackageLoadingError(AEAException)\n

Class for exceptions that are raised for loading errors of AEA packages.

"},{"location":"aea-framework-documentation/api/exceptions/#aeasetuperror-objects","title":"AEASetupError Objects","text":"
class AEASetupError(AEAException)\n

Class for exceptions that are raised for setup errors of AEA packages.

"},{"location":"aea-framework-documentation/api/exceptions/#aeateardownerror-objects","title":"AEATeardownError Objects","text":"
class AEATeardownError(AEAException)\n

Class for exceptions that are raised for teardown errors of AEA packages.

"},{"location":"aea-framework-documentation/api/exceptions/#aeaactexception-objects","title":"AEAActException Objects","text":"
class AEAActException(AEAException)\n

Class for exceptions that are raised for act errors of AEA packages.

"},{"location":"aea-framework-documentation/api/exceptions/#aeahandleexception-objects","title":"AEAHandleException Objects","text":"
class AEAHandleException(AEAException)\n

Class for exceptions that are raised for handler errors of AEA packages.

"},{"location":"aea-framework-documentation/api/exceptions/#aeainstantiationexception-objects","title":"AEAInstantiationException Objects","text":"
class AEAInstantiationException(AEAException)\n

Class for exceptions that are raised for instantiation errors of AEA packages.

"},{"location":"aea-framework-documentation/api/exceptions/#aeapluginerror-objects","title":"AEAPluginError Objects","text":"
class AEAPluginError(AEAException)\n

Class for exceptions that are raised for wrong plugin setup of the working set.

"},{"location":"aea-framework-documentation/api/exceptions/#aeaenforceerror-objects","title":"AEAEnforceError Objects","text":"
class AEAEnforceError(AEAException)\n

Class for enforcement errors.

"},{"location":"aea-framework-documentation/api/exceptions/#aeavalidationerror-objects","title":"AEAValidationError Objects","text":"
class AEAValidationError(AEAException)\n

Class for validation errors of an AEA.

"},{"location":"aea-framework-documentation/api/exceptions/#aeacomponentloadexception-objects","title":"AEAComponentLoadException Objects","text":"
class AEAComponentLoadException(AEAException)\n

Class for component loading errors of an AEA.

"},{"location":"aea-framework-documentation/api/exceptions/#aeawalletnoaddressexception-objects","title":"AEAWalletNoAddressException Objects","text":"
class AEAWalletNoAddressException(AEAException)\n

Class for attempts to instantiate a wallet without addresses.

"},{"location":"aea-framework-documentation/api/exceptions/#_stopruntime-objects","title":"_StopRuntime Objects","text":"
class _StopRuntime(Exception)\n

Exception to stop runtime.

For internal usage only! Used to perform asyncio call from sync callbacks.

"},{"location":"aea-framework-documentation/api/exceptions/#__init__","title":"__init__","text":"
def __init__(reraise: Optional[Exception] = None) -> None\n

Init _StopRuntime exception.

Arguments:

  • reraise: exception to reraise.

"},{"location":"aea-framework-documentation/api/exceptions/#enforce","title":"enforce","text":"
def enforce(is_valid_condition: bool,\nexception_text: str,\nexception_class: Type[Exception] = AEAEnforceError) -> None\n

Evaluate a condition and raise an exception with the provided text if it is not satisfied.

Arguments:

  • is_valid_condition: the valid condition
  • exception_text: the exception to be raised
  • exception_class: the class of exception

"},{"location":"aea-framework-documentation/api/exceptions/#parse_exception","title":"parse_exception","text":"
def parse_exception(exception: Exception, limit: int = -1) -> str\n

Parse an exception to get the relevant lines.

Arguments:

  • exception: the exception to be parsed
  • limit: the limit

Returns:

exception as string

"},{"location":"aea-framework-documentation/api/launcher/","title":"Launcher","text":""},{"location":"aea-framework-documentation/api/launcher/#aealauncher","title":"aea.launcher","text":"

This module contains the implementation of multiple AEA configs launcher.

"},{"location":"aea-framework-documentation/api/launcher/#load_agent","title":"load_agent","text":"
def load_agent(agent_dir: Union[PathLike, str],\npassword: Optional[str] = None) -> AEA\n

Load AEA from directory.

Arguments:

  • agent_dir: agent configuration directory
  • password: the password to encrypt/decrypt the private key.

Returns:

AEA instance

"},{"location":"aea-framework-documentation/api/launcher/#aeadirtask-objects","title":"AEADirTask Objects","text":"
class AEADirTask(AbstractExecutorTask)\n

Task to run agent from agent configuration directory.

"},{"location":"aea-framework-documentation/api/launcher/#__init__","title":"__init__","text":"
def __init__(agent_dir: Union[PathLike, str],\npassword: Optional[str] = None) -> None\n

Init aea config dir task.

Arguments:

  • agent_dir: directory with aea config.
  • password: the password to encrypt/decrypt the private key.

"},{"location":"aea-framework-documentation/api/launcher/#id","title":"id","text":"
@property\ndef id() -> Union[PathLike, str]\n

Return agent_dir.

"},{"location":"aea-framework-documentation/api/launcher/#start","title":"start","text":"
def start() -> None\n

Start task.

"},{"location":"aea-framework-documentation/api/launcher/#stop","title":"stop","text":"
def stop() -> None\n

Stop task.

"},{"location":"aea-framework-documentation/api/launcher/#create_async_task","title":"create_async_task","text":"
def create_async_task(loop: AbstractEventLoop) -> TaskAwaitable\n

Return asyncio Task for task run in asyncio loop.

"},{"location":"aea-framework-documentation/api/launcher/#aeadirmultiprocesstask-objects","title":"AEADirMultiprocessTask Objects","text":"
class AEADirMultiprocessTask(AbstractMultiprocessExecutorTask)\n

Task to run agent from agent configuration directory.

Version for multiprocess executor mode.

"},{"location":"aea-framework-documentation/api/launcher/#__init___1","title":"__init__","text":"
def __init__(agent_dir: Union[PathLike, str],\nlog_level: Optional[str] = None,\npassword: Optional[str] = None) -> None\n

Init aea config dir task.

Arguments:

  • agent_dir: directory with aea config.
  • log_level: debug level applied for AEA in subprocess
  • password: the password to encrypt/decrypt the private key.

"},{"location":"aea-framework-documentation/api/launcher/#id_1","title":"id","text":"
@property\ndef id() -> Union[PathLike, str]\n

Return agent_dir.

"},{"location":"aea-framework-documentation/api/launcher/#failed","title":"failed","text":"
@property\ndef failed() -> bool\n

Return was exception failed or not.

If it's running it's not failed.

Returns:

bool

"},{"location":"aea-framework-documentation/api/launcher/#start_1","title":"start","text":"
def start() -> Tuple[Callable, Sequence[Any]]\n

Return function and arguments to call within subprocess.

"},{"location":"aea-framework-documentation/api/launcher/#stop_1","title":"stop","text":"
def stop() -> None\n

Stop task.

"},{"location":"aea-framework-documentation/api/launcher/#aealauncher-objects","title":"AEALauncher Objects","text":"
class AEALauncher(AbstractMultipleRunner)\n

Run multiple AEA instances.

"},{"location":"aea-framework-documentation/api/launcher/#__init___2","title":"__init__","text":"
def __init__(agent_dirs: Sequence[Union[PathLike, str]],\nmode: str,\nfail_policy: ExecutorExceptionPolicies = ExecutorExceptionPolicies\n.propagate,\nlog_level: Optional[str] = None,\npassword: Optional[str] = None) -> None\n

Init AEALauncher.

Arguments:

  • agent_dirs: sequence of AEA config directories.
  • mode: executor name to use.
  • fail_policy: one of ExecutorExceptionPolicies to be used with Executor
  • log_level: debug level applied for AEA in subprocesses
  • password: the password to encrypt/decrypt the private key.
"},{"location":"aea-framework-documentation/api/multiplexer/","title":"Multiplexer","text":""},{"location":"aea-framework-documentation/api/multiplexer/#aeamultiplexer","title":"aea.multiplexer","text":"

Module for the multiplexer class and related classes.

"},{"location":"aea-framework-documentation/api/multiplexer/#multiplexerstatus-objects","title":"MultiplexerStatus Objects","text":"
class MultiplexerStatus(AsyncState)\n

The connection status class.

"},{"location":"aea-framework-documentation/api/multiplexer/#__init__","title":"__init__","text":"
def __init__() -> None\n

Initialize the connection status.

"},{"location":"aea-framework-documentation/api/multiplexer/#is_connected","title":"is_connected","text":"
@property\ndef is_connected() -> bool\n

Return is connected.

"},{"location":"aea-framework-documentation/api/multiplexer/#is_connecting","title":"is_connecting","text":"
@property\ndef is_connecting() -> bool\n

Return is connecting.

"},{"location":"aea-framework-documentation/api/multiplexer/#is_disconnected","title":"is_disconnected","text":"
@property\ndef is_disconnected() -> bool\n

Return is disconnected.

"},{"location":"aea-framework-documentation/api/multiplexer/#is_disconnecting","title":"is_disconnecting","text":"
@property\ndef is_disconnecting() -> bool\n

Return is disconnected.

"},{"location":"aea-framework-documentation/api/multiplexer/#asyncmultiplexer-objects","title":"AsyncMultiplexer Objects","text":"
class AsyncMultiplexer(Runnable, WithLogger)\n

This class can handle multiple connections at once.

"},{"location":"aea-framework-documentation/api/multiplexer/#__init___1","title":"__init__","text":"
def __init__(\nconnections: Optional[Sequence[Connection]] = None,\ndefault_connection_index: int = 0,\nloop: Optional[AbstractEventLoop] = None,\nexception_policy: ExceptionPolicyEnum = ExceptionPolicyEnum.propagate,\nthreaded: bool = False,\nagent_name: str = \"standalone\",\ndefault_routing: Optional[Dict[PublicId, PublicId]] = None,\ndefault_connection: Optional[PublicId] = None,\nprotocols: Optional[List[Union[Protocol, Message]]] = None) -> None\n

Initialize the connection multiplexer.

Arguments:

  • connections: a sequence of connections.
  • default_connection_index: the index of the connection to use as default. This information is used for envelopes which don't specify any routing context. If connections is None, this parameter is ignored.
  • loop: the event loop to run the multiplexer. If None, a new event loop is created.
  • exception_policy: the exception policy used for connections.
  • threaded: if True, run in threaded mode, else async
  • agent_name: the name of the agent that owns the multiplexer, for logging purposes.
  • default_routing: default routing map
  • default_connection: default connection
  • protocols: protocols used

"},{"location":"aea-framework-documentation/api/multiplexer/#default_connection","title":"default_connection","text":"
@property\ndef default_connection() -> Optional[Connection]\n

Get the default connection.

"},{"location":"aea-framework-documentation/api/multiplexer/#in_queue","title":"in_queue","text":"
@property\ndef in_queue() -> AsyncFriendlyQueue\n

Get the in queue.

"},{"location":"aea-framework-documentation/api/multiplexer/#out_queue","title":"out_queue","text":"
@property\ndef out_queue() -> asyncio.Queue\n

Get the out queue.

"},{"location":"aea-framework-documentation/api/multiplexer/#connections","title":"connections","text":"
@property\ndef connections() -> Tuple[Connection, ...]\n

Get the connections.

"},{"location":"aea-framework-documentation/api/multiplexer/#is_connected_1","title":"is_connected","text":"
@property\ndef is_connected() -> bool\n

Check whether the multiplexer is processing envelopes.

"},{"location":"aea-framework-documentation/api/multiplexer/#default_routing","title":"default_routing","text":"
@property\ndef default_routing() -> Dict[PublicId, PublicId]\n

Get the default routing.

"},{"location":"aea-framework-documentation/api/multiplexer/#default_routing_1","title":"default_routing","text":"
@default_routing.setter\ndef default_routing(default_routing: Dict[PublicId, PublicId]) -> None\n

Set the default routing.

"},{"location":"aea-framework-documentation/api/multiplexer/#connection_status","title":"connection_status","text":"
@property\ndef connection_status() -> MultiplexerStatus\n

Get the connection status.

"},{"location":"aea-framework-documentation/api/multiplexer/#run","title":"run","text":"
async def run() -> None\n

Run multiplexer connect and receive/send tasks.

"},{"location":"aea-framework-documentation/api/multiplexer/#set_loop","title":"set_loop","text":"
def set_loop(loop: AbstractEventLoop) -> None\n

Set event loop and all event loop related objects.

Arguments:

  • loop: asyncio event loop.

"},{"location":"aea-framework-documentation/api/multiplexer/#add_connection","title":"add_connection","text":"
def add_connection(connection: Connection, is_default: bool = False) -> None\n

Add a connection to the multiplexer.

Arguments:

  • connection: the connection to add.
  • is_default: whether the connection added should be the default one.

"},{"location":"aea-framework-documentation/api/multiplexer/#connect","title":"connect","text":"
async def connect() -> None\n

Connect the multiplexer.

"},{"location":"aea-framework-documentation/api/multiplexer/#disconnect","title":"disconnect","text":"
async def disconnect() -> None\n

Disconnect the multiplexer.

"},{"location":"aea-framework-documentation/api/multiplexer/#get","title":"get","text":"
def get(block: bool = False,\ntimeout: Optional[float] = None) -> Optional[Envelope]\n

Get an envelope within a timeout.

Arguments:

  • block: make the call blocking (ignore the timeout).
  • timeout: the timeout to wait until an envelope is received.

Returns:

the envelope, or None if no envelope is available within a timeout.

"},{"location":"aea-framework-documentation/api/multiplexer/#async_get","title":"async_get","text":"
async def async_get() -> Envelope\n

Get an envelope async way.

Returns:

the envelope

"},{"location":"aea-framework-documentation/api/multiplexer/#async_wait","title":"async_wait","text":"
async def async_wait() -> None\n

Get an envelope async way.

Returns:

the envelope

"},{"location":"aea-framework-documentation/api/multiplexer/#put","title":"put","text":"
def put(envelope: Envelope) -> None\n

Schedule an envelope for sending it.

Notice that the output queue is an asyncio.Queue which uses an event loop running on a different thread than the one used in this function.

Arguments:

  • envelope: the envelope to be sent.

"},{"location":"aea-framework-documentation/api/multiplexer/#multiplexer-objects","title":"Multiplexer Objects","text":"
class Multiplexer(AsyncMultiplexer)\n

Transit sync multiplexer for compatibility.

"},{"location":"aea-framework-documentation/api/multiplexer/#__init___2","title":"__init__","text":"
def __init__(*args: Any, **kwargs: Any) -> None\n

Initialize the connection multiplexer.

Arguments:

  • args: arguments
  • kwargs: keyword arguments

"},{"location":"aea-framework-documentation/api/multiplexer/#set_loop_1","title":"set_loop","text":"
def set_loop(loop: AbstractEventLoop) -> None\n

Set event loop and all event loop related objects.

Arguments:

  • loop: asyncio event loop.

"},{"location":"aea-framework-documentation/api/multiplexer/#connect_1","title":"connect","text":"
def connect() -> None\n

Connect the multiplexer.

Synchronously in thread spawned if new loop created.

"},{"location":"aea-framework-documentation/api/multiplexer/#disconnect_1","title":"disconnect","text":"
def disconnect() -> None\n

Disconnect the multiplexer.

Also stops a dedicated thread for event loop if spawned on connect.

"},{"location":"aea-framework-documentation/api/multiplexer/#put_1","title":"put","text":"
def put(envelope: Envelope) -> None\n

Schedule an envelope for sending it.

Notice that the output queue is an asyncio.Queue which uses an event loop running on a different thread than the one used in this function.

Arguments:

  • envelope: the envelope to be sent.

"},{"location":"aea-framework-documentation/api/multiplexer/#inbox-objects","title":"InBox Objects","text":"
class InBox()\n

A queue from where you can only consume envelopes.

"},{"location":"aea-framework-documentation/api/multiplexer/#__init___3","title":"__init__","text":"
def __init__(multiplexer: AsyncMultiplexer) -> None\n

Initialize the inbox.

Arguments:

  • multiplexer: the multiplexer

"},{"location":"aea-framework-documentation/api/multiplexer/#empty","title":"empty","text":"
def empty() -> bool\n

Check for a envelope on the in queue.

Returns:

boolean indicating whether there is an envelope or not

"},{"location":"aea-framework-documentation/api/multiplexer/#get_1","title":"get","text":"
def get(block: bool = False, timeout: Optional[float] = None) -> Envelope\n

Check for a envelope on the in queue.

Arguments:

  • block: make the call blocking (ignore the timeout).
  • timeout: times out the block after timeout seconds.

Raises:

  • Empty: if the attempt to get an envelope fails.

Returns:

the envelope object.

"},{"location":"aea-framework-documentation/api/multiplexer/#get_nowait","title":"get_nowait","text":"
def get_nowait() -> Optional[Envelope]\n

Check for a envelope on the in queue and wait for no time.

Returns:

the envelope object

"},{"location":"aea-framework-documentation/api/multiplexer/#async_get_1","title":"async_get","text":"
async def async_get() -> Envelope\n

Check for a envelope on the in queue.

Returns:

the envelope object.

"},{"location":"aea-framework-documentation/api/multiplexer/#async_wait_1","title":"async_wait","text":"
async def async_wait() -> None\n

Check for a envelope on the in queue.

"},{"location":"aea-framework-documentation/api/multiplexer/#outbox-objects","title":"OutBox Objects","text":"
class OutBox()\n

A queue from where you can only enqueue envelopes.

"},{"location":"aea-framework-documentation/api/multiplexer/#__init___4","title":"__init__","text":"
def __init__(multiplexer: AsyncMultiplexer) -> None\n

Initialize the outbox.

Arguments:

  • multiplexer: the multiplexer

"},{"location":"aea-framework-documentation/api/multiplexer/#empty_1","title":"empty","text":"
def empty() -> bool\n

Check for a envelope on the in queue.

Returns:

boolean indicating whether there is an envelope or not

"},{"location":"aea-framework-documentation/api/multiplexer/#put_2","title":"put","text":"
def put(envelope: Envelope) -> None\n

Put an envelope into the queue.

Arguments:

  • envelope: the envelope.

"},{"location":"aea-framework-documentation/api/multiplexer/#put_message","title":"put_message","text":"
def put_message(message: Message,\ncontext: Optional[EnvelopeContext] = None) -> None\n

Put a message in the outbox.

This constructs an envelope with the input arguments.

Arguments:

  • message: the message
  • context: the envelope context
"},{"location":"aea-framework-documentation/api/runner/","title":"Runner","text":""},{"location":"aea-framework-documentation/api/runner/#aearunner","title":"aea.runner","text":"

This module contains the implementation of AEA multiple instances runner.

"},{"location":"aea-framework-documentation/api/runner/#aeainstancetask-objects","title":"AEAInstanceTask Objects","text":"
class AEAInstanceTask(AbstractExecutorTask)\n

Task to run agent instance.

"},{"location":"aea-framework-documentation/api/runner/#__init__","title":"__init__","text":"
def __init__(agent: AEA) -> None\n

Init aea instance task.

Arguments:

  • agent: AEA instance to run within task.

"},{"location":"aea-framework-documentation/api/runner/#id","title":"id","text":"
@property\ndef id() -> str\n

Return agent name.

"},{"location":"aea-framework-documentation/api/runner/#start","title":"start","text":"
def start() -> None\n

Start task.

"},{"location":"aea-framework-documentation/api/runner/#stop","title":"stop","text":"
def stop() -> None\n

Stop task.

"},{"location":"aea-framework-documentation/api/runner/#create_async_task","title":"create_async_task","text":"
def create_async_task(loop: AbstractEventLoop) -> TaskAwaitable\n

Return asyncio Task for task run in asyncio loop.

Arguments:

  • loop: abstract event loop

Returns:

task to run runtime

"},{"location":"aea-framework-documentation/api/runner/#aearunner-objects","title":"AEARunner Objects","text":"
class AEARunner(AbstractMultipleRunner)\n

Run multiple AEA instances.

"},{"location":"aea-framework-documentation/api/runner/#__init___1","title":"__init__","text":"
def __init__(\nagents: Sequence[AEA],\nmode: str,\nfail_policy: ExecutorExceptionPolicies = ExecutorExceptionPolicies.\npropagate\n) -> None\n

Init AEARunner.

Arguments:

  • agents: sequence of AEA instances to run.
  • mode: executor name to use.
  • fail_policy: one of ExecutorExceptionPolicies to be used with Executor
"},{"location":"aea-framework-documentation/api/runtime/","title":"Runtime","text":""},{"location":"aea-framework-documentation/api/runtime/#aearuntime","title":"aea.runtime","text":"

This module contains the implementation of runtime for economic agent (AEA).

"},{"location":"aea-framework-documentation/api/runtime/#runtimestates-objects","title":"RuntimeStates Objects","text":"
class RuntimeStates(Enum)\n

Runtime states.

"},{"location":"aea-framework-documentation/api/runtime/#baseruntime-objects","title":"BaseRuntime Objects","text":"
class BaseRuntime(Runnable, WithLogger)\n

Abstract runtime class to create implementations.

"},{"location":"aea-framework-documentation/api/runtime/#__init__","title":"__init__","text":"
def __init__(agent: AbstractAgent,\nmultiplexer_options: Dict,\nloop_mode: Optional[str] = None,\nloop: Optional[AbstractEventLoop] = None,\nthreaded: bool = False,\ntask_manager_mode: Optional[str] = None) -> None\n

Init runtime.

Arguments:

  • agent: Agent to run.
  • multiplexer_options: options for the multiplexer.
  • loop_mode: agent main loop mode.
  • loop: optional event loop. if not provided a new one will be created.
  • threaded: if True, run in threaded mode, else async
  • task_manager_mode: mode of the task manager.

"},{"location":"aea-framework-documentation/api/runtime/#storage","title":"storage","text":"
@property\ndef storage() -> Optional[Storage]\n

Get optional storage.

"},{"location":"aea-framework-documentation/api/runtime/#loop_mode","title":"loop_mode","text":"
@property\ndef loop_mode() -> str\n

Get current loop mode.

"},{"location":"aea-framework-documentation/api/runtime/#task_manager","title":"task_manager","text":"
@property\ndef task_manager() -> TaskManager\n

Get the task manager.

"},{"location":"aea-framework-documentation/api/runtime/#loop","title":"loop","text":"
@property\ndef loop() -> Optional[AbstractEventLoop]\n

Get event loop.

"},{"location":"aea-framework-documentation/api/runtime/#agent_loop","title":"agent_loop","text":"
@property\ndef agent_loop() -> BaseAgentLoop\n

Get the agent loop.

"},{"location":"aea-framework-documentation/api/runtime/#multiplexer","title":"multiplexer","text":"
@property\ndef multiplexer() -> AsyncMultiplexer\n

Get multiplexer.

"},{"location":"aea-framework-documentation/api/runtime/#is_running","title":"is_running","text":"
@property\ndef is_running() -> bool\n

Get running state of the runtime.

"},{"location":"aea-framework-documentation/api/runtime/#is_stopped","title":"is_stopped","text":"
@property\ndef is_stopped() -> bool\n

Get stopped state of the runtime.

"},{"location":"aea-framework-documentation/api/runtime/#state","title":"state","text":"
@property\ndef state() -> RuntimeStates\n

Get runtime state.

Returns:

RuntimeStates

"},{"location":"aea-framework-documentation/api/runtime/#decision_maker","title":"decision_maker","text":"
@property\ndef decision_maker() -> DecisionMaker\n

Return decision maker if set.

"},{"location":"aea-framework-documentation/api/runtime/#set_decision_maker","title":"set_decision_maker","text":"
def set_decision_maker(decision_maker_handler: DecisionMakerHandler) -> None\n

Set decision maker with handler provided.

"},{"location":"aea-framework-documentation/api/runtime/#set_loop","title":"set_loop","text":"
def set_loop(loop: AbstractEventLoop) -> None\n

Set event loop to be used.

Arguments:

  • loop: event loop to use.

"},{"location":"aea-framework-documentation/api/runtime/#asyncruntime-objects","title":"AsyncRuntime Objects","text":"
class AsyncRuntime(BaseRuntime)\n

Asynchronous runtime: uses asyncio loop for multiplexer and async agent main loop.

"},{"location":"aea-framework-documentation/api/runtime/#__init___1","title":"__init__","text":"
def __init__(agent: AbstractAgent,\nmultiplexer_options: Dict,\nloop_mode: Optional[str] = None,\nloop: Optional[AbstractEventLoop] = None,\nthreaded: bool = False,\ntask_manager_mode: Optional[str] = None) -> None\n

Init runtime.

Arguments:

  • agent: Agent to run.
  • multiplexer_options: options for the multiplexer.
  • loop_mode: agent main loop mode.
  • loop: optional event loop. if not provided a new one will be created.
  • threaded: if True, run in threaded mode, else async
  • task_manager_mode: mode of the task manager.

"},{"location":"aea-framework-documentation/api/runtime/#set_loop_1","title":"set_loop","text":"
def set_loop(loop: AbstractEventLoop) -> None\n

Set event loop to be used.

Arguments:

  • loop: event loop to use.

"},{"location":"aea-framework-documentation/api/runtime/#run","title":"run","text":"
async def run() -> None\n

Start runtime task.

Starts multiplexer and agent loop.

"},{"location":"aea-framework-documentation/api/runtime/#stop_runtime","title":"stop_runtime","text":"
async def stop_runtime() -> None\n

Stop runtime coroutine.

Stop main loop. Tear down the agent.. Disconnect multiplexer.

"},{"location":"aea-framework-documentation/api/runtime/#run_runtime","title":"run_runtime","text":"
async def run_runtime() -> None\n

Run runtime which means start agent loop, multiplexer and storage.

"},{"location":"aea-framework-documentation/api/runtime/#threadedruntime-objects","title":"ThreadedRuntime Objects","text":"
class ThreadedRuntime(AsyncRuntime)\n

Run agent and multiplexer in different threads with own asyncio loops.

"},{"location":"aea-framework-documentation/api/components/base/","title":"Base","text":""},{"location":"aea-framework-documentation/api/components/base/#aeacomponentsbase","title":"aea.components.base","text":"

This module contains definitions of agent components.

"},{"location":"aea-framework-documentation/api/components/base/#component-objects","title":"Component Objects","text":"
class Component(ABC, WithLogger)\n

Abstract class for an agent component.

"},{"location":"aea-framework-documentation/api/components/base/#__init__","title":"__init__","text":"
def __init__(configuration: Optional[ComponentConfiguration] = None,\nis_vendor: bool = False,\n**kwargs: Any) -> None\n

Initialize a package.

Arguments:

  • configuration: the package configuration.
  • is_vendor: whether the package is vendorized.
  • kwargs: the keyword arguments for the logger.

"},{"location":"aea-framework-documentation/api/components/base/#component_type","title":"component_type","text":"
@property\ndef component_type() -> ComponentType\n

Get the component type.

"},{"location":"aea-framework-documentation/api/components/base/#is_vendor","title":"is_vendor","text":"
@property\ndef is_vendor() -> bool\n

Get whether the component is vendorized or not.

"},{"location":"aea-framework-documentation/api/components/base/#prefix_import_path","title":"prefix_import_path","text":"
@property\ndef prefix_import_path() -> str\n

Get the prefix import path for this component.

"},{"location":"aea-framework-documentation/api/components/base/#component_id","title":"component_id","text":"
@property\ndef component_id() -> ComponentId\n

Ge the package id.

"},{"location":"aea-framework-documentation/api/components/base/#public_id","title":"public_id","text":"
@property\ndef public_id() -> PublicId\n

Get the public id.

"},{"location":"aea-framework-documentation/api/components/base/#configuration","title":"configuration","text":"
@property\ndef configuration() -> ComponentConfiguration\n

Get the component configuration.

"},{"location":"aea-framework-documentation/api/components/base/#directory","title":"directory","text":"
@property\ndef directory() -> Path\n

Get the directory. Raise error if it has not been set yet.

"},{"location":"aea-framework-documentation/api/components/base/#directory_1","title":"directory","text":"
@directory.setter\ndef directory(path: Path) -> None\n

Set the directory. Raise error if already set.

"},{"location":"aea-framework-documentation/api/components/base/#build_directory","title":"build_directory","text":"
@property\ndef build_directory() -> Optional[str]\n

Get build directory for the component.

"},{"location":"aea-framework-documentation/api/components/base/#load_aea_package","title":"load_aea_package","text":"
def load_aea_package(configuration: ComponentConfiguration) -> None\n

Load the AEA package from configuration.

It adds all the init.py modules into sys.modules.

Arguments:

  • configuration: the configuration object.

"},{"location":"aea-framework-documentation/api/components/base/#perform_load_aea_package","title":"perform_load_aea_package","text":"
def perform_load_aea_package(dir_: Path, author: str, package_type_plural: str,\npackage_name: str) -> None\n

Load the AEA package from values provided.

It adds all the init.py modules into sys.modules.

Arguments:

  • dir_: path of the component.
  • author: str
  • package_type_plural: str
  • package_name: str
"},{"location":"aea-framework-documentation/api/components/loader/","title":"Loader","text":""},{"location":"aea-framework-documentation/api/components/loader/#aeacomponentsloader","title":"aea.components.loader","text":"

This module contains utilities for loading components.

"},{"location":"aea-framework-documentation/api/components/loader/#component_type_to_class","title":"component_type_to_class","text":"
def component_type_to_class(component_type: ComponentType) -> Type[Component]\n

Get the component class from the component type.

Arguments:

  • component_type: the component type

Returns:

the component class

"},{"location":"aea-framework-documentation/api/components/loader/#load_component_from_config","title":"load_component_from_config","text":"
def load_component_from_config(configuration: ComponentConfiguration, *args,\n**kwargs) -> Component\n

Load a component from a directory.

Arguments:

  • configuration: the component configuration.
  • args: the positional arguments.
  • kwargs: the keyword arguments.

Returns:

the component instance.

"},{"location":"aea-framework-documentation/api/components/loader/#aeapackagenotfound-objects","title":"AEAPackageNotFound Objects","text":"
class AEAPackageNotFound(Exception)\n

Exception when failed to import package, cause not exists.

"},{"location":"aea-framework-documentation/api/components/utils/","title":"Utils","text":""},{"location":"aea-framework-documentation/api/components/utils/#aeacomponentsutils","title":"aea.components.utils","text":"

This module contains the component loading utils.

"},{"location":"aea-framework-documentation/api/configurations/constants/","title":"Constants","text":""},{"location":"aea-framework-documentation/api/configurations/constants/#aeaconfigurationsconstants","title":"aea.configurations.constants","text":"

Module to declare constants.

"},{"location":"aea-framework-documentation/api/configurations/data_types/","title":"Data Types","text":""},{"location":"aea-framework-documentation/api/configurations/data_types/#aeaconfigurationsdata_types","title":"aea.configurations.data_types","text":"

Base config data types.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#jsonserializable-objects","title":"JSONSerializable Objects","text":"
class JSONSerializable(ABC)\n

Interface for JSON-serializable objects.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#json","title":"json","text":"
@property\n@abstractmethod\ndef json() -> Dict\n

Compute the JSON representation.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#from_json","title":"from_json","text":"
@classmethod\n@abstractmethod\ndef from_json(cls, obj: Dict) -> \"JSONSerializable\"\n

Build from a JSON object.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#packageversion-objects","title":"PackageVersion Objects","text":"
@functools.total_ordering\nclass PackageVersion()\n

A package version.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#__init__","title":"__init__","text":"
def __init__(version_like: PackageVersionLike) -> None\n

Initialize a package version.

Arguments:

  • version_like: a string, os a semver.VersionInfo object.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#is_latest","title":"is_latest","text":"
@property\ndef is_latest() -> bool\n

Check whether the version is 'latest'.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#__str__","title":"__str__","text":"
def __str__() -> str\n

Get the string representation.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#__eq__","title":"__eq__","text":"
def __eq__(other: Any) -> bool\n

Check equality.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#__lt__","title":"__lt__","text":"
def __lt__(other: Any) -> bool\n

Compare with another object.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#packagetype-objects","title":"PackageType Objects","text":"
class PackageType(Enum)\n

Package types.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#to_plural","title":"to_plural","text":"
def to_plural() -> str\n

Get the plural name.

PackageType.AGENT.to_plural() 'agents' PackageType.PROTOCOL.to_plural() 'protocols' PackageType.CONNECTION.to_plural() 'connections' PackageType.SKILL.to_plural() 'skills' PackageType.CONTRACT.to_plural() 'contracts'

Returns:

pluralised package type

"},{"location":"aea-framework-documentation/api/configurations/data_types/#__str___1","title":"__str__","text":"
def __str__() -> str\n

Convert to string.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#componenttype-objects","title":"ComponentType Objects","text":"
class ComponentType(Enum)\n

Enum of component types supported.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#to_package_type","title":"to_package_type","text":"
def to_package_type() -> PackageType\n

Get package type for component type.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#plurals","title":"plurals","text":"
@staticmethod\ndef plurals() -> Collection[str]\n

Get the collection of type names, plural.

ComponentType.plurals() ['protocols', 'connections', 'skills', 'contracts']

Returns:

list of all pluralised component types

"},{"location":"aea-framework-documentation/api/configurations/data_types/#to_plural_1","title":"to_plural","text":"
def to_plural() -> str\n

Get the plural version of the component type.

ComponentType.PROTOCOL.to_plural() 'protocols' ComponentType.CONNECTION.to_plural() 'connections' ComponentType.SKILL.to_plural() 'skills' ComponentType.CONTRACT.to_plural() 'contracts'

Returns:

pluralised component type

"},{"location":"aea-framework-documentation/api/configurations/data_types/#__str___2","title":"__str__","text":"
def __str__() -> str\n

Get the string representation.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#publicid-objects","title":"PublicId Objects","text":"
class PublicId(JSONSerializable)\n

This class implement a public identifier.

A public identifier is composed of three elements: - author - name - version

The concatenation of those three elements gives the public identifier:

author/name:version\n

public_id = PublicId(\"author\", \"my_package\", \"0.1.0\") assert public_id.author == \"author\" assert public_id.name == \"my_package\" assert public_id.version == \"0.1.0\" another_public_id = PublicId(\"author\", \"my_package\", \"0.1.0\") assert hash(public_id) == hash(another_public_id) assert public_id == another_public_id latest_public_id = PublicId(\"author\", \"my_package\", \"latest\") latest_public_id latest_public_id.package_version.is_latest True

"},{"location":"aea-framework-documentation/api/configurations/data_types/#__init___1","title":"__init__","text":"
def __init__(author: SimpleIdOrStr,\nname: SimpleIdOrStr,\nversion: Optional[PackageVersionLike] = None) -> None\n

Initialize the public identifier.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#author","title":"author","text":"
@property\ndef author() -> str\n

Get the author.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#name","title":"name","text":"
@property\ndef name() -> str\n

Get the name.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#version","title":"version","text":"
@property\ndef version() -> str\n

Get the version string.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#package_version","title":"package_version","text":"
@property\ndef package_version() -> PackageVersion\n

Get the package version object.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#to_any","title":"to_any","text":"
def to_any() -> \"PublicId\"\n

Return the same public id, but with any version.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#same_prefix","title":"same_prefix","text":"
def same_prefix(other: \"PublicId\") -> bool\n

Check if the other public id has the same author and name of this.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#to_latest","title":"to_latest","text":"
def to_latest() -> \"PublicId\"\n

Return the same public id, but with latest version.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#is_valid_str","title":"is_valid_str","text":"
@classmethod\ndef is_valid_str(cls, public_id_string: str) -> bool\n

Check if a string is a public id.

Arguments:

  • public_id_string: the public id in string format.

Returns:

bool indicating validity

"},{"location":"aea-framework-documentation/api/configurations/data_types/#from_str","title":"from_str","text":"
@classmethod\ndef from_str(cls, public_id_string: str) -> \"PublicId\"\n

Initialize the public id from the string.

str(PublicId.from_str(\"author/package_name:0.1.0\")) 'author/package_name:0.1.0'

A bad formatted input raises value error:

PublicId.from_str(\"bad/formatted:input\") Traceback (most recent call last): ... ValueError: Input 'bad/formatted:input' is not well formatted.

Arguments:

  • public_id_string: the public id in string format.

Raises:

  • ValueError: if the string in input is not well formatted.

Returns:

the public id object.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#try_from_str","title":"try_from_str","text":"
@classmethod\ndef try_from_str(cls, public_id_string: str) -> Optional[\"PublicId\"]\n

Safely try to get public id from string.

Arguments:

  • public_id_string: the public id in string format.

Returns:

the public id object or None

"},{"location":"aea-framework-documentation/api/configurations/data_types/#from_uri_path","title":"from_uri_path","text":"
@classmethod\ndef from_uri_path(cls, public_id_uri_path: str) -> \"PublicId\"\n

Initialize the public id from the string.

str(PublicId.from_uri_path(\"author/package_name/0.1.0\")) 'author/package_name:0.1.0'

A bad formatted input raises value error:

PublicId.from_uri_path(\"bad/formatted:input\") Traceback (most recent call last): ... ValueError: Input 'bad/formatted:input' is not well formatted.

Arguments:

  • public_id_uri_path: the public id in uri path string format.

Raises:

  • ValueError: if the string in input is not well formatted.

Returns:

the public id object.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#to_uri_path","title":"to_uri_path","text":"
@property\ndef to_uri_path() -> str\n

Turn the public id into a uri path string.

Returns:

uri path string

"},{"location":"aea-framework-documentation/api/configurations/data_types/#json_1","title":"json","text":"
@property\ndef json() -> Dict\n

Compute the JSON representation.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#from_json_1","title":"from_json","text":"
@classmethod\ndef from_json(cls, obj: Dict) -> \"PublicId\"\n

Build from a JSON object.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#__hash__","title":"__hash__","text":"
def __hash__() -> int\n

Get the hash.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#__str___3","title":"__str__","text":"
def __str__() -> str\n

Get the string representation.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#__repr__","title":"__repr__","text":"
def __repr__() -> str\n

Get the representation.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#__eq___1","title":"__eq__","text":"
def __eq__(other: Any) -> bool\n

Compare with another object.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#__lt___1","title":"__lt__","text":"
def __lt__(other: Any) -> bool\n

Compare two public ids.

public_id_1 = PublicId(\"author_1\", \"name_1\", \"0.1.0\") public_id_2 = PublicId(\"author_1\", \"name_1\", \"0.1.1\") public_id_3 = PublicId(\"author_1\", \"name_2\", \"0.1.0\") public_id_1 > public_id_2 False public_id_1 < public_id_2 True

public_id_1 < public_id_3 Traceback (most recent call last): ... ValueError: The public IDs author_1/name_1:0.1.0 and author_1/name_2:0.1.0 cannot be compared. Their author or name attributes are different.

Arguments:

  • other: the object to compate to

Raises:

  • ValueError: if the public ids cannot be confirmed

Returns:

whether or not the inequality is satisfied

"},{"location":"aea-framework-documentation/api/configurations/data_types/#packageid-objects","title":"PackageId Objects","text":"
class PackageId()\n

A package identifier.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#__init___2","title":"__init__","text":"
def __init__(package_type: Union[PackageType, str],\npublic_id: PublicId) -> None\n

Initialize the package id.

Arguments:

  • package_type: the package type.
  • public_id: the public id.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#package_type","title":"package_type","text":"
@property\ndef package_type() -> PackageType\n

Get the package type.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#public_id","title":"public_id","text":"
@property\ndef public_id() -> PublicId\n

Get the public id.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#author_1","title":"author","text":"
@property\ndef author() -> str\n

Get the author of the package.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#name_1","title":"name","text":"
@property\ndef name() -> str\n

Get the name of the package.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#version_1","title":"version","text":"
@property\ndef version() -> str\n

Get the version of the package.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#package_prefix","title":"package_prefix","text":"
@property\ndef package_prefix() -> Tuple[PackageType, str, str]\n

Get the package identifier without the version.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#from_uri_path_1","title":"from_uri_path","text":"
@classmethod\ndef from_uri_path(cls, package_id_uri_path: str) -> \"PackageId\"\n

Initialize the package id from the string.

str(PackageId.from_uri_path(\"skill/author/package_name/0.1.0\")) '(skill, author/package_name:0.1.0)'

A bad formatted input raises value error:

PackageId.from_uri_path(\"very/bad/formatted:input\") Traceback (most recent call last): ... ValueError: Input 'very/bad/formatted:input' is not well formatted.

Arguments:

  • package_id_uri_path: the package id in uri path string format.

Raises:

  • ValueError: if the string in input is not well formatted.

Returns:

the package id object.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#to_uri_path_1","title":"to_uri_path","text":"
@property\ndef to_uri_path() -> str\n

Turn the package id into a uri path string.

Returns:

uri path string

"},{"location":"aea-framework-documentation/api/configurations/data_types/#__hash___1","title":"__hash__","text":"
def __hash__() -> int\n

Get the hash.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#__str___4","title":"__str__","text":"
def __str__() -> str\n

Get the string representation.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#__repr___1","title":"__repr__","text":"
def __repr__() -> str\n

Get the object representation in string.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#__eq___2","title":"__eq__","text":"
def __eq__(other: Any) -> bool\n

Compare with another object.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#__lt___2","title":"__lt__","text":"
def __lt__(other: Any) -> bool\n

Compare two public ids.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#componentid-objects","title":"ComponentId Objects","text":"
class ComponentId(PackageId)\n

Class to represent a component identifier.

A component id is a package id, but excludes the case when the package is an agent.

pacakge_id = PackageId(PackageType.PROTOCOL, PublicId(\"author\", \"name\", \"0.1.0\")) component_id = ComponentId(ComponentType.PROTOCOL, PublicId(\"author\", \"name\", \"0.1.0\")) pacakge_id == component_id True

component_id2 = ComponentId(ComponentType.PROTOCOL, PublicId(\"author\", \"name\", \"0.1.1\")) pacakge_id == component_id2 False

"},{"location":"aea-framework-documentation/api/configurations/data_types/#__init___3","title":"__init__","text":"
def __init__(component_type: Union[ComponentType, str],\npublic_id: PublicId) -> None\n

Initialize the component id.

Arguments:

  • component_type: the component type.
  • public_id: the public id.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#component_type","title":"component_type","text":"
@property\ndef component_type() -> ComponentType\n

Get the component type.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#component_prefix","title":"component_prefix","text":"
@property\ndef component_prefix() -> PackageIdPrefix\n

Get the component identifier without the version.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#same_prefix_1","title":"same_prefix","text":"
def same_prefix(other: \"ComponentId\") -> bool\n

Check if the other component id has the same type, author and name of this.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#prefix_import_path","title":"prefix_import_path","text":"
@property\ndef prefix_import_path() -> str\n

Get the prefix import path for this component.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#json_2","title":"json","text":"
@property\ndef json() -> Dict\n

Get the JSON representation.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#from_json_2","title":"from_json","text":"
@classmethod\ndef from_json(cls, json_data: Dict) -> \"ComponentId\"\n

Create component id from json data.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#pypipackagename-objects","title":"PyPIPackageName Objects","text":"
class PyPIPackageName(RegexConstrainedString)\n

A PyPI Package name.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#gitref-objects","title":"GitRef Objects","text":"
class GitRef(RegexConstrainedString)\n

A Git reference.

It can be a branch name, a commit hash or a tag.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#dependency-objects","title":"Dependency Objects","text":"
class Dependency()\n

This class represents a PyPI dependency.

It contains the following information: - version: a version specifier(s) (e.g. '==0.1.0'). - index: the PyPI index where to download the package from (default: https://pypi.org) - git: the URL to the Git repository (e.g. https://github.com/fetchai/agents-aea.git) - ref: either the branch name, the tag, the commit number or a Git reference (default: 'master'.)

If the 'git' field is set, the 'version' field will be ignored. These fields will be forwarded to the 'pip' command.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#__init___4","title":"__init__","text":"
def __init__(name: Union[PyPIPackageName, str],\nversion: Union[str, SpecifierSet] = \"\",\nindex: Optional[str] = None,\ngit: Optional[str] = None,\nref: Optional[Union[GitRef, str]] = None) -> None\n

Initialize a PyPI dependency.

Arguments:

  • name: the package name.
  • version: the specifier set object
  • index: the URL to the PyPI server.
  • git: the URL to a git repository.
  • ref: the Git reference (branch/commit/tag).

"},{"location":"aea-framework-documentation/api/configurations/data_types/#name_2","title":"name","text":"
@property\ndef name() -> str\n

Get the name.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#version_2","title":"version","text":"
@property\ndef version() -> str\n

Get the version.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#index","title":"index","text":"
@property\ndef index() -> Optional[str]\n

Get the index.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#git","title":"git","text":"
@property\ndef git() -> Optional[str]\n

Get the git.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#ref","title":"ref","text":"
@property\ndef ref() -> Optional[str]\n

Get the ref.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#from_json_3","title":"from_json","text":"
@classmethod\ndef from_json(cls, obj: Dict[str, Dict[str, str]]) -> \"Dependency\"\n

Parse a dependency object from a dictionary.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#to_json","title":"to_json","text":"
def to_json() -> Dict[str, Dict[str, str]]\n

Transform the object to JSON.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#get_pip_install_args","title":"get_pip_install_args","text":"
def get_pip_install_args() -> List[str]\n

Get 'pip install' arguments.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#__str___5","title":"__str__","text":"
def __str__() -> str\n

Get the string representation.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#__eq___3","title":"__eq__","text":"
def __eq__(other: Any) -> bool\n

Compare with another object.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#dependencies","title":"Dependencies","text":"

A dictionary from package name to dependency data structure (see above). The package name must satisfy the constraints on Python packages names.

The main advantage of having a dictionary is that we implicitly filter out dependency duplicates. We cannot have two items with the same package name since the keys of a YAML object form a set.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#crudcollection-objects","title":"CRUDCollection Objects","text":"
class CRUDCollection(Generic[T])\n

Interface of a CRUD collection.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#__init___5","title":"__init__","text":"
def __init__() -> None\n

Instantiate a CRUD collection.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#create","title":"create","text":"
def create(item_id: str, item: T) -> None\n

Add an item.

Arguments:

  • item_id: the item id.
  • item: the item to be added.

Raises:

  • ValueError: if the item with the same id is already in the collection.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#read","title":"read","text":"
def read(item_id: str) -> Optional[T]\n

Get an item by its name.

Arguments:

  • item_id: the item id.

Returns:

the associated item, or None if the item id is not present.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#update","title":"update","text":"
def update(item_id: str, item: T) -> None\n

Update an existing item.

Arguments:

  • item_id: the item id.
  • item: the item to be added.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#delete","title":"delete","text":"
def delete(item_id: str) -> None\n

Delete an item.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#read_all","title":"read_all","text":"
def read_all() -> List[Tuple[str, T]]\n

Read all the items.

"},{"location":"aea-framework-documentation/api/configurations/data_types/#keys","title":"keys","text":"
def keys() -> Set[str]\n

Get the set of keys.

"},{"location":"aea-framework-documentation/api/configurations/manager/","title":"Manager","text":""},{"location":"aea-framework-documentation/api/configurations/manager/#aeaconfigurationsmanager","title":"aea.configurations.manager","text":"

Implementation of the AgentConfigManager.

"},{"location":"aea-framework-documentation/api/configurations/manager/#variabledoesnotexist-objects","title":"VariableDoesNotExist Objects","text":"
class VariableDoesNotExist(ValueError)\n

Variable does not exist in a config exception.

"},{"location":"aea-framework-documentation/api/configurations/manager/#handle_dotted_path","title":"handle_dotted_path","text":"
def handle_dotted_path(\nvalue: str,\nauthor: str,\naea_project_path: Union[str, Path] = \".\"\n) -> Tuple[List[str], Path, ConfigLoader, Optional[ComponentId]]\n

Separate the path between path to resource and json path to attribute.

Allowed values: 'agent.an_attribute_name' 'protocols.my_protocol.an_attribute_name' 'connections.my_connection.an_attribute_name' 'contracts.my_contract.an_attribute_name' 'skills.my_skill.an_attribute_name' 'vendor.author.[protocols|contracts|connections|skills].package_name.attribute_name

We also return the component id to retrieve the configuration of a specific component. Notice that at this point we don't know the version, so we put 'latest' as version, but later we will ignore it because we will filter with only the component prefix (i.e. the triple type, author and name).

Arguments:

  • value: dotted path.
  • author: the author string.
  • aea_project_path: project path

Returns:

Tuple[list of settings dict keys, filepath, config loader, component id].

"},{"location":"aea-framework-documentation/api/configurations/manager/#find_component_directory_from_component_id","title":"find_component_directory_from_component_id","text":"
def find_component_directory_from_component_id(\naea_project_directory: Path, component_id: ComponentId) -> Path\n

Find a component directory from component id.

"},{"location":"aea-framework-documentation/api/configurations/manager/#agentconfigmanager-objects","title":"AgentConfigManager Objects","text":"
class AgentConfigManager()\n

AeaConfig manager.

"},{"location":"aea-framework-documentation/api/configurations/manager/#__init__","title":"__init__","text":"
def __init__(agent_config: AgentConfig,\naea_project_directory: Union[str, Path],\nenv_vars_friendly: bool = False) -> None\n

Init manager.

Arguments:

  • agent_config: AgentConfig to manage.
  • aea_project_directory: directory where project for agent_config placed.
  • env_vars_friendly: whether or not it is env vars friendly

"},{"location":"aea-framework-documentation/api/configurations/manager/#load_component_configuration","title":"load_component_configuration","text":"
def load_component_configuration(\ncomponent_id: ComponentId,\nskip_consistency_check: bool = True) -> ComponentConfiguration\n

Load component configuration from the project directory.

Arguments:

  • component_id: Id of the component to load config for.
  • skip_consistency_check: bool.

Returns:

ComponentConfiguration

"},{"location":"aea-framework-documentation/api/configurations/manager/#agent_config_file_path","title":"agent_config_file_path","text":"
@property\ndef agent_config_file_path() -> Path\n

Return agent config file path.

"},{"location":"aea-framework-documentation/api/configurations/manager/#load","title":"load","text":"
@classmethod\ndef load(cls,\naea_project_path: Union[Path, str],\nsubstitude_env_vars: bool = False) -> \"AgentConfigManager\"\n

Create AgentConfigManager instance from agent project path.

"},{"location":"aea-framework-documentation/api/configurations/manager/#set_variable","title":"set_variable","text":"
def set_variable(path: VariablePath, value: JSON_TYPES) -> None\n

Set config variable.

Arguments:

  • path: str dotted path or List[Union[ComponentId, str]]
  • value: one of the json friendly objects.

"},{"location":"aea-framework-documentation/api/configurations/manager/#get_variable","title":"get_variable","text":"
def get_variable(path: VariablePath) -> JSON_TYPES\n

Set config variable.

Arguments:

  • path: str dotted path or List[Union[ComponentId, str]]

Returns:

json friendly value.

"},{"location":"aea-framework-documentation/api/configurations/manager/#update_config","title":"update_config","text":"
def update_config(overrides: Dict) -> None\n

Apply overrides for agent config.

Validates and applies agent config and component overrides. Does not save it on the disc!

Arguments:

  • overrides: overridden values dictionary

Returns:

None

"},{"location":"aea-framework-documentation/api/configurations/manager/#validate_current_config","title":"validate_current_config","text":"
def validate_current_config() -> None\n

Check is current config valid.

"},{"location":"aea-framework-documentation/api/configurations/manager/#json","title":"json","text":"
@property\ndef json() -> Dict\n

Return current agent config json representation.

"},{"location":"aea-framework-documentation/api/configurations/manager/#dump_config","title":"dump_config","text":"
def dump_config() -> None\n

Save agent config on the disc.

"},{"location":"aea-framework-documentation/api/configurations/manager/#verify_private_keys","title":"verify_private_keys","text":"
@classmethod\ndef verify_private_keys(\ncls,\naea_project_path: Union[Path, str],\nprivate_key_helper: Callable[[AgentConfig, Path, Optional[str]], None],\nsubstitude_env_vars: bool = False,\npassword: Optional[str] = None) -> \"AgentConfigManager\"\n

Verify private keys.

Does not saves the config! Use AgentConfigManager.dump_config()

Arguments:

  • aea_project_path: path to an AEA project.
  • private_key_helper: private_key_helper is a function that use agent config to check the keys
  • substitude_env_vars: replace env vars with values, does not dump config
  • password: the password to encrypt/decrypt the private key.

Returns:

the agent configuration manager.

"},{"location":"aea-framework-documentation/api/configurations/manager/#get_overridables","title":"get_overridables","text":"
def get_overridables() -> Tuple[Dict, Dict[ComponentId, Dict]]\n

Get config overridables.

"},{"location":"aea-framework-documentation/api/configurations/pypi/","title":"Pypi","text":""},{"location":"aea-framework-documentation/api/configurations/pypi/#aeaconfigurationspypi","title":"aea.configurations.pypi","text":"

This module contains a checker for PyPI version consistency.

"},{"location":"aea-framework-documentation/api/configurations/pypi/#and_","title":"and_","text":"
def and_(s1: SpecifierSet, s2: SpecifierSet) -> SpecifierSet\n

Do the and between two specifier sets.

"},{"location":"aea-framework-documentation/api/configurations/pypi/#is_satisfiable","title":"is_satisfiable","text":"
def is_satisfiable(specifier_set: SpecifierSet) -> bool\n

Check if the specifier set is satisfiable.

Satisfiable means that there exists a version number that satisfies all the constraints. It is worth noticing that it doesn't mean that that version number with that package actually exists.

from packaging.specifiers import SpecifierSet

The specifier set \">0.9, ==1.0\" is satisfiable: the version number \"1.0\" satisfies the constraints

s1 = SpecifierSet(\">0.9,==1.0\") \"1.0\" in s1 True is_satisfiable(s1) True

The specifier set \"==1.0, >1.1\" is not satisfiable:

s1 = SpecifierSet(\"==1.0,>1.1\") is_satisfiable(s1) False

For other details, please refer to PEP440:

https://www.python.org/dev/peps/pep-0440\n

Arguments:

  • specifier_set: the specifier set.

Returns:

False if the constraints are surely non-satisfiable, True if we don't know.

"},{"location":"aea-framework-documentation/api/configurations/pypi/#is_simple_dep","title":"is_simple_dep","text":"
def is_simple_dep(dep: Dependency) -> bool\n

Check if it is a simple dependency.

Namely, if it has no field specified, or only the 'version' field set.

Arguments:

  • dep: the dependency

Returns:

whether it is a simple dependency or not

"},{"location":"aea-framework-documentation/api/configurations/pypi/#to_set_specifier","title":"to_set_specifier","text":"
def to_set_specifier(dep: Dependency) -> SpecifierSet\n

Get the set specifier. It assumes to be a simple dependency (see above).

"},{"location":"aea-framework-documentation/api/configurations/pypi/#merge_dependencies","title":"merge_dependencies","text":"
def merge_dependencies(dep1: Dependencies, dep2: Dependencies) -> Dependencies\n

Merge two groups of dependencies.

If some of them are not \"simple\" (see above), and there is no risk of conflict because there is no other package with the same name, we leave them; otherwise we raise an error.

Arguments:

  • dep1: the first operand
  • dep2: the second operand.

Returns:

the merged dependencies.

"},{"location":"aea-framework-documentation/api/configurations/pypi/#merge_dependencies_list","title":"merge_dependencies_list","text":"
def merge_dependencies_list(*deps: Dependencies) -> Dependencies\n

Merge a list of dependencies.

Arguments:

  • deps: the list of dependencies

Returns:

the merged dependencies.

"},{"location":"aea-framework-documentation/api/configurations/utils/","title":"Utils","text":""},{"location":"aea-framework-documentation/api/configurations/utils/#aeaconfigurationsutils","title":"aea.configurations.utils","text":"

AEA configuration utils.

"},{"location":"aea-framework-documentation/api/configurations/utils/#replace_component_ids","title":"replace_component_ids","text":"
@singledispatch\ndef replace_component_ids(\n_arg: PackageConfiguration,\n_replacements: Dict[ComponentType, Dict[PublicId, PublicId]]) -> None\n

Update public id references in a package configuration.

This depends on the actual configuration being considered.

"},{"location":"aea-framework-documentation/api/configurations/utils/#_","title":"_","text":"
@replace_component_ids.register(AgentConfig)  # type: ignore\ndef _(arg: AgentConfig, replacements: Dict[ComponentType,\nDict[PublicId, PublicId]]) -> None\n

Replace references in agent configuration.

It breaks down in: 1) replace public ids in 'protocols', 'connections', 'contracts' and 'skills'; 2) replace public ids in default routing; 3) replace public id of default connection; 4) replace custom component configurations.

Arguments:

  • arg: the agent configuration.
  • replacements: the replacement mapping.

"},{"location":"aea-framework-documentation/api/configurations/utils/#__1","title":"_","text":"
@replace_component_ids.register(ProtocolConfig)  # type: ignore\ndef _(_arg: ProtocolConfig,\n_replacements: Dict[ComponentType, Dict[PublicId, PublicId]]) -> None\n

Do nothing - protocols have no references.

"},{"location":"aea-framework-documentation/api/configurations/utils/#__2","title":"_","text":"
@replace_component_ids.register(ConnectionConfig)  # type: ignore\ndef _(arg: ConnectionConfig,\nreplacements: Dict[ComponentType, Dict[PublicId, PublicId]]) -> None\n

Replace references in a connection configuration.

"},{"location":"aea-framework-documentation/api/configurations/utils/#__3","title":"_","text":"
@replace_component_ids.register(ContractConfig)  # type: ignore\ndef _(_arg: ContractConfig,\n_replacements: Dict[ComponentType, Dict[PublicId, PublicId]]) -> None\n

Do nothing - contracts have no references.

"},{"location":"aea-framework-documentation/api/configurations/utils/#__4","title":"_","text":"
@replace_component_ids.register(SkillConfig)  # type: ignore\ndef _(arg: SkillConfig, replacements: Dict[ComponentType,\nDict[PublicId, PublicId]]) -> None\n

Replace references in a skill configuration.

"},{"location":"aea-framework-documentation/api/configurations/utils/#get_latest_component_id_from_prefix","title":"get_latest_component_id_from_prefix","text":"
def get_latest_component_id_from_prefix(\nagent_config: AgentConfig,\ncomponent_prefix: PackageIdPrefix) -> Optional[ComponentId]\n

Get component id with the greatest version in an agent configuration given its prefix.

Arguments:

  • agent_config: the agent configuration.
  • component_prefix: the package prefix.

Returns:

the package id with the greatest version, or None if not found.

"},{"location":"aea-framework-documentation/api/configurations/validation/","title":"Validation","text":""},{"location":"aea-framework-documentation/api/configurations/validation/#aeaconfigurationsvalidation","title":"aea.configurations.validation","text":"

Implementation of the configuration validation.

"},{"location":"aea-framework-documentation/api/configurations/validation/#make_jsonschema_base_uri","title":"make_jsonschema_base_uri","text":"
def make_jsonschema_base_uri(base_uri_path: Path) -> str\n

Make the JSONSchema base URI, cross-platform.

Arguments:

  • base_uri_path: the path to the base directory.

Returns:

the string in URI form.

"},{"location":"aea-framework-documentation/api/configurations/validation/#extrapropertieserror-objects","title":"ExtraPropertiesError Objects","text":"
class ExtraPropertiesError(ValueError)\n

Extra properties exception.

"},{"location":"aea-framework-documentation/api/configurations/validation/#__str__","title":"__str__","text":"
def __str__() -> str\n

Get string representation of the object.

"},{"location":"aea-framework-documentation/api/configurations/validation/#__repr__","title":"__repr__","text":"
def __repr__() -> str\n

Get string representation of the object.

"},{"location":"aea-framework-documentation/api/configurations/validation/#customtypechecker-objects","title":"CustomTypeChecker Objects","text":"
class CustomTypeChecker(TypeChecker)\n

Custom type checker to handle env variables.

"},{"location":"aea-framework-documentation/api/configurations/validation/#is_type","title":"is_type","text":"
def is_type(instance, type) -> bool\n

Check is instance of type.

"},{"location":"aea-framework-documentation/api/configurations/validation/#own_additional_properties","title":"own_additional_properties","text":"
def own_additional_properties(validator, aP, instance, schema) -> Iterator\n

Additional properties validator.

"},{"location":"aea-framework-documentation/api/configurations/validation/#configvalidator-objects","title":"ConfigValidator Objects","text":"
class ConfigValidator()\n

Configuration validator implementation.

"},{"location":"aea-framework-documentation/api/configurations/validation/#__init__","title":"__init__","text":"
def __init__(schema_filename: str, env_vars_friendly: bool = False) -> None\n

Initialize the parser for configuration files.

Arguments:

  • schema_filename: the path to the JSON-schema file in 'aea/configurations/schemas'.
  • env_vars_friendly: whether or not it is env var friendly.

"},{"location":"aea-framework-documentation/api/configurations/validation/#split_component_id_and_config","title":"split_component_id_and_config","text":"
@staticmethod\ndef split_component_id_and_config(\ncomponent_index: int,\ncomponent_configuration_json: Dict) -> ComponentId\n

Split component id and configuration.

Arguments:

  • component_index: the position of the component configuration in the agent config file..
  • component_configuration_json: the JSON object to process.

Raises:

  • ValueError: if the component id cannot be extracted.

Returns:

the component id and the configuration object.

"},{"location":"aea-framework-documentation/api/configurations/validation/#validate_component_configuration","title":"validate_component_configuration","text":"
@classmethod\ndef validate_component_configuration(cls,\ncomponent_id: ComponentId,\nconfiguration: Dict,\nenv_vars_friendly: bool = False) -> None\n

Validate the component configuration of an agent configuration file.

This check is to detect inconsistencies in the specified fields.

Arguments:

  • component_id: the component id.
  • configuration: the configuration dictionary.
  • env_vars_friendly: bool, if set True, will not raise errors over the env variable definitions.

Raises:

  • ValueError: if the configuration is not valid.

"},{"location":"aea-framework-documentation/api/configurations/validation/#validate","title":"validate","text":"
def validate(json_data: Dict) -> None\n

Validate a JSON object against the right JSON schema.

Arguments:

  • json_data: the JSON data.

"},{"location":"aea-framework-documentation/api/configurations/validation/#validate_agent_components_configuration","title":"validate_agent_components_configuration","text":"
def validate_agent_components_configuration(\ncomponent_configurations: Dict) -> None\n

Validate agent component configurations overrides.

Arguments:

  • component_configurations: the component configurations to validate.

"},{"location":"aea-framework-documentation/api/configurations/validation/#required_fields","title":"required_fields","text":"
@property\ndef required_fields() -> List[str]\n

Get the required fields.

Returns:

list of required fields.

"},{"location":"aea-framework-documentation/api/configurations/validation/#validate_data_with_pattern","title":"validate_data_with_pattern","text":"
def validate_data_with_pattern(data: dict,\npattern: dict,\nexcludes: Optional[List[Tuple[str]]] = None,\nskip_env_vars: bool = False) -> List[str]\n

Validate data dict with pattern dict for attributes present and type match.

Arguments:

  • data: data dict to validate
  • pattern: dict with pattern to check over
  • excludes: list of tuples of str of paths to be skipped during the check
  • skip_env_vars: is set True will not check data type over env variables.

Returns:

list of str with error descriptions

"},{"location":"aea-framework-documentation/api/configurations/validation/#filter_data","title":"filter_data","text":"
def filter_data(base: Any, updates: Any) -> Any\n

Return difference in values or SAME_MARK object if values are the same.

"},{"location":"aea-framework-documentation/api/connections/base/","title":"Base","text":""},{"location":"aea-framework-documentation/api/connections/base/#aeaconnectionsbase","title":"aea.connections.base","text":"

The base connection package.

"},{"location":"aea-framework-documentation/api/connections/base/#connectionstates-objects","title":"ConnectionStates Objects","text":"
class ConnectionStates(Enum)\n

Connection states enum.

"},{"location":"aea-framework-documentation/api/connections/base/#connection-objects","title":"Connection Objects","text":"
class Connection(Component, ABC)\n

Abstract definition of a connection.

"},{"location":"aea-framework-documentation/api/connections/base/#__init__","title":"__init__","text":"
def __init__(configuration: ConnectionConfig,\ndata_dir: str,\nidentity: Optional[Identity] = None,\ncrypto_store: Optional[CryptoStore] = None,\nrestricted_to_protocols: Optional[Set[PublicId]] = None,\nexcluded_protocols: Optional[Set[PublicId]] = None,\n**kwargs: Any) -> None\n

Initialize the connection.

The configuration must be specified if and only if the following parameters are None: connection_id, excluded_protocols or restricted_to_protocols.

Arguments:

  • configuration: the connection configuration.
  • data_dir: directory where to put local files.
  • identity: the identity object held by the agent.
  • crypto_store: the crypto store for encrypted communication.
  • restricted_to_protocols: the set of protocols ids of the only supported protocols for this connection.
  • excluded_protocols: the set of protocols ids that we want to exclude for this connection.
  • kwargs: keyword arguments passed to component base

"},{"location":"aea-framework-documentation/api/connections/base/#loop","title":"loop","text":"
@property\ndef loop() -> asyncio.AbstractEventLoop\n

Get the event loop.

"},{"location":"aea-framework-documentation/api/connections/base/#address","title":"address","text":"
@property\ndef address() -> \"Address\"\n

Get the address.

"},{"location":"aea-framework-documentation/api/connections/base/#crypto_store","title":"crypto_store","text":"
@property\ndef crypto_store() -> CryptoStore\n

Get the crypto store.

"},{"location":"aea-framework-documentation/api/connections/base/#has_crypto_store","title":"has_crypto_store","text":"
@property\ndef has_crypto_store() -> bool\n

Check if the connection has the crypto store.

"},{"location":"aea-framework-documentation/api/connections/base/#data_dir","title":"data_dir","text":"
@property\ndef data_dir() -> str\n

Get the data directory.

"},{"location":"aea-framework-documentation/api/connections/base/#component_type","title":"component_type","text":"
@property\ndef component_type() -> ComponentType\n

Get the component type.

"},{"location":"aea-framework-documentation/api/connections/base/#configuration","title":"configuration","text":"
@property\ndef configuration() -> ConnectionConfig\n

Get the connection configuration.

"},{"location":"aea-framework-documentation/api/connections/base/#restricted_to_protocols","title":"restricted_to_protocols","text":"
@property\ndef restricted_to_protocols() -> Set[PublicId]\n

Get the ids of the protocols this connection is restricted to.

"},{"location":"aea-framework-documentation/api/connections/base/#excluded_protocols","title":"excluded_protocols","text":"
@property\ndef excluded_protocols() -> Set[PublicId]\n

Get the ids of the excluded protocols for this connection.

"},{"location":"aea-framework-documentation/api/connections/base/#state","title":"state","text":"
@property\ndef state() -> ConnectionStates\n

Get the connection status.

"},{"location":"aea-framework-documentation/api/connections/base/#state_1","title":"state","text":"
@state.setter\ndef state(value: ConnectionStates) -> None\n

Set the connection status.

"},{"location":"aea-framework-documentation/api/connections/base/#connect","title":"connect","text":"
@abstractmethod\nasync def connect() -> None\n

Set up the connection.

"},{"location":"aea-framework-documentation/api/connections/base/#disconnect","title":"disconnect","text":"
@abstractmethod\nasync def disconnect() -> None\n

Tear down the connection.

"},{"location":"aea-framework-documentation/api/connections/base/#send","title":"send","text":"
@abstractmethod\nasync def send(envelope: \"Envelope\") -> None\n

Send an envelope.

Arguments:

  • envelope: the envelope to send.

Returns:

None

"},{"location":"aea-framework-documentation/api/connections/base/#receive","title":"receive","text":"
@abstractmethod\nasync def receive(*args: Any, **kwargs: Any) -> Optional[\"Envelope\"]\n

Receive an envelope.

Arguments:

  • args: positional arguments
  • kwargs: keyword arguments

Returns:

the received envelope, or None if an error occurred.

"},{"location":"aea-framework-documentation/api/connections/base/#from_dir","title":"from_dir","text":"
@classmethod\ndef from_dir(cls, directory: str, identity: Identity,\ncrypto_store: CryptoStore, data_dir: str,\n**kwargs: Any) -> \"Connection\"\n

Load the connection from a directory.

Arguments:

  • directory: the directory to the connection package.
  • identity: the identity object.
  • crypto_store: object to access the connection crypto objects.
  • data_dir: the assets directory.
  • kwargs: keyword arguments passed to connection base

Returns:

the connection object.

"},{"location":"aea-framework-documentation/api/connections/base/#from_config","title":"from_config","text":"
@classmethod\ndef from_config(cls, configuration: ConnectionConfig, identity: Identity,\ncrypto_store: CryptoStore, data_dir: str,\n**kwargs: Any) -> \"Connection\"\n

Load a connection from a configuration.

Arguments:

  • configuration: the connection configuration.
  • identity: the identity object.
  • crypto_store: object to access the connection crypto objects.
  • data_dir: the directory of the AEA project data.
  • kwargs: keyword arguments passed to component base

Returns:

an instance of the concrete connection class.

"},{"location":"aea-framework-documentation/api/connections/base/#is_connected","title":"is_connected","text":"
@property\ndef is_connected() -> bool\n

Return is connected state.

"},{"location":"aea-framework-documentation/api/connections/base/#is_connecting","title":"is_connecting","text":"
@property\ndef is_connecting() -> bool\n

Return is connecting state.

"},{"location":"aea-framework-documentation/api/connections/base/#is_disconnected","title":"is_disconnected","text":"
@property\ndef is_disconnected() -> bool\n

Return is disconnected state.

"},{"location":"aea-framework-documentation/api/connections/base/#basesyncconnection-objects","title":"BaseSyncConnection Objects","text":"
class BaseSyncConnection(Connection)\n

Base sync connection class to write connections with sync code.

"},{"location":"aea-framework-documentation/api/connections/base/#__init___1","title":"__init__","text":"
def __init__(configuration: ConnectionConfig,\ndata_dir: str,\nidentity: Optional[Identity] = None,\ncrypto_store: Optional[CryptoStore] = None,\nrestricted_to_protocols: Optional[Set[PublicId]] = None,\nexcluded_protocols: Optional[Set[PublicId]] = None,\n**kwargs: Any) -> None\n

Initialize the connection.

The configuration must be specified if and only if the following parameters are None: connection_id, excluded_protocols or restricted_to_protocols.

Arguments:

  • configuration: the connection configuration.
  • data_dir: directory where to put local files.
  • identity: the identity object held by the agent.
  • crypto_store: the crypto store for encrypted communication.
  • restricted_to_protocols: the set of protocols ids of the only supported protocols for this connection.
  • excluded_protocols: the set of protocols ids that we want to exclude for this connection.
  • kwargs: keyword arguments passed to connection base

"},{"location":"aea-framework-documentation/api/connections/base/#put_envelope","title":"put_envelope","text":"
def put_envelope(envelope: Optional[\"Envelope\"]) -> None\n

Put envelope in to the incoming queue.

"},{"location":"aea-framework-documentation/api/connections/base/#connect_1","title":"connect","text":"
async def connect() -> None\n

Connect connection.

"},{"location":"aea-framework-documentation/api/connections/base/#disconnect_1","title":"disconnect","text":"
async def disconnect() -> None\n

Disconnect connection.

"},{"location":"aea-framework-documentation/api/connections/base/#send_1","title":"send","text":"
async def send(envelope: \"Envelope\") -> None\n

Send envelope to connection.

"},{"location":"aea-framework-documentation/api/connections/base/#receive_1","title":"receive","text":"
async def receive(*args: Any, **kwargs: Any) -> Optional[\"Envelope\"]\n

Get an envelope from the connection.

"},{"location":"aea-framework-documentation/api/connections/base/#start_main","title":"start_main","text":"
def start_main() -> None\n

Start main function of the connection.

"},{"location":"aea-framework-documentation/api/connections/base/#main","title":"main","text":"
def main() -> None\n

Run main body of the connection in dedicated thread.

"},{"location":"aea-framework-documentation/api/connections/base/#on_connect","title":"on_connect","text":"
@abstractmethod\ndef on_connect() -> None\n

Run on connect method called.

"},{"location":"aea-framework-documentation/api/connections/base/#on_disconnect","title":"on_disconnect","text":"
@abstractmethod\ndef on_disconnect() -> None\n

Run on disconnect method called.

"},{"location":"aea-framework-documentation/api/connections/base/#on_send","title":"on_send","text":"
@abstractmethod\ndef on_send(envelope: \"Envelope\") -> None\n

Run on send method called.

"},{"location":"aea-framework-documentation/api/context/base/","title":"Context","text":""},{"location":"aea-framework-documentation/api/context/base/#aeacontextbase","title":"aea.context.base","text":"

This module contains the agent context class.

"},{"location":"aea-framework-documentation/api/context/base/#agentcontext-objects","title":"AgentContext Objects","text":"
class AgentContext()\n

Provide read access to relevant objects of the agent for the skills.

"},{"location":"aea-framework-documentation/api/context/base/#__init__","title":"__init__","text":"
def __init__(identity: Identity,\nconnection_status: MultiplexerStatus,\noutbox: OutBox,\ndecision_maker_message_queue: Queue,\ndecision_maker_handler_context: SimpleNamespace,\ntask_manager: TaskManager,\ndefault_ledger_id: str,\ncurrency_denominations: Dict[str, str],\ndefault_connection: Optional[PublicId],\ndefault_routing: Dict[PublicId, PublicId],\nsearch_service_address: Address,\ndecision_maker_address: Address,\ndata_dir: str,\nstorage_callable: Callable[[], Optional[Storage]] = lambda: None,\nsend_to_skill: Optional[Callable] = None,\n**kwargs: Any) -> None\n

Initialize an agent context.

Arguments:

  • identity: the identity object
  • connection_status: the connection status of the multiplexer
  • outbox: the outbox
  • decision_maker_message_queue: the (in) queue of the decision maker
  • decision_maker_handler_context: the decision maker's name space
  • task_manager: the task manager
  • default_ledger_id: the default ledger id
  • currency_denominations: mapping from ledger ids to currency denominations
  • default_connection: the default connection
  • default_routing: the default routing
  • search_service_address: the address of the search service
  • decision_maker_address: the address of the decision maker
  • data_dir: directory where to put local files.
  • storage_callable: function that returns optional storage attached to agent.
  • send_to_skill: callable for sending envelopes to skills.
  • kwargs: keyword arguments to be attached in the agent context namespace.

"},{"location":"aea-framework-documentation/api/context/base/#send_to_skill","title":"send_to_skill","text":"
def send_to_skill(message_or_envelope: Union[Message, Envelope],\ncontext: Optional[EnvelopeContext] = None) -> None\n

Send message or envelope to another skill.

If message passed it will be wrapped into envelope with optional envelope context.

Arguments:

  • message_or_envelope: envelope to send to another skill.
  • context: the optional envelope context

"},{"location":"aea-framework-documentation/api/context/base/#storage","title":"storage","text":"
@property\ndef storage() -> Optional[Storage]\n

Return storage instance if enabled in AEA.

"},{"location":"aea-framework-documentation/api/context/base/#data_dir","title":"data_dir","text":"
@property\ndef data_dir() -> str\n

Return assets directory.

"},{"location":"aea-framework-documentation/api/context/base/#shared_state","title":"shared_state","text":"
@property\ndef shared_state() -> Dict[str, Any]\n

Get the shared state dictionary.

The shared state is the only object which skills can use to exchange state directly. It is accessible (read and write) from all skills.

Returns:

dictionary of the shared state.

"},{"location":"aea-framework-documentation/api/context/base/#identity","title":"identity","text":"
@property\ndef identity() -> Identity\n

Get the identity.

"},{"location":"aea-framework-documentation/api/context/base/#agent_name","title":"agent_name","text":"
@property\ndef agent_name() -> str\n

Get agent name.

"},{"location":"aea-framework-documentation/api/context/base/#addresses","title":"addresses","text":"
@property\ndef addresses() -> Dict[str, Address]\n

Get addresses.

"},{"location":"aea-framework-documentation/api/context/base/#public_keys","title":"public_keys","text":"
@property\ndef public_keys() -> Dict[str, str]\n

Get public keys.

"},{"location":"aea-framework-documentation/api/context/base/#address","title":"address","text":"
@property\ndef address() -> Address\n

Get the default address.

"},{"location":"aea-framework-documentation/api/context/base/#public_key","title":"public_key","text":"
@property\ndef public_key() -> str\n

Get the default public key.

"},{"location":"aea-framework-documentation/api/context/base/#connection_status","title":"connection_status","text":"
@property\ndef connection_status() -> MultiplexerStatus\n

Get connection status of the multiplexer.

"},{"location":"aea-framework-documentation/api/context/base/#outbox","title":"outbox","text":"
@property\ndef outbox() -> OutBox\n

Get outbox.

"},{"location":"aea-framework-documentation/api/context/base/#decision_maker_message_queue","title":"decision_maker_message_queue","text":"
@property\ndef decision_maker_message_queue() -> Queue\n

Get decision maker queue.

"},{"location":"aea-framework-documentation/api/context/base/#decision_maker_handler_context","title":"decision_maker_handler_context","text":"
@property\ndef decision_maker_handler_context() -> SimpleNamespace\n

Get the decision maker handler context.

"},{"location":"aea-framework-documentation/api/context/base/#task_manager","title":"task_manager","text":"
@property\ndef task_manager() -> TaskManager\n

Get the task manager.

"},{"location":"aea-framework-documentation/api/context/base/#search_service_address","title":"search_service_address","text":"
@property\ndef search_service_address() -> Address\n

Get the address of the search service.

"},{"location":"aea-framework-documentation/api/context/base/#decision_maker_address","title":"decision_maker_address","text":"
@property\ndef decision_maker_address() -> Address\n

Get the address of the decision maker.

"},{"location":"aea-framework-documentation/api/context/base/#default_ledger_id","title":"default_ledger_id","text":"
@property\ndef default_ledger_id() -> str\n

Get the default ledger id.

"},{"location":"aea-framework-documentation/api/context/base/#currency_denominations","title":"currency_denominations","text":"
@property\ndef currency_denominations() -> Dict[str, str]\n

Get a dictionary mapping ledger ids to currency denominations.

"},{"location":"aea-framework-documentation/api/context/base/#default_connection","title":"default_connection","text":"
@property\ndef default_connection() -> Optional[PublicId]\n

Get the default connection.

"},{"location":"aea-framework-documentation/api/context/base/#default_routing","title":"default_routing","text":"
@property\ndef default_routing() -> Dict[PublicId, PublicId]\n

Get the default routing.

"},{"location":"aea-framework-documentation/api/context/base/#namespace","title":"namespace","text":"
@property\ndef namespace() -> SimpleNamespace\n

Get the agent context namespace.

"},{"location":"aea-framework-documentation/api/contracts/base/","title":"Base","text":""},{"location":"aea-framework-documentation/api/contracts/base/#aeacontractsbase","title":"aea.contracts.base","text":"

The base contract.

"},{"location":"aea-framework-documentation/api/contracts/base/#contract-objects","title":"Contract Objects","text":"
class Contract(Component)\n

Abstract definition of a contract.

"},{"location":"aea-framework-documentation/api/contracts/base/#__init__","title":"__init__","text":"
def __init__(contract_config: ContractConfig, **kwargs: Any) -> None\n

Initialize the contract.

Arguments:

  • contract_config: the contract configurations.
  • kwargs: the keyword arguments.

"},{"location":"aea-framework-documentation/api/contracts/base/#id","title":"id","text":"
@property\ndef id() -> PublicId\n

Get the name.

"},{"location":"aea-framework-documentation/api/contracts/base/#configuration","title":"configuration","text":"
@property\ndef configuration() -> ContractConfig\n

Get the configuration.

"},{"location":"aea-framework-documentation/api/contracts/base/#get_instance","title":"get_instance","text":"
@classmethod\ndef get_instance(cls,\nledger_api: LedgerApi,\ncontract_address: Optional[str] = None) -> Any\n

Get the instance.

Arguments:

  • ledger_api: the ledger api we are using.
  • contract_address: the contract address.

Returns:

the contract instance

"},{"location":"aea-framework-documentation/api/contracts/base/#from_dir","title":"from_dir","text":"
@classmethod\ndef from_dir(cls, directory: str, **kwargs: Any) -> \"Contract\"\n

Load the protocol from a directory.

Arguments:

  • directory: the directory to the skill package.
  • kwargs: the keyword arguments.

Returns:

the contract object.

"},{"location":"aea-framework-documentation/api/contracts/base/#from_config","title":"from_config","text":"
@classmethod\ndef from_config(cls, configuration: ContractConfig,\n**kwargs: Any) -> \"Contract\"\n

Load contract from configuration.

Arguments:

  • configuration: the contract configuration.
  • kwargs: the keyword arguments.

Returns:

the contract object.

"},{"location":"aea-framework-documentation/api/contracts/base/#get_deploy_transaction","title":"get_deploy_transaction","text":"
@classmethod\ndef get_deploy_transaction(cls, ledger_api: LedgerApi, deployer_address: str,\n**kwargs: Any) -> Optional[JSONLike]\n

Handler method for the 'GET_DEPLOY_TRANSACTION' requests.

Implement this method in the sub class if you want to handle the contract requests manually.

Arguments:

  • ledger_api: the ledger apis.
  • deployer_address: The address that will deploy the contract.
  • kwargs: keyword arguments.

Returns:

the tx

"},{"location":"aea-framework-documentation/api/contracts/base/#get_raw_transaction","title":"get_raw_transaction","text":"
@classmethod\ndef get_raw_transaction(cls, ledger_api: LedgerApi, contract_address: str,\n**kwargs: Any) -> Optional[JSONLike]\n

Handler method for the 'GET_RAW_TRANSACTION' requests.

Implement this method in the sub class if you want to handle the contract requests manually.

Arguments:

  • ledger_api: the ledger apis.
  • contract_address: the contract address.
  • kwargs: the keyword arguments.

Returns:

the tx # noqa: DAR202

"},{"location":"aea-framework-documentation/api/contracts/base/#get_raw_message","title":"get_raw_message","text":"
@classmethod\ndef get_raw_message(cls, ledger_api: LedgerApi, contract_address: str,\n**kwargs: Any) -> Optional[bytes]\n

Handler method for the 'GET_RAW_MESSAGE' requests.

Implement this method in the sub class if you want to handle the contract requests manually.

Arguments:

  • ledger_api: the ledger apis.
  • contract_address: the contract address.
  • kwargs: the keyword arguments.

Returns:

the tx # noqa: DAR202

"},{"location":"aea-framework-documentation/api/contracts/base/#get_state","title":"get_state","text":"
@classmethod\ndef get_state(cls, ledger_api: LedgerApi, contract_address: str,\n**kwargs: Any) -> Optional[JSONLike]\n

Handler method for the 'GET_STATE' requests.

Implement this method in the sub class if you want to handle the contract requests manually.

Arguments:

  • ledger_api: the ledger apis.
  • contract_address: the contract address.
  • kwargs: the keyword arguments.

Returns:

the tx # noqa: DAR202

"},{"location":"aea-framework-documentation/api/crypto/base/","title":"Base","text":""},{"location":"aea-framework-documentation/api/crypto/base/#aeacryptobase","title":"aea.crypto.base","text":"

Abstract module wrapping the public and private key cryptography and ledger api.

"},{"location":"aea-framework-documentation/api/crypto/base/#crypto-objects","title":"Crypto Objects","text":"
class Crypto(Generic[EntityClass], ABC)\n

Base class for a crypto object.

"},{"location":"aea-framework-documentation/api/crypto/base/#__init__","title":"__init__","text":"
def __init__(private_key_path: Optional[str] = None,\npassword: Optional[str] = None,\n**kwargs: Any) -> None\n

Initialize the crypto object.

The actual behaviour of this constructor is determined by the abstract methods 'generate_private_key()' and 'load_private_key_from_path(). Either way, the entity object will be accessible as a property.

Arguments:

  • private_key_path: the path to the private key. If None, the key will be generated by 'generate_private_key()'. If not None, the path will be processed by 'load_private_key_from_path()'.
  • password: the password to encrypt/decrypt the private key.
  • kwargs: keyword arguments.

"},{"location":"aea-framework-documentation/api/crypto/base/#generate_private_key","title":"generate_private_key","text":"
@classmethod\n@abstractmethod\ndef generate_private_key(cls) -> EntityClass\n

Generate a private key.

Returns:

the entity object. Implementation dependent.

"},{"location":"aea-framework-documentation/api/crypto/base/#load_private_key_from_path","title":"load_private_key_from_path","text":"
@classmethod\n@abstractmethod\ndef load_private_key_from_path(cls,\nfile_name: str,\npassword: Optional[str] = None) -> EntityClass\n

Load a private key in hex format for raw private key and json format for encrypted private key from a file.

Arguments:

  • file_name: the path to the hex/json file.
  • password: the password to encrypt/decrypt the private key.

Returns:

the entity object.

"},{"location":"aea-framework-documentation/api/crypto/base/#entity","title":"entity","text":"
@property\ndef entity() -> EntityClass\n

Return an entity object.

Returns:

an entity object

"},{"location":"aea-framework-documentation/api/crypto/base/#private_key","title":"private_key","text":"
@property\n@abstractmethod\ndef private_key() -> str\n

Return a private key.

Returns:

a private key string

"},{"location":"aea-framework-documentation/api/crypto/base/#public_key","title":"public_key","text":"
@property\n@abstractmethod\ndef public_key() -> str\n

Return a public key.

Returns:

a public key string

"},{"location":"aea-framework-documentation/api/crypto/base/#address","title":"address","text":"
@property\n@abstractmethod\ndef address() -> str\n

Return the address.

Returns:

an address string

"},{"location":"aea-framework-documentation/api/crypto/base/#sign_message","title":"sign_message","text":"
@abstractmethod\ndef sign_message(message: bytes, is_deprecated_mode: bool = False) -> str\n

Sign a message in bytes string form.

Arguments:

  • message: the message to be signed
  • is_deprecated_mode: if the deprecated signing is used

Returns:

signature of the message in string form

"},{"location":"aea-framework-documentation/api/crypto/base/#sign_transaction","title":"sign_transaction","text":"
@abstractmethod\ndef sign_transaction(transaction: JSONLike) -> JSONLike\n

Sign a transaction in dict form.

Arguments:

  • transaction: the transaction to be signed

Returns:

signed transaction

"},{"location":"aea-framework-documentation/api/crypto/base/#load","title":"load","text":"
@classmethod\ndef load(cls, private_key_file: str, password: Optional[str] = None) -> str\n

Load private key from file.

Arguments:

  • private_key_file: the file where the key is stored.
  • password: the password to encrypt/decrypt the private key.

Returns:

private_key in hex string format

"},{"location":"aea-framework-documentation/api/crypto/base/#dump","title":"dump","text":"
def dump(private_key_file: str, password: Optional[str] = None) -> None\n

Dump private key to file.

Arguments:

  • private_key_file: the file where the key is stored.
  • password: the password to encrypt/decrypt the private key.

"},{"location":"aea-framework-documentation/api/crypto/base/#encrypt","title":"encrypt","text":"
@abstractmethod\ndef encrypt(password: str) -> str\n

Encrypt the private key and return in json.

Arguments:

  • password: the password to decrypt.

Returns:

json string containing encrypted private key.

"},{"location":"aea-framework-documentation/api/crypto/base/#decrypt","title":"decrypt","text":"
@classmethod\n@abstractmethod\ndef decrypt(cls, keyfile_json: str, password: str) -> str\n

Decrypt the private key and return in raw form.

Arguments:

  • keyfile_json: json string containing encrypted private key.
  • password: the password to decrypt.

Returns:

the raw private key.

"},{"location":"aea-framework-documentation/api/crypto/base/#helper-objects","title":"Helper Objects","text":"
class Helper(ABC)\n

Interface for helper class usable as Mixin for LedgerApi or as standalone class.

"},{"location":"aea-framework-documentation/api/crypto/base/#is_transaction_settled","title":"is_transaction_settled","text":"
@staticmethod\n@abstractmethod\ndef is_transaction_settled(tx_receipt: JSONLike) -> bool\n

Check whether a transaction is settled or not.

Arguments:

  • tx_receipt: the receipt associated to the transaction.

Returns:

True if the transaction has been settled, False o/w.

"},{"location":"aea-framework-documentation/api/crypto/base/#is_transaction_valid","title":"is_transaction_valid","text":"
@staticmethod\n@abstractmethod\ndef is_transaction_valid(tx: JSONLike, seller: Address, client: Address,\ntx_nonce: str, amount: int) -> bool\n

Check whether a transaction is valid or not.

Arguments:

  • tx: the transaction.
  • seller: the address of the seller.
  • client: the address of the client.
  • tx_nonce: the transaction nonce.
  • amount: the amount we expect to get from the transaction.

Returns:

True if the random_message is equals to tx['input']

"},{"location":"aea-framework-documentation/api/crypto/base/#get_contract_address","title":"get_contract_address","text":"
@staticmethod\n@abstractmethod\ndef get_contract_address(tx_receipt: JSONLike) -> Optional[str]\n

Get the contract address from a transaction receipt.

Arguments:

  • tx_receipt: the transaction digest

Returns:

the contract address if successful

"},{"location":"aea-framework-documentation/api/crypto/base/#generate_tx_nonce","title":"generate_tx_nonce","text":"
@staticmethod\n@abstractmethod\ndef generate_tx_nonce(seller: Address, client: Address) -> str\n

Generate a unique hash to distinguish transactions with the same terms.

Arguments:

  • seller: the address of the seller.
  • client: the address of the client.

Returns:

return the hash in hex.

"},{"location":"aea-framework-documentation/api/crypto/base/#get_address_from_public_key","title":"get_address_from_public_key","text":"
@classmethod\n@abstractmethod\ndef get_address_from_public_key(cls, public_key: str) -> str\n

Get the address from the public key.

Arguments:

  • public_key: the public key

Returns:

str

"},{"location":"aea-framework-documentation/api/crypto/base/#recover_message","title":"recover_message","text":"
@classmethod\n@abstractmethod\ndef recover_message(cls,\nmessage: bytes,\nsignature: str,\nis_deprecated_mode: bool = False) -> Tuple[Address, ...]\n

Recover the addresses from the hash.

Arguments:

  • message: the message we expect
  • signature: the transaction signature
  • is_deprecated_mode: if the deprecated signing was used

Returns:

the recovered addresses

"},{"location":"aea-framework-documentation/api/crypto/base/#recover_public_keys_from_message","title":"recover_public_keys_from_message","text":"
@classmethod\n@abstractmethod\ndef recover_public_keys_from_message(\ncls,\nmessage: bytes,\nsignature: str,\nis_deprecated_mode: bool = False) -> Tuple[str, ...]\n

Get the public key used to produce the signature of the message

Arguments:

  • message: raw bytes used to produce signature
  • signature: signature of the message
  • is_deprecated_mode: if the deprecated signing was used

Returns:

the recovered public keys

"},{"location":"aea-framework-documentation/api/crypto/base/#get_hash","title":"get_hash","text":"
@staticmethod\n@abstractmethod\ndef get_hash(message: bytes) -> str\n

Get the hash of a message.

Arguments:

  • message: the message to be hashed.

Returns:

the hash of the message.

"},{"location":"aea-framework-documentation/api/crypto/base/#is_valid_address","title":"is_valid_address","text":"
@classmethod\n@abstractmethod\ndef is_valid_address(cls, address: Address) -> bool\n

Check if the address is valid.

Arguments:

  • address: the address to validate

"},{"location":"aea-framework-documentation/api/crypto/base/#load_contract_interface","title":"load_contract_interface","text":"
@classmethod\n@abstractmethod\ndef load_contract_interface(cls, file_path: Path) -> Dict[str, str]\n

Load contract interface.

Arguments:

  • file_path: the file path to the interface

Returns:

the interface

"},{"location":"aea-framework-documentation/api/crypto/base/#ledgerapi-objects","title":"LedgerApi Objects","text":"
class LedgerApi(Helper, ABC)\n

Interface for ledger APIs.

"},{"location":"aea-framework-documentation/api/crypto/base/#api","title":"api","text":"
@property\n@abstractmethod\ndef api() -> Any\n

Get the underlying API object.

This can be used for low-level operations with the concrete ledger APIs. If there is no such object, return None.

"},{"location":"aea-framework-documentation/api/crypto/base/#get_balance","title":"get_balance","text":"
@abstractmethod\ndef get_balance(address: Address) -> Optional[int]\n

Get the balance of a given account.

This usually takes the form of a web request to be waited synchronously.

Arguments:

  • address: the address.

Returns:

the balance.

"},{"location":"aea-framework-documentation/api/crypto/base/#get_state","title":"get_state","text":"
@abstractmethod\ndef get_state(callable_name: str, *args: Any,\n**kwargs: Any) -> Optional[JSONLike]\n

Call a specified function on the underlying ledger API.

This usually takes the form of a web request to be waited synchronously.

Arguments:

  • callable_name: the name of the API function to be called.
  • args: the positional arguments for the API function.
  • kwargs: the keyword arguments for the API function.

Returns:

the ledger API response.

"},{"location":"aea-framework-documentation/api/crypto/base/#get_transfer_transaction","title":"get_transfer_transaction","text":"
@abstractmethod\ndef get_transfer_transaction(sender_address: Address,\ndestination_address: Address, amount: int,\ntx_fee: int, tx_nonce: str,\n**kwargs: Any) -> Optional[JSONLike]\n

Submit a transfer transaction to the ledger.

Arguments:

  • sender_address: the sender address of the payer.
  • destination_address: the destination address of the payee.
  • amount: the amount of wealth to be transferred.
  • tx_fee: the transaction fee.
  • tx_nonce: verifies the authenticity of the tx
  • kwargs: the keyword arguments.

Returns:

the transfer transaction

"},{"location":"aea-framework-documentation/api/crypto/base/#send_signed_transaction","title":"send_signed_transaction","text":"
@abstractmethod\ndef send_signed_transaction(tx_signed: JSONLike) -> Optional[str]\n

Send a signed transaction and wait for confirmation.

Use keyword arguments for the specifying the signed transaction payload.

Arguments:

  • tx_signed: the signed transaction

"},{"location":"aea-framework-documentation/api/crypto/base/#get_transaction_receipt","title":"get_transaction_receipt","text":"
@abstractmethod\ndef get_transaction_receipt(tx_digest: str) -> Optional[JSONLike]\n

Get the transaction receipt for a transaction digest.

Arguments:

  • tx_digest: the digest associated to the transaction.

Returns:

the tx receipt, if present

"},{"location":"aea-framework-documentation/api/crypto/base/#get_transaction","title":"get_transaction","text":"
@abstractmethod\ndef get_transaction(tx_digest: str) -> Optional[JSONLike]\n

Get the transaction for a transaction digest.

Arguments:

  • tx_digest: the digest associated to the transaction.

Returns:

the tx, if present

"},{"location":"aea-framework-documentation/api/crypto/base/#get_contract_instance","title":"get_contract_instance","text":"
@abstractmethod\ndef get_contract_instance(contract_interface: Dict[str, str],\ncontract_address: Optional[str] = None) -> Any\n

Get the instance of a contract.

Arguments:

  • contract_interface: the contract interface.
  • contract_address: the contract address.

Returns:

the contract instance

"},{"location":"aea-framework-documentation/api/crypto/base/#get_deploy_transaction","title":"get_deploy_transaction","text":"
@abstractmethod\ndef get_deploy_transaction(contract_interface: Dict[str, str],\ndeployer_address: Address,\n**kwargs: Any) -> Optional[JSONLike]\n

Get the transaction to deploy the smart contract.

Arguments:

  • contract_interface: the contract interface.
  • deployer_address: The address that will deploy the contract.
  • kwargs: the keyword arguments.

Returns:

tx: the transaction dictionary.

"},{"location":"aea-framework-documentation/api/crypto/base/#update_with_gas_estimate","title":"update_with_gas_estimate","text":"
@abstractmethod\ndef update_with_gas_estimate(transaction: JSONLike) -> JSONLike\n

Attempts to update the transaction with a gas estimate

Arguments:

  • transaction: the transaction

Returns:

the updated transaction

"},{"location":"aea-framework-documentation/api/crypto/base/#faucetapi-objects","title":"FaucetApi Objects","text":"
class FaucetApi(ABC)\n

Interface for testnet faucet APIs.

"},{"location":"aea-framework-documentation/api/crypto/base/#get_wealth","title":"get_wealth","text":"
@abstractmethod\ndef get_wealth(address: Address, url: Optional[str] = None) -> None\n

Get wealth from the faucet for the provided address.

Arguments:

  • address: the address.
  • url: the url

Returns:

None

"},{"location":"aea-framework-documentation/api/crypto/helpers/","title":"Helpers","text":""},{"location":"aea-framework-documentation/api/crypto/helpers/#aeacryptohelpers","title":"aea.crypto.helpers","text":"

Module wrapping the helpers of public and private key cryptography.

"},{"location":"aea-framework-documentation/api/crypto/helpers/#try_validate_private_key_path","title":"try_validate_private_key_path","text":"
def try_validate_private_key_path(ledger_id: str,\nprivate_key_path: str,\npassword: Optional[str] = None) -> None\n

Try validate a private key path.

Arguments:

  • ledger_id: one of 'fetchai', 'ethereum'
  • private_key_path: the path to the private key.
  • password: the password to encrypt/decrypt the private key.

Raises:

  • None: ValueError if the identifier is invalid.

"},{"location":"aea-framework-documentation/api/crypto/helpers/#create_private_key","title":"create_private_key","text":"
def create_private_key(ledger_id: str,\nprivate_key_file: str,\npassword: Optional[str] = None) -> None\n

Create a private key for the specified ledger identifier.

Arguments:

  • ledger_id: the ledger identifier.
  • private_key_file: the private key file.
  • password: the password to encrypt/decrypt the private key.

Raises:

  • None: ValueError if the identifier is invalid.

"},{"location":"aea-framework-documentation/api/crypto/helpers/#try_generate_testnet_wealth","title":"try_generate_testnet_wealth","text":"
def try_generate_testnet_wealth(identifier: str,\naddress: str,\nurl: Optional[str] = None,\n_sync: bool = True) -> None\n

Try generate wealth on a testnet.

Arguments:

  • identifier: the identifier of the ledger
  • address: the address to check for
  • url: the url
  • _sync: whether to wait to sync or not; currently unused

"},{"location":"aea-framework-documentation/api/crypto/helpers/#private_key_verify","title":"private_key_verify","text":"
def private_key_verify(aea_conf: AgentConfig,\naea_project_path: Path,\npassword: Optional[str] = None) -> None\n

Check key.

Arguments:

  • aea_conf: AgentConfig
  • aea_project_path: Path, where project placed.
  • password: the password to encrypt/decrypt the private key.

"},{"location":"aea-framework-documentation/api/crypto/helpers/#make_certificate","title":"make_certificate","text":"
def make_certificate(ledger_id: str,\ncrypto_private_key_path: str,\nmessage: bytes,\noutput_path: str,\npassword: Optional[str] = None) -> str\n

Create certificate.

Arguments:

  • ledger_id: the ledger id
  • crypto_private_key_path: the path to the private key.
  • message: the message to be signed.
  • output_path: the location where to save the certificate.
  • password: the password to encrypt/decrypt the private keys.

Returns:

the signature/certificate

"},{"location":"aea-framework-documentation/api/crypto/helpers/#get_wallet_from_agent_config","title":"get_wallet_from_agent_config","text":"
def get_wallet_from_agent_config(agent_config: AgentConfig,\npassword: Optional[str] = None) -> Wallet\n

Get wallet from agent_cofig provided.

Arguments:

  • agent_config: the agent configuration object
  • password: the password to encrypt/decrypt the private keys.

Returns:

wallet

"},{"location":"aea-framework-documentation/api/crypto/helpers/#decrypterror-objects","title":"DecryptError Objects","text":"
class DecryptError(ValueError)\n

Error on bytes decryption with password.

"},{"location":"aea-framework-documentation/api/crypto/helpers/#__init__","title":"__init__","text":"
def __init__(msg: Optional[str] = None) -> None\n

Init exception.

"},{"location":"aea-framework-documentation/api/crypto/helpers/#keyisincorrect-objects","title":"KeyIsIncorrect Objects","text":"
class KeyIsIncorrect(ValueError)\n

Error decoding hex string to bytes for private key.

"},{"location":"aea-framework-documentation/api/crypto/helpers/#hex_to_bytes_for_key","title":"hex_to_bytes_for_key","text":"
def hex_to_bytes_for_key(data: str) -> bytes\n

Convert hex string to bytes with error handling.

"},{"location":"aea-framework-documentation/api/crypto/ledger_apis/","title":"LedgerApis","text":""},{"location":"aea-framework-documentation/api/crypto/ledger_apis/#aeacryptoledger_apis","title":"aea.crypto.ledger_apis","text":"

Module wrapping all the public and private keys cryptography.

"},{"location":"aea-framework-documentation/api/crypto/ledger_apis/#ledgerapis-objects","title":"LedgerApis Objects","text":"
class LedgerApis()\n

Store all the ledger apis we initialise.

"},{"location":"aea-framework-documentation/api/crypto/ledger_apis/#has_ledger","title":"has_ledger","text":"
@staticmethod\ndef has_ledger(identifier: str) -> bool\n

Check if it has the api.

"},{"location":"aea-framework-documentation/api/crypto/ledger_apis/#get_api","title":"get_api","text":"
@classmethod\ndef get_api(cls, identifier: str) -> LedgerApi\n

Get the ledger API.

"},{"location":"aea-framework-documentation/api/crypto/ledger_apis/#get_balance","title":"get_balance","text":"
@classmethod\ndef get_balance(cls, identifier: str, address: str) -> Optional[int]\n

Get the token balance.

Arguments:

  • identifier: the identifier of the ledger
  • address: the address to check for

Returns:

the token balance

"},{"location":"aea-framework-documentation/api/crypto/ledger_apis/#get_transfer_transaction","title":"get_transfer_transaction","text":"
@classmethod\ndef get_transfer_transaction(cls, identifier: str, sender_address: str,\ndestination_address: str, amount: int,\ntx_fee: int, tx_nonce: str,\n**kwargs: Any) -> Optional[Any]\n

Get a transaction to transfer from self to destination.

Arguments:

  • identifier: the identifier of the ledger
  • sender_address: the address of the sender
  • destination_address: the address of the receiver
  • amount: the amount
  • tx_nonce: verifies the authenticity of the tx
  • tx_fee: the tx fee
  • kwargs: the keyword arguments.

Returns:

tx

"},{"location":"aea-framework-documentation/api/crypto/ledger_apis/#send_signed_transaction","title":"send_signed_transaction","text":"
@classmethod\ndef send_signed_transaction(cls, identifier: str,\ntx_signed: Any) -> Optional[str]\n

Send a signed transaction and wait for confirmation.

Arguments:

  • identifier: the identifier of the ledger
  • tx_signed: the signed transaction

Returns:

the tx_digest, if present

"},{"location":"aea-framework-documentation/api/crypto/ledger_apis/#get_transaction_receipt","title":"get_transaction_receipt","text":"
@classmethod\ndef get_transaction_receipt(cls, identifier: str,\ntx_digest: str) -> Optional[Any]\n

Get the transaction receipt for a transaction digest.

Arguments:

  • identifier: the identifier of the ledger
  • tx_digest: the digest associated to the transaction.

Returns:

the tx receipt, if present

"},{"location":"aea-framework-documentation/api/crypto/ledger_apis/#get_transaction","title":"get_transaction","text":"
@classmethod\ndef get_transaction(cls, identifier: str, tx_digest: str) -> Optional[Any]\n

Get the transaction for a transaction digest.

Arguments:

  • identifier: the identifier of the ledger
  • tx_digest: the digest associated to the transaction.

Returns:

the tx, if present

"},{"location":"aea-framework-documentation/api/crypto/ledger_apis/#get_contract_address","title":"get_contract_address","text":"
@staticmethod\ndef get_contract_address(identifier: str,\ntx_receipt: Any) -> Optional[Address]\n

Get the contract address from a transaction receipt.

Arguments:

  • identifier: the identifier of the ledger
  • tx_receipt: the transaction receipt

Returns:

the contract address if successful

"},{"location":"aea-framework-documentation/api/crypto/ledger_apis/#is_transaction_settled","title":"is_transaction_settled","text":"
@staticmethod\ndef is_transaction_settled(identifier: str, tx_receipt: Any) -> bool\n

Check whether the transaction is settled and correct.

Arguments:

  • identifier: the identifier of the ledger
  • tx_receipt: the transaction digest

Returns:

True if correctly settled, False otherwise

"},{"location":"aea-framework-documentation/api/crypto/ledger_apis/#is_transaction_valid","title":"is_transaction_valid","text":"
@staticmethod\ndef is_transaction_valid(identifier: str, tx: Any, seller: Address,\nclient: Address, tx_nonce: str, amount: int) -> bool\n

Check whether the transaction is valid.

Arguments:

  • identifier: Ledger identifier
  • tx: the transaction
  • seller: the address of the seller.
  • client: the address of the client.
  • tx_nonce: the transaction nonce.
  • amount: the amount we expect to get from the transaction.

Returns:

True if is valid , False otherwise

"},{"location":"aea-framework-documentation/api/crypto/ledger_apis/#generate_tx_nonce","title":"generate_tx_nonce","text":"
@staticmethod\ndef generate_tx_nonce(identifier: str, seller: Address,\nclient: Address) -> str\n

Generate a random str message.

Arguments:

  • identifier: ledger identifier.
  • seller: the address of the seller.
  • client: the address of the client.

Returns:

return the hash in hex.

"},{"location":"aea-framework-documentation/api/crypto/ledger_apis/#recover_message","title":"recover_message","text":"
@staticmethod\ndef recover_message(identifier: str,\nmessage: bytes,\nsignature: str,\nis_deprecated_mode: bool = False) -> Tuple[Address, ...]\n

Recover the addresses from the hash.

Arguments:

  • identifier: ledger identifier.
  • message: the message we expect
  • signature: the transaction signature
  • is_deprecated_mode: if the deprecated signing was used

Returns:

the recovered addresses

"},{"location":"aea-framework-documentation/api/crypto/ledger_apis/#get_hash","title":"get_hash","text":"
@staticmethod\ndef get_hash(identifier: str, message: bytes) -> str\n

Get the hash of a message.

Arguments:

  • identifier: ledger identifier.
  • message: the message to be hashed.

Returns:

the hash of the message.

"},{"location":"aea-framework-documentation/api/crypto/ledger_apis/#is_valid_address","title":"is_valid_address","text":"
@staticmethod\ndef is_valid_address(identifier: str, address: Address) -> bool\n

Check if the address is valid.

Arguments:

  • identifier: ledger identifier.
  • address: the address to validate.

Returns:

whether it is a valid address or not.

"},{"location":"aea-framework-documentation/api/crypto/plugin/","title":"Plugin","text":""},{"location":"aea-framework-documentation/api/crypto/plugin/#aeacryptoplugin","title":"aea.crypto.plugin","text":"

Implementation of plug-in mechanism for cryptos.

"},{"location":"aea-framework-documentation/api/crypto/plugin/#plugin-objects","title":"Plugin Objects","text":"
class Plugin()\n

Class that implements an AEA plugin.

"},{"location":"aea-framework-documentation/api/crypto/plugin/#__init__","title":"__init__","text":"
def __init__(group: str, entry_point: EntryPoint)\n

Initialize the plugin.

Arguments:

  • group: the group the plugin belongs to.
  • entry_point: the entrypoint.

"},{"location":"aea-framework-documentation/api/crypto/plugin/#name","title":"name","text":"
@property\ndef name() -> str\n

Get the plugin identifier.

"},{"location":"aea-framework-documentation/api/crypto/plugin/#group","title":"group","text":"
@property\ndef group() -> str\n

Get the group.

"},{"location":"aea-framework-documentation/api/crypto/plugin/#attr","title":"attr","text":"
@property\ndef attr() -> str\n

Get the class name.

"},{"location":"aea-framework-documentation/api/crypto/plugin/#entry_point_path","title":"entry_point_path","text":"
@property\ndef entry_point_path() -> str\n

Get the entry point path.

"},{"location":"aea-framework-documentation/api/crypto/plugin/#load_all_plugins","title":"load_all_plugins","text":"
def load_all_plugins(is_raising_exception: bool = True) -> None\n

Load all plugins.

"},{"location":"aea-framework-documentation/api/crypto/wallet/","title":"Wallet","text":""},{"location":"aea-framework-documentation/api/crypto/wallet/#aeacryptowallet","title":"aea.crypto.wallet","text":"

Module wrapping all the public and private keys cryptography.

"},{"location":"aea-framework-documentation/api/crypto/wallet/#cryptostore-objects","title":"CryptoStore Objects","text":"
class CryptoStore()\n

Utility class to store and retrieve crypto objects.

"},{"location":"aea-framework-documentation/api/crypto/wallet/#__init__","title":"__init__","text":"
def __init__(crypto_id_to_path: Optional[Dict[str, Optional[str]]] = None,\npassword: Optional[str] = None) -> None\n

Initialize the crypto store.

Arguments:

  • crypto_id_to_path: dictionary from crypto id to an (optional) path to the private key.
  • password: the password to encrypt/decrypt the private key.

"},{"location":"aea-framework-documentation/api/crypto/wallet/#public_keys","title":"public_keys","text":"
@property\ndef public_keys() -> Dict[str, str]\n

Get the public_key dictionary.

"},{"location":"aea-framework-documentation/api/crypto/wallet/#crypto_objects","title":"crypto_objects","text":"
@property\ndef crypto_objects() -> Dict[str, Crypto]\n

Get the crypto objects (key pair).

"},{"location":"aea-framework-documentation/api/crypto/wallet/#addresses","title":"addresses","text":"
@property\ndef addresses() -> Dict[str, str]\n

Get the crypto addresses.

"},{"location":"aea-framework-documentation/api/crypto/wallet/#private_keys","title":"private_keys","text":"
@property\ndef private_keys() -> Dict[str, str]\n

Get the crypto addresses.

"},{"location":"aea-framework-documentation/api/crypto/wallet/#wallet-objects","title":"Wallet Objects","text":"
class Wallet()\n

Container for crypto objects.

The cryptos are separated into two categories:

  • main cryptos: used by the AEA for the economic side (i.e. signing transaction)
  • connection cryptos: exposed to the connection objects for encrypted communication.

"},{"location":"aea-framework-documentation/api/crypto/wallet/#__init___1","title":"__init__","text":"
def __init__(\nprivate_key_paths: Dict[str, Optional[str]],\nconnection_private_key_paths: Optional[Dict[str,\nOptional[str]]] = None,\npassword: Optional[str] = None)\n

Instantiate a wallet object.

Arguments:

  • private_key_paths: the private key paths
  • connection_private_key_paths: the private key paths for the connections.
  • password: the password to encrypt/decrypt the private key.

"},{"location":"aea-framework-documentation/api/crypto/wallet/#public_keys_1","title":"public_keys","text":"
@property\ndef public_keys() -> Dict[str, str]\n

Get the public_key dictionary.

"},{"location":"aea-framework-documentation/api/crypto/wallet/#crypto_objects_1","title":"crypto_objects","text":"
@property\ndef crypto_objects() -> Dict[str, Crypto]\n

Get the crypto objects (key pair).

"},{"location":"aea-framework-documentation/api/crypto/wallet/#addresses_1","title":"addresses","text":"
@property\ndef addresses() -> Dict[str, str]\n

Get the crypto addresses.

"},{"location":"aea-framework-documentation/api/crypto/wallet/#private_keys_1","title":"private_keys","text":"
@property\ndef private_keys() -> Dict[str, str]\n

Get the crypto addresses.

"},{"location":"aea-framework-documentation/api/crypto/wallet/#main_cryptos","title":"main_cryptos","text":"
@property\ndef main_cryptos() -> CryptoStore\n

Get the main crypto store.

"},{"location":"aea-framework-documentation/api/crypto/wallet/#connection_cryptos","title":"connection_cryptos","text":"
@property\ndef connection_cryptos() -> CryptoStore\n

Get the connection crypto store.

"},{"location":"aea-framework-documentation/api/crypto/wallet/#sign_message","title":"sign_message","text":"
def sign_message(crypto_id: str,\nmessage: bytes,\nis_deprecated_mode: bool = False) -> Optional[str]\n

Sign a message.

Arguments:

  • crypto_id: the id of the crypto
  • message: the message to be signed
  • is_deprecated_mode: what signing mode to use

Returns:

the signature of the message

"},{"location":"aea-framework-documentation/api/crypto/wallet/#sign_transaction","title":"sign_transaction","text":"
def sign_transaction(crypto_id: str, transaction: Any) -> Optional[JSONLike]\n

Sign a tx.

Arguments:

  • crypto_id: the id of the crypto
  • transaction: the transaction to be signed

Returns:

the signed tx

"},{"location":"aea-framework-documentation/api/crypto/registries/base/","title":"Base","text":""},{"location":"aea-framework-documentation/api/crypto/registries/base/#aeacryptoregistriesbase","title":"aea.crypto.registries.base","text":"

This module implements the base registry.

"},{"location":"aea-framework-documentation/api/crypto/registries/base/#itemid-objects","title":"ItemId Objects","text":"
class ItemId(RegexConstrainedString)\n

The identifier of an item class.

"},{"location":"aea-framework-documentation/api/crypto/registries/base/#name","title":"name","text":"
@property\ndef name() -> str\n

Get the id name.

"},{"location":"aea-framework-documentation/api/crypto/registries/base/#entrypoint-objects","title":"EntryPoint Objects","text":"
class EntryPoint(Generic[ItemType], RegexConstrainedString)\n

The entry point for a resource.

The regular expression matches the strings in the following format:

path.to.module:className\n

"},{"location":"aea-framework-documentation/api/crypto/registries/base/#__init__","title":"__init__","text":"
def __init__(seq: Union[\"EntryPoint\", str]) -> None\n

Initialize the entrypoint.

"},{"location":"aea-framework-documentation/api/crypto/registries/base/#import_path","title":"import_path","text":"
@property\ndef import_path() -> str\n

Get the import path.

"},{"location":"aea-framework-documentation/api/crypto/registries/base/#class_name","title":"class_name","text":"
@property\ndef class_name() -> str\n

Get the class name.

"},{"location":"aea-framework-documentation/api/crypto/registries/base/#load","title":"load","text":"
def load() -> Type[ItemType]\n

Load the item object.

Returns:

the crypto object, loaded following the spec.

"},{"location":"aea-framework-documentation/api/crypto/registries/base/#itemspec-objects","title":"ItemSpec Objects","text":"
class ItemSpec(Generic[ItemType])\n

A specification for a particular instance of an object.

"},{"location":"aea-framework-documentation/api/crypto/registries/base/#__init___1","title":"__init__","text":"
def __init__(id_: ItemId,\nentry_point: EntryPoint[ItemType],\nclass_kwargs: Optional[Dict[str, Any]] = None,\n**kwargs: Dict) -> None\n

Initialize an item specification.

Arguments:

  • id_: the id associated to this specification
  • entry_point: The Python entry_point of the environment class (e.g. module.name:Class).
  • class_kwargs: keyword arguments to be attached on the class as class variables.
  • kwargs: other custom keyword arguments.

"},{"location":"aea-framework-documentation/api/crypto/registries/base/#make","title":"make","text":"
def make(**kwargs: Any) -> ItemType\n

Instantiate an instance of the item object with appropriate arguments.

Arguments:

  • kwargs: the key word arguments

Returns:

an item

"},{"location":"aea-framework-documentation/api/crypto/registries/base/#get_class","title":"get_class","text":"
def get_class() -> Type[ItemType]\n

Get the class of the item with class variables instantiated.

Returns:

an item class

"},{"location":"aea-framework-documentation/api/crypto/registries/base/#registry-objects","title":"Registry Objects","text":"
class Registry(Generic[ItemType])\n

Registry for generic classes.

"},{"location":"aea-framework-documentation/api/crypto/registries/base/#__init___2","title":"__init__","text":"
def __init__() -> None\n

Initialize the registry.

"},{"location":"aea-framework-documentation/api/crypto/registries/base/#supported_ids","title":"supported_ids","text":"
@property\ndef supported_ids() -> Set[str]\n

Get the supported item ids.

"},{"location":"aea-framework-documentation/api/crypto/registries/base/#register","title":"register","text":"
def register(id_: Union[ItemId, str],\nentry_point: Union[EntryPoint[ItemType], str],\nclass_kwargs: Optional[Dict[str, Any]] = None,\n**kwargs: Any) -> None\n

Register an item type.

Arguments:

  • id_: the identifier for the crypto type.
  • entry_point: the entry point to load the crypto object.
  • class_kwargs: keyword arguments to be attached on the class as class variables.
  • kwargs: arguments to provide to the crypto class.

"},{"location":"aea-framework-documentation/api/crypto/registries/base/#make_1","title":"make","text":"
def make(id_: Union[ItemId, str],\nmodule: Optional[str] = None,\n**kwargs: Any) -> ItemType\n

Create an instance of the associated type item id.

Arguments:

  • id_: the id of the item class. Make sure it has been registered earlier before calling this function.
  • module: dotted path to a module. whether a module should be loaded before creating the object. this argument is useful when the item might not be registered beforehand, and loading the specified module will make the registration. E.g. suppose the call to 'register' for a custom object is located in some_package/init.py. By providing module=\"some_package\", the call to 'register' in such module gets triggered and the make can then find the identifier.
  • kwargs: keyword arguments to be forwarded to the object.

Returns:

the new item instance.

"},{"location":"aea-framework-documentation/api/crypto/registries/base/#make_cls","title":"make_cls","text":"
def make_cls(id_: Union[ItemId, str],\nmodule: Optional[str] = None) -> Type[ItemType]\n

Load a class of the associated type item id.

Arguments:

  • id_: the id of the item class. Make sure it has been registered earlier before calling this function.
  • module: dotted path to a module. whether a module should be loaded before creating the object. this argument is useful when the item might not be registered beforehand, and loading the specified module will make the registration. E.g. suppose the call to 'register' for a custom object is located in some_package/init.py. By providing module=\"some_package\", the call to 'register' in such module gets triggered and the make can then find the identifier.

Returns:

the new item class.

"},{"location":"aea-framework-documentation/api/crypto/registries/base/#has_spec","title":"has_spec","text":"
def has_spec(item_id: ItemId) -> bool\n

Check whether there exist a spec associated with an item id.

Arguments:

  • item_id: the item identifier.

Returns:

True if it is registered, False otherwise.

"},{"location":"aea-framework-documentation/api/decision_maker/base/","title":"Base","text":""},{"location":"aea-framework-documentation/api/decision_maker/base/#aeadecision_makerbase","title":"aea.decision_maker.base","text":"

This module contains the decision maker class.

"},{"location":"aea-framework-documentation/api/decision_maker/base/#ownershipstate-objects","title":"OwnershipState Objects","text":"
class OwnershipState(ABC)\n

Represent the ownership state of an agent (can proxy a ledger).

"},{"location":"aea-framework-documentation/api/decision_maker/base/#set","title":"set","text":"
@abstractmethod\ndef set(**kwargs: Any) -> None\n

Set values on the ownership state.

Arguments:

  • kwargs: the relevant keyword arguments

"},{"location":"aea-framework-documentation/api/decision_maker/base/#apply_delta","title":"apply_delta","text":"
@abstractmethod\ndef apply_delta(**kwargs: Any) -> None\n

Apply a state update to the ownership state.

This method is used to apply a raw state update without a transaction.

Arguments:

  • kwargs: the relevant keyword arguments

"},{"location":"aea-framework-documentation/api/decision_maker/base/#is_initialized","title":"is_initialized","text":"
@property\n@abstractmethod\ndef is_initialized() -> bool\n

Get the initialization status.

"},{"location":"aea-framework-documentation/api/decision_maker/base/#is_affordable_transaction","title":"is_affordable_transaction","text":"
@abstractmethod\ndef is_affordable_transaction(terms: Terms) -> bool\n

Check if the transaction is affordable (and consistent).

Arguments:

  • terms: the transaction terms

Returns:

True if the transaction is legal wrt the current state, false otherwise.

"},{"location":"aea-framework-documentation/api/decision_maker/base/#apply_transactions","title":"apply_transactions","text":"
@abstractmethod\ndef apply_transactions(list_of_terms: List[Terms]) -> \"OwnershipState\"\n

Apply a list of transactions to (a copy of) the current state.

Arguments:

  • list_of_terms: the sequence of transaction terms.

Returns:

the final state.

"},{"location":"aea-framework-documentation/api/decision_maker/base/#__copy__","title":"__copy__","text":"
@abstractmethod\ndef __copy__() -> \"OwnershipState\"\n

Copy the object.

"},{"location":"aea-framework-documentation/api/decision_maker/base/#preferences-objects","title":"Preferences Objects","text":"
class Preferences(ABC)\n

Class to represent the preferences.

"},{"location":"aea-framework-documentation/api/decision_maker/base/#set_1","title":"set","text":"
@abstractmethod\ndef set(**kwargs: Any) -> None\n

Set values on the preferences.

Arguments:

  • kwargs: the relevant key word arguments

"},{"location":"aea-framework-documentation/api/decision_maker/base/#is_initialized_1","title":"is_initialized","text":"
@property\n@abstractmethod\ndef is_initialized() -> bool\n

Get the initialization status.

Returns True if exchange_params_by_currency_id and utility_params_by_good_id are not None.

"},{"location":"aea-framework-documentation/api/decision_maker/base/#marginal_utility","title":"marginal_utility","text":"
@abstractmethod\ndef marginal_utility(ownership_state: OwnershipState, **kwargs: Any) -> float\n

Compute the marginal utility.

Arguments:

  • ownership_state: the ownership state against which to compute the marginal utility.
  • kwargs: optional keyword arguments

Returns:

the marginal utility score

"},{"location":"aea-framework-documentation/api/decision_maker/base/#utility_diff_from_transaction","title":"utility_diff_from_transaction","text":"
@abstractmethod\ndef utility_diff_from_transaction(ownership_state: OwnershipState,\nterms: Terms) -> float\n

Simulate a transaction and get the resulting utility difference (taking into account the fee).

Arguments:

  • ownership_state: the ownership state against which to apply the transaction.
  • terms: the transaction terms.

Returns:

the score.

"},{"location":"aea-framework-documentation/api/decision_maker/base/#__copy___1","title":"__copy__","text":"
@abstractmethod\ndef __copy__() -> \"Preferences\"\n

Copy the object.

"},{"location":"aea-framework-documentation/api/decision_maker/base/#protectedqueue-objects","title":"ProtectedQueue Objects","text":"
class ProtectedQueue(Queue)\n

A wrapper of a queue to protect which object can read from it.

"},{"location":"aea-framework-documentation/api/decision_maker/base/#__init__","title":"__init__","text":"
def __init__(access_code: str) -> None\n

Initialize the protected queue.

Arguments:

  • access_code: the access code to read from the queue

"},{"location":"aea-framework-documentation/api/decision_maker/base/#put","title":"put","text":"
def put(internal_message: Optional[Message],\nblock: bool = True,\ntimeout: Optional[float] = None) -> None\n

Put an internal message on the queue.

If optional args block is true and timeout is None (the default), block if necessary until a free slot is available. If timeout is a positive number, it blocks at most timeout seconds and raises the Full exception if no free slot was available within that time. Otherwise (block is false), put an item on the queue if a free slot is immediately available, else raise the Full exception (timeout is ignored in that case).

Arguments:

  • internal_message: the internal message to put on the queue
  • block: whether to block or not
  • timeout: timeout on block

Raises:

  • None: ValueError, if the item is not an internal message

"},{"location":"aea-framework-documentation/api/decision_maker/base/#put_nowait","title":"put_nowait","text":"
def put_nowait(internal_message: Optional[Message]) -> None\n

Put an internal message on the queue.

Equivalent to put(item, False).

Arguments:

  • internal_message: the internal message to put on the queue

Raises:

  • None: ValueError, if the item is not an internal message

"},{"location":"aea-framework-documentation/api/decision_maker/base/#get","title":"get","text":"
def get(block: bool = True, timeout: Optional[float] = None) -> None\n

Inaccessible get method.

Arguments:

  • block: whether to block or not
  • timeout: timeout on block

Raises:

  • None: ValueError, access not permitted.

"},{"location":"aea-framework-documentation/api/decision_maker/base/#get_nowait","title":"get_nowait","text":"
def get_nowait() -> None\n

Inaccessible get_nowait method.

Raises:

  • None: ValueError, access not permitted.

"},{"location":"aea-framework-documentation/api/decision_maker/base/#protected_get","title":"protected_get","text":"
def protected_get(access_code: str,\nblock: bool = True,\ntimeout: Optional[float] = None) -> Optional[Message]\n

Access protected get method.

Arguments:

  • access_code: the access code
  • block: If optional args block is true and timeout is None (the default), block if necessary until an item is available.
  • timeout: If timeout is a positive number, it blocks at most timeout seconds and raises the Empty exception if no item was available within that time.

Raises:

  • None: ValueError, if caller is not permitted

Returns:

internal message

"},{"location":"aea-framework-documentation/api/decision_maker/base/#decisionmakerhandler-objects","title":"DecisionMakerHandler Objects","text":"
class DecisionMakerHandler(WithLogger, ABC)\n

This class implements the decision maker.

"},{"location":"aea-framework-documentation/api/decision_maker/base/#__init___1","title":"__init__","text":"
def __init__(identity: Identity, wallet: Wallet, config: Dict[str, Any],\n**kwargs: Any) -> None\n

Initialize the decision maker handler.

Arguments:

  • identity: the identity
  • wallet: the wallet
  • config: the user defined configuration of the handler
  • kwargs: the key word arguments

"},{"location":"aea-framework-documentation/api/decision_maker/base/#agent_name","title":"agent_name","text":"
@property\ndef agent_name() -> str\n

Get the agent name.

"},{"location":"aea-framework-documentation/api/decision_maker/base/#identity","title":"identity","text":"
@property\ndef identity() -> Identity\n

Get identity of the agent.

"},{"location":"aea-framework-documentation/api/decision_maker/base/#wallet","title":"wallet","text":"
@property\ndef wallet() -> Wallet\n

Get wallet of the agent.

"},{"location":"aea-framework-documentation/api/decision_maker/base/#config","title":"config","text":"
@property\ndef config() -> Dict[str, Any]\n

Get user defined configuration

"},{"location":"aea-framework-documentation/api/decision_maker/base/#context","title":"context","text":"
@property\ndef context() -> SimpleNamespace\n

Get the context.

"},{"location":"aea-framework-documentation/api/decision_maker/base/#message_out_queue","title":"message_out_queue","text":"
@property\ndef message_out_queue() -> AsyncFriendlyQueue\n

Get (out) queue.

"},{"location":"aea-framework-documentation/api/decision_maker/base/#handle","title":"handle","text":"
@abstractmethod\ndef handle(message: Message) -> None\n

Handle an internal message from the skills.

Arguments:

  • message: the internal message

"},{"location":"aea-framework-documentation/api/decision_maker/base/#decisionmaker-objects","title":"DecisionMaker Objects","text":"
class DecisionMaker(WithLogger)\n

This class implements the decision maker.

"},{"location":"aea-framework-documentation/api/decision_maker/base/#__init___2","title":"__init__","text":"
def __init__(decision_maker_handler: DecisionMakerHandler) -> None\n

Initialize the decision maker.

Arguments:

  • decision_maker_handler: the decision maker handler

"},{"location":"aea-framework-documentation/api/decision_maker/base/#agent_name_1","title":"agent_name","text":"
@property\ndef agent_name() -> str\n

Get the agent name.

"},{"location":"aea-framework-documentation/api/decision_maker/base/#message_in_queue","title":"message_in_queue","text":"
@property\ndef message_in_queue() -> ProtectedQueue\n

Get (in) queue.

"},{"location":"aea-framework-documentation/api/decision_maker/base/#message_out_queue_1","title":"message_out_queue","text":"
@property\ndef message_out_queue() -> AsyncFriendlyQueue\n

Get (out) queue.

"},{"location":"aea-framework-documentation/api/decision_maker/base/#decision_maker_handler","title":"decision_maker_handler","text":"
@property\ndef decision_maker_handler() -> DecisionMakerHandler\n

Get the decision maker handler.

"},{"location":"aea-framework-documentation/api/decision_maker/base/#start","title":"start","text":"
def start() -> None\n

Start the decision maker.

"},{"location":"aea-framework-documentation/api/decision_maker/base/#stop","title":"stop","text":"
def stop() -> None\n

Stop the decision maker.

"},{"location":"aea-framework-documentation/api/decision_maker/base/#execute","title":"execute","text":"
def execute() -> None\n

Execute the decision maker.

Performs the following while not stopped:

  • gets internal messages from the in queue and calls handle() on them

"},{"location":"aea-framework-documentation/api/decision_maker/base/#handle_1","title":"handle","text":"
def handle(message: Message) -> None\n

Handle an internal message from the skills.

Arguments:

  • message: the internal message
"},{"location":"aea-framework-documentation/api/decision_maker/default/","title":"Default","text":""},{"location":"aea-framework-documentation/api/decision_maker/default/#aeadecision_makerdefault","title":"aea.decision_maker.default","text":"

This module contains the decision maker class.

"},{"location":"aea-framework-documentation/api/decision_maker/default/#decisionmakerhandler-objects","title":"DecisionMakerHandler Objects","text":"
class DecisionMakerHandler(BaseDecisionMakerHandler)\n

This class implements the decision maker.

"},{"location":"aea-framework-documentation/api/decision_maker/default/#signingdialogues-objects","title":"SigningDialogues Objects","text":"
class SigningDialogues(BaseSigningDialogues)\n

This class keeps track of all oef_search dialogues.

"},{"location":"aea-framework-documentation/api/decision_maker/default/#__init__","title":"__init__","text":"
def __init__(self_address: Address, **kwargs: Any) -> None\n

Initialize dialogues.

Arguments:

  • self_address: the address of the entity for whom dialogues are maintained
  • kwargs: the keyword arguments

"},{"location":"aea-framework-documentation/api/decision_maker/default/#__init___1","title":"__init__","text":"
def __init__(identity: Identity, wallet: Wallet, config: Dict[str,\nAny]) -> None\n

Initialize the decision maker.

Arguments:

  • identity: the identity
  • wallet: the wallet
  • config: the user defined configuration of the handler

"},{"location":"aea-framework-documentation/api/decision_maker/default/#handle","title":"handle","text":"
def handle(message: Message) -> None\n

Handle an internal message from the skills.

Arguments:

  • message: the internal message
"},{"location":"aea-framework-documentation/api/decision_maker/gop/","title":"GOP","text":""},{"location":"aea-framework-documentation/api/decision_maker/gop/#aeadecision_makergop","title":"aea.decision_maker.gop","text":"

This module contains the decision maker class.

"},{"location":"aea-framework-documentation/api/decision_maker/gop/#goalpursuitreadiness-objects","title":"GoalPursuitReadiness Objects","text":"
class GoalPursuitReadiness()\n

The goal pursuit readiness.

"},{"location":"aea-framework-documentation/api/decision_maker/gop/#status-objects","title":"Status Objects","text":"
class Status(Enum)\n

The enum of the readiness status.

In particular, it can be one of the following:

  • Status.READY: when the agent is ready to pursuit its goal
  • Status.NOT_READY: when the agent is not ready to pursuit its goal

"},{"location":"aea-framework-documentation/api/decision_maker/gop/#__init__","title":"__init__","text":"
def __init__() -> None\n

Instantiate the goal pursuit readiness.

"},{"location":"aea-framework-documentation/api/decision_maker/gop/#is_ready","title":"is_ready","text":"
@property\ndef is_ready() -> bool\n

Get the readiness.

"},{"location":"aea-framework-documentation/api/decision_maker/gop/#update","title":"update","text":"
def update(new_status: Status) -> None\n

Update the goal pursuit readiness.

Arguments:

  • new_status: the new status

"},{"location":"aea-framework-documentation/api/decision_maker/gop/#ownershipstate-objects","title":"OwnershipState Objects","text":"
class OwnershipState(BaseOwnershipState)\n

Represent the ownership state of an agent (can proxy a ledger).

"},{"location":"aea-framework-documentation/api/decision_maker/gop/#__init___1","title":"__init__","text":"
def __init__() -> None\n

Instantiate an ownership state object.

"},{"location":"aea-framework-documentation/api/decision_maker/gop/#set","title":"set","text":"
def set(amount_by_currency_id: CurrencyHoldings = None,\nquantities_by_good_id: GoodHoldings = None,\n**kwargs: Any) -> None\n

Set values on the ownership state.

Arguments:

  • amount_by_currency_id: the currency endowment of the agent in this state.
  • quantities_by_good_id: the good endowment of the agent in this state.
  • kwargs: the keyword arguments.

"},{"location":"aea-framework-documentation/api/decision_maker/gop/#apply_delta","title":"apply_delta","text":"
def apply_delta(delta_amount_by_currency_id: Dict[str, int] = None,\ndelta_quantities_by_good_id: Dict[str, int] = None,\n**kwargs: Any) -> None\n

Apply a state update to the ownership state.

This method is used to apply a raw state update without a transaction.

Arguments:

  • delta_amount_by_currency_id: the delta in the currency amounts
  • delta_quantities_by_good_id: the delta in the quantities by good
  • kwargs: the keyword arguments

"},{"location":"aea-framework-documentation/api/decision_maker/gop/#is_initialized","title":"is_initialized","text":"
@property\ndef is_initialized() -> bool\n

Get the initialization status.

"},{"location":"aea-framework-documentation/api/decision_maker/gop/#amount_by_currency_id","title":"amount_by_currency_id","text":"
@property\ndef amount_by_currency_id() -> CurrencyHoldings\n

Get currency holdings in this state.

"},{"location":"aea-framework-documentation/api/decision_maker/gop/#quantities_by_good_id","title":"quantities_by_good_id","text":"
@property\ndef quantities_by_good_id() -> GoodHoldings\n

Get good holdings in this state.

"},{"location":"aea-framework-documentation/api/decision_maker/gop/#is_affordable_transaction","title":"is_affordable_transaction","text":"
def is_affordable_transaction(terms: Terms) -> bool\n

Check if the transaction is affordable (and consistent).

E.g. check that the agent state has enough money if it is a buyer or enough holdings if it is a seller. Note, the agent is the sender of the transaction message by design.

Arguments:

  • terms: the transaction terms

Returns:

True if the transaction is legal wrt the current state, false otherwise.

"},{"location":"aea-framework-documentation/api/decision_maker/gop/#is_affordable","title":"is_affordable","text":"
def is_affordable(terms: Terms) -> bool\n

Check if the tx is affordable.

Arguments:

  • terms: the transaction terms

Returns:

whether the transaction is affordable or not

"},{"location":"aea-framework-documentation/api/decision_maker/gop/#update_1","title":"update","text":"
def update(terms: Terms) -> None\n

Update the agent state from a transaction.

Arguments:

  • terms: the transaction terms

"},{"location":"aea-framework-documentation/api/decision_maker/gop/#apply_transactions","title":"apply_transactions","text":"
def apply_transactions(list_of_terms: List[Terms]) -> \"OwnershipState\"\n

Apply a list of transactions to (a copy of) the current state.

Arguments:

  • list_of_terms: the sequence of transaction terms.

Returns:

the final state.

"},{"location":"aea-framework-documentation/api/decision_maker/gop/#__copy__","title":"__copy__","text":"
def __copy__() -> \"OwnershipState\"\n

Copy the object.

"},{"location":"aea-framework-documentation/api/decision_maker/gop/#preferences-objects","title":"Preferences Objects","text":"
class Preferences(BasePreferences)\n

Class to represent the preferences.

"},{"location":"aea-framework-documentation/api/decision_maker/gop/#__init___2","title":"__init__","text":"
def __init__() -> None\n

Instantiate an agent preference object.

"},{"location":"aea-framework-documentation/api/decision_maker/gop/#set_1","title":"set","text":"
def set(exchange_params_by_currency_id: ExchangeParams = None,\nutility_params_by_good_id: UtilityParams = None,\n**kwargs: Any) -> None\n

Set values on the preferences.

Arguments:

  • exchange_params_by_currency_id: the exchange params.
  • utility_params_by_good_id: the utility params for every asset.
  • kwargs: the keyword arguments.

"},{"location":"aea-framework-documentation/api/decision_maker/gop/#is_initialized_1","title":"is_initialized","text":"
@property\ndef is_initialized() -> bool\n

Get the initialization status.

Returns:

True if exchange_params_by_currency_id and utility_params_by_good_id are not None.

"},{"location":"aea-framework-documentation/api/decision_maker/gop/#exchange_params_by_currency_id","title":"exchange_params_by_currency_id","text":"
@property\ndef exchange_params_by_currency_id() -> ExchangeParams\n

Get exchange parameter for each currency.

"},{"location":"aea-framework-documentation/api/decision_maker/gop/#utility_params_by_good_id","title":"utility_params_by_good_id","text":"
@property\ndef utility_params_by_good_id() -> UtilityParams\n

Get utility parameter for each good.

"},{"location":"aea-framework-documentation/api/decision_maker/gop/#logarithmic_utility","title":"logarithmic_utility","text":"
def logarithmic_utility(quantities_by_good_id: GoodHoldings) -> float\n

Compute agent's utility given her utility function params and a good bundle.

Arguments:

  • quantities_by_good_id: the good holdings (dictionary) with the identifier (key) and quantity (value) for each good

Returns:

utility value

"},{"location":"aea-framework-documentation/api/decision_maker/gop/#linear_utility","title":"linear_utility","text":"
def linear_utility(amount_by_currency_id: CurrencyHoldings) -> float\n

Compute agent's utility given her utility function params and a currency bundle.

Arguments:

  • amount_by_currency_id: the currency holdings (dictionary) with the identifier (key) and quantity (value) for each currency

Returns:

utility value

"},{"location":"aea-framework-documentation/api/decision_maker/gop/#utility","title":"utility","text":"
def utility(quantities_by_good_id: GoodHoldings,\namount_by_currency_id: CurrencyHoldings) -> float\n

Compute the utility given the good and currency holdings.

Arguments:

  • quantities_by_good_id: the good holdings
  • amount_by_currency_id: the currency holdings

Returns:

the utility value.

"},{"location":"aea-framework-documentation/api/decision_maker/gop/#marginal_utility","title":"marginal_utility","text":"
def marginal_utility(\nownership_state: BaseOwnershipState,\ndelta_quantities_by_good_id: Optional[GoodHoldings] = None,\ndelta_amount_by_currency_id: Optional[CurrencyHoldings] = None,\n**kwargs: Any) -> float\n

Compute the marginal utility.

Arguments:

  • ownership_state: the ownership state against which to compute the marginal utility.
  • delta_quantities_by_good_id: the change in good holdings
  • delta_amount_by_currency_id: the change in money holdings
  • kwargs: the keyword arguments

Returns:

the marginal utility score

"},{"location":"aea-framework-documentation/api/decision_maker/gop/#utility_diff_from_transaction","title":"utility_diff_from_transaction","text":"
def utility_diff_from_transaction(ownership_state: BaseOwnershipState,\nterms: Terms) -> float\n

Simulate a transaction and get the resulting utility difference (taking into account the fee).

Arguments:

  • ownership_state: the ownership state against which to apply the transaction.
  • terms: the transaction terms.

Returns:

the score.

"},{"location":"aea-framework-documentation/api/decision_maker/gop/#is_utility_enhancing","title":"is_utility_enhancing","text":"
def is_utility_enhancing(ownership_state: BaseOwnershipState,\nterms: Terms) -> bool\n

Check if the tx is utility enhancing.

Arguments:

  • ownership_state: the ownership state against which to apply the transaction.
  • terms: the transaction terms

Returns:

whether the transaction is utility enhancing or not

"},{"location":"aea-framework-documentation/api/decision_maker/gop/#__copy___1","title":"__copy__","text":"
def __copy__() -> \"Preferences\"\n

Copy the object.

"},{"location":"aea-framework-documentation/api/decision_maker/gop/#decisionmakerhandler-objects","title":"DecisionMakerHandler Objects","text":"
class DecisionMakerHandler(BaseDecisionMakerHandler)\n

This class implements the decision maker.

"},{"location":"aea-framework-documentation/api/decision_maker/gop/#signingdialogues-objects","title":"SigningDialogues Objects","text":"
class SigningDialogues(BaseSigningDialogues)\n

This class keeps track of all oef_search dialogues.

"},{"location":"aea-framework-documentation/api/decision_maker/gop/#__init___3","title":"__init__","text":"
def __init__(self_address: Address, **kwargs: Any) -> None\n

Initialize dialogues.

Arguments:

  • self_address: the address of the entity for whom dialogues are maintained
  • kwargs: the keyword arguments

"},{"location":"aea-framework-documentation/api/decision_maker/gop/#stateupdatedialogues-objects","title":"StateUpdateDialogues Objects","text":"
class StateUpdateDialogues(BaseStateUpdateDialogues)\n

This class keeps track of all oef_search dialogues.

"},{"location":"aea-framework-documentation/api/decision_maker/gop/#__init___4","title":"__init__","text":"
def __init__(self_address: Address, **kwargs: Any) -> None\n

Initialize dialogues.

Arguments:

  • self_address: the address of the entity for whom dialogues are maintained
  • kwargs: the keyword arguments

"},{"location":"aea-framework-documentation/api/decision_maker/gop/#__init___5","title":"__init__","text":"
def __init__(identity: Identity, wallet: Wallet, config: Dict[str,\nAny]) -> None\n

Initialize the decision maker.

Arguments:

  • identity: the identity
  • wallet: the wallet
  • config: the user defined configuration of the handler

"},{"location":"aea-framework-documentation/api/decision_maker/gop/#handle","title":"handle","text":"
def handle(message: Message) -> None\n

Handle an internal message from the skills.

Arguments:

  • message: the internal message
"},{"location":"aea-framework-documentation/api/error_handler/base/","title":"Base","text":""},{"location":"aea-framework-documentation/api/error_handler/base/#aeaerror_handlerbase","title":"aea.error_handler.base","text":"

This module contains the abstract error handler class.

"},{"location":"aea-framework-documentation/api/error_handler/base/#abstracterrorhandler-objects","title":"AbstractErrorHandler Objects","text":"
class AbstractErrorHandler(ABC)\n

Error handler class for handling problematic envelopes.

"},{"location":"aea-framework-documentation/api/error_handler/base/#__init__","title":"__init__","text":"
def __init__(**kwargs: Any)\n

Instantiate error handler.

"},{"location":"aea-framework-documentation/api/error_handler/base/#config","title":"config","text":"
@property\ndef config() -> Dict[str, Any]\n

Get handler config.

"},{"location":"aea-framework-documentation/api/error_handler/base/#send_unsupported_protocol","title":"send_unsupported_protocol","text":"
@abstractmethod\ndef send_unsupported_protocol(envelope: Envelope, logger: Logger) -> None\n

Handle the received envelope in case the protocol is not supported.

Arguments:

  • envelope: the envelope
  • logger: the logger

Returns:

None

"},{"location":"aea-framework-documentation/api/error_handler/base/#send_decoding_error","title":"send_decoding_error","text":"
@abstractmethod\ndef send_decoding_error(envelope: Envelope, exception: Exception,\nlogger: Logger) -> None\n

Handle a decoding error.

Arguments:

  • envelope: the envelope
  • exception: the exception raised during decoding
  • logger: the logger

Returns:

None

"},{"location":"aea-framework-documentation/api/error_handler/base/#send_no_active_handler","title":"send_no_active_handler","text":"
@abstractmethod\ndef send_no_active_handler(envelope: Envelope, reason: str,\nlogger: Logger) -> None\n

Handle the received envelope in case the handler is not supported.

Arguments:

  • envelope: the envelope
  • reason: the reason for the failure
  • logger: the logger

Returns:

None

"},{"location":"aea-framework-documentation/api/error_handler/default/","title":"Default","text":""},{"location":"aea-framework-documentation/api/error_handler/default/#aeaerror_handlerdefault","title":"aea.error_handler.default","text":"

This module contains the default error handler class.

"},{"location":"aea-framework-documentation/api/error_handler/default/#errorhandler-objects","title":"ErrorHandler Objects","text":"
class ErrorHandler(AbstractErrorHandler)\n

Error handler class for handling problematic envelopes.

"},{"location":"aea-framework-documentation/api/error_handler/default/#__init__","title":"__init__","text":"
def __init__(**kwargs: Any)\n

Instantiate error handler.

"},{"location":"aea-framework-documentation/api/error_handler/default/#send_unsupported_protocol","title":"send_unsupported_protocol","text":"
def send_unsupported_protocol(envelope: Envelope, logger: Logger) -> None\n

Handle the received envelope in case the protocol is not supported.

Arguments:

  • envelope: the envelope
  • logger: the logger

"},{"location":"aea-framework-documentation/api/error_handler/default/#send_decoding_error","title":"send_decoding_error","text":"
def send_decoding_error(envelope: Envelope, exception: Exception,\nlogger: Logger) -> None\n

Handle a decoding error.

Arguments:

  • envelope: the envelope
  • exception: the exception raised during decoding
  • logger: the logger

"},{"location":"aea-framework-documentation/api/error_handler/default/#send_no_active_handler","title":"send_no_active_handler","text":"
def send_no_active_handler(envelope: Envelope, reason: str,\nlogger: Logger) -> None\n

Handle the received envelope in case the handler is not supported.

Arguments:

  • envelope: the envelope
  • reason: the reason for the failure
  • logger: the logger
"},{"location":"aea-framework-documentation/api/helpers/async_friendly_queue/","title":"Async Friendly Queue","text":""},{"location":"aea-framework-documentation/api/helpers/async_friendly_queue/#aeahelpersasync_friendly_queue","title":"aea.helpers.async_friendly_queue","text":"

This module contains the implementation of AsyncFriendlyQueue.

"},{"location":"aea-framework-documentation/api/helpers/async_friendly_queue/#asyncfriendlyqueue-objects","title":"AsyncFriendlyQueue Objects","text":"
class AsyncFriendlyQueue(queue.Queue)\n

queue.Queue with async_get method.

"},{"location":"aea-framework-documentation/api/helpers/async_friendly_queue/#__init__","title":"__init__","text":"
def __init__(*args: Any, **kwargs: Any) -> None\n

Init queue.

"},{"location":"aea-framework-documentation/api/helpers/async_friendly_queue/#put","title":"put","text":"
def put(item: Any, *args: Any, **kwargs: Any) -> None\n

Put an item into the queue.

Arguments:

  • item: item to put in the queue
  • args: similar to queue.Queue.put
  • kwargs: similar to queue.Queue.put

"},{"location":"aea-framework-documentation/api/helpers/async_friendly_queue/#get","title":"get","text":"
def get(*args: Any, **kwargs: Any) -> Any\n

Get an item into the queue.

Arguments:

  • args: similar to queue.Queue.get
  • kwargs: similar to queue.Queue.get

Returns:

similar to queue.Queue.get

"},{"location":"aea-framework-documentation/api/helpers/async_friendly_queue/#async_wait","title":"async_wait","text":"
async def async_wait() -> None\n

Wait an item appears in the queue.

Returns:

None

"},{"location":"aea-framework-documentation/api/helpers/async_friendly_queue/#async_get","title":"async_get","text":"
async def async_get() -> Any\n

Wait and get an item from the queue.

Returns:

item from queue

"},{"location":"aea-framework-documentation/api/helpers/async_utils/","title":"Async Utils","text":""},{"location":"aea-framework-documentation/api/helpers/async_utils/#aeahelpersasync_utils","title":"aea.helpers.async_utils","text":"

This module contains the misc utils for async code.

"},{"location":"aea-framework-documentation/api/helpers/async_utils/#ensure_list","title":"ensure_list","text":"
def ensure_list(value: Any) -> List\n

Return [value] or list(value) if value is a sequence.

"},{"location":"aea-framework-documentation/api/helpers/async_utils/#asyncstate-objects","title":"AsyncState Objects","text":"
class AsyncState()\n

Awaitable state.

"},{"location":"aea-framework-documentation/api/helpers/async_utils/#__init__","title":"__init__","text":"
def __init__(initial_state: Any = None,\nstates_enum: Optional[Container[Any]] = None) -> None\n

Init async state.

Arguments:

  • initial_state: state to set on start.
  • states_enum: container of valid states if not provided state not checked on set.

"},{"location":"aea-framework-documentation/api/helpers/async_utils/#set","title":"set","text":"
def set(state: Any) -> None\n

Set state.

"},{"location":"aea-framework-documentation/api/helpers/async_utils/#add_callback","title":"add_callback","text":"
def add_callback(callback_fn: Callable[[Any], None]) -> None\n

Add callback to track state changes.

Arguments:

  • callback_fn: callable object to be called on state changed.

"},{"location":"aea-framework-documentation/api/helpers/async_utils/#get","title":"get","text":"
def get() -> Any\n

Get state.

"},{"location":"aea-framework-documentation/api/helpers/async_utils/#wait","title":"wait","text":"
async def wait(state_or_states: Union[Any, Sequence[Any]]) -> Tuple[Any, Any]\n

Wait state to be set.

Arguments:

  • state_or_states: state or list of states.

Returns:

tuple of previous state and new state.

"},{"location":"aea-framework-documentation/api/helpers/async_utils/#transit","title":"transit","text":"
@contextmanager\ndef transit(initial: Any = not_set,\nsuccess: Any = not_set,\nfail: Any = not_set) -> Generator\n

Change state context according to success or not.

Arguments:

  • initial: set state on context enter, not_set by default
  • success: set state on context block done, not_set by default
  • fail: set state on context block raises exception, not_set by default

Returns:

generator

"},{"location":"aea-framework-documentation/api/helpers/async_utils/#periodiccaller-objects","title":"PeriodicCaller Objects","text":"
class PeriodicCaller()\n

Schedule a periodic call of callable using event loop.

Used for periodic function run using asyncio.

"},{"location":"aea-framework-documentation/api/helpers/async_utils/#__init___1","title":"__init__","text":"
def __init__(callback: Callable,\nperiod: float,\nstart_at: Optional[datetime.datetime] = None,\nexception_callback: Optional[Callable[[Callable, Exception],\nNone]] = None,\nloop: Optional[AbstractEventLoop] = None) -> None\n

Init periodic caller.

Arguments:

  • callback: function to call periodically
  • period: period in seconds.
  • start_at: optional first call datetime
  • exception_callback: optional handler to call on exception raised.
  • loop: optional asyncio event loop

"},{"location":"aea-framework-documentation/api/helpers/async_utils/#start","title":"start","text":"
def start() -> None\n

Activate period calls.

"},{"location":"aea-framework-documentation/api/helpers/async_utils/#stop","title":"stop","text":"
def stop() -> None\n

Remove from schedule.

"},{"location":"aea-framework-documentation/api/helpers/async_utils/#anotherthreadtask-objects","title":"AnotherThreadTask Objects","text":"
class AnotherThreadTask()\n

Schedule a task to run on the loop in another thread.

Provides better cancel behaviour: on cancel it will wait till cancelled completely.

"},{"location":"aea-framework-documentation/api/helpers/async_utils/#__init___2","title":"__init__","text":"
def __init__(coro: Coroutine[Any, Any, Any], loop: AbstractEventLoop) -> None\n

Init the task.

Arguments:

  • coro: coroutine to schedule
  • loop: an event loop to schedule on.

"},{"location":"aea-framework-documentation/api/helpers/async_utils/#result","title":"result","text":"
def result(timeout: Optional[float] = None) -> Any\n

Wait for coroutine execution result.

Arguments:

  • timeout: optional timeout to wait in seconds.

Returns:

result

"},{"location":"aea-framework-documentation/api/helpers/async_utils/#cancel","title":"cancel","text":"
def cancel() -> None\n

Cancel coroutine task execution in a target loop.

"},{"location":"aea-framework-documentation/api/helpers/async_utils/#done","title":"done","text":"
def done() -> bool\n

Check task is done.

"},{"location":"aea-framework-documentation/api/helpers/async_utils/#threadedasyncrunner-objects","title":"ThreadedAsyncRunner Objects","text":"
class ThreadedAsyncRunner(Thread)\n

Util to run thread with event loop and execute coroutines inside.

"},{"location":"aea-framework-documentation/api/helpers/async_utils/#__init___3","title":"__init__","text":"
def __init__(loop: Optional[AbstractEventLoop] = None) -> None\n

Init threaded runner.

Arguments:

  • loop: optional event loop. is it's running loop, threaded runner will use it.

"},{"location":"aea-framework-documentation/api/helpers/async_utils/#start_1","title":"start","text":"
def start() -> None\n

Start event loop in dedicated thread.

"},{"location":"aea-framework-documentation/api/helpers/async_utils/#run","title":"run","text":"
def run() -> None\n

Run code inside thread.

"},{"location":"aea-framework-documentation/api/helpers/async_utils/#call","title":"call","text":"
def call(coro: Coroutine[Any, Any, Any]) -> Any\n

Run a coroutine inside the event loop.

Arguments:

  • coro: a coroutine to run.

Returns:

task

"},{"location":"aea-framework-documentation/api/helpers/async_utils/#stop_1","title":"stop","text":"
def stop() -> None\n

Stop event loop in thread.

"},{"location":"aea-framework-documentation/api/helpers/async_utils/#runnable-objects","title":"Runnable Objects","text":"
class Runnable(ABC)\n

Abstract Runnable class.

Use to run async task in same event loop or in dedicated thread. Provides: start, stop sync methods to start and stop task Use wait_completed to await task was completed.

"},{"location":"aea-framework-documentation/api/helpers/async_utils/#__init___4","title":"__init__","text":"
def __init__(loop: asyncio.AbstractEventLoop = None,\nthreaded: bool = False) -> None\n

Init runnable.

Arguments:

  • loop: asyncio event loop to use.
  • threaded: bool. start in thread if True.

"},{"location":"aea-framework-documentation/api/helpers/async_utils/#start_2","title":"start","text":"
def start() -> bool\n

Start runnable.

Returns:

bool started or not.

"},{"location":"aea-framework-documentation/api/helpers/async_utils/#is_running","title":"is_running","text":"
@property\ndef is_running() -> bool\n

Get running state.

"},{"location":"aea-framework-documentation/api/helpers/async_utils/#run_1","title":"run","text":"
@abstractmethod\nasync def run() -> Any\n

Implement run logic respectful to CancelError on termination.

"},{"location":"aea-framework-documentation/api/helpers/async_utils/#wait_completed","title":"wait_completed","text":"
def wait_completed(sync: bool = False,\ntimeout: float = None,\nforce_result: bool = False) -> Union[Coroutine, Awaitable]\n

Wait runnable execution completed.

Arguments:

  • sync: bool. blocking wait
  • timeout: float seconds
  • force_result: check result even it was waited.

Returns:

awaitable if sync is False, otherwise None

"},{"location":"aea-framework-documentation/api/helpers/async_utils/#stop_2","title":"stop","text":"
def stop(force: bool = False) -> None\n

Stop runnable.

"},{"location":"aea-framework-documentation/api/helpers/async_utils/#start_and_wait_completed","title":"start_and_wait_completed","text":"
def start_and_wait_completed(*args: Any,\n**kwargs: Any) -> Union[Coroutine, Awaitable]\n

Alias for start and wait methods.

"},{"location":"aea-framework-documentation/api/helpers/base/","title":"Base","text":""},{"location":"aea-framework-documentation/api/helpers/base/#aeahelpersbase","title":"aea.helpers.base","text":"

Miscellaneous helpers.

"},{"location":"aea-framework-documentation/api/helpers/base/#locate","title":"locate","text":"
def locate(path: str) -> Any\n

Locate an object by name or dotted save_path, importing as necessary.

"},{"location":"aea-framework-documentation/api/helpers/base/#load_module","title":"load_module","text":"
def load_module(dotted_path: str, filepath: Path) -> types.ModuleType\n

Load a module.

Arguments:

  • dotted_path: the dotted save_path of the package/module.
  • filepath: the file to the package/module.

Raises:

  • ValueError: if the filepath provided is not a module. # noqa: DAR402
  • Exception: if the execution of the module raises exception. # noqa: DAR402

Returns:

module type

"},{"location":"aea-framework-documentation/api/helpers/base/#load_env_file","title":"load_env_file","text":"
def load_env_file(env_file: str) -> None\n

Load the content of the environment file into the process environment.

Arguments:

  • env_file: save_path to the env file.

"},{"location":"aea-framework-documentation/api/helpers/base/#sigint_crossplatform","title":"sigint_crossplatform","text":"
def sigint_crossplatform(process: subprocess.Popen) -> None\n

Send a SIGINT, cross-platform.

The reason is because the subprocess module doesn't have an API to send a SIGINT-like signal both on Posix and Windows with a single method.

However, a subprocess.Popen class has the method 'send_signal' that gives more flexibility in this terms.

Arguments:

  • process: the process to send the signal to.

"},{"location":"aea-framework-documentation/api/helpers/base/#win_popen_kwargs","title":"win_popen_kwargs","text":"
def win_popen_kwargs() -> dict\n

Return kwargs to start a process in windows with new process group.

Help to handle ctrl c properly. Return empty dict if platform is not win32

Returns:

windows popen kwargs

"},{"location":"aea-framework-documentation/api/helpers/base/#send_control_c","title":"send_control_c","text":"
def send_control_c(process: subprocess.Popen,\nkill_group: bool = False) -> None\n

Send ctrl-C cross-platform to terminate a subprocess.

Arguments:

  • process: the process to send the signal to.
  • kill_group: whether or not to kill group

"},{"location":"aea-framework-documentation/api/helpers/base/#regexconstrainedstring-objects","title":"RegexConstrainedString Objects","text":"
class RegexConstrainedString(UserString)\n

A string that is constrained by a regex.

The default behaviour is to match anything. Subclass this class and change the 'REGEX' class attribute to implement a different behaviour.

"},{"location":"aea-framework-documentation/api/helpers/base/#__init__","title":"__init__","text":"
def __init__(seq: Union[UserString, str]) -> None\n

Initialize a regex constrained string.

"},{"location":"aea-framework-documentation/api/helpers/base/#simpleid-objects","title":"SimpleId Objects","text":"
class SimpleId(RegexConstrainedString)\n

A simple identifier.

The allowed strings are all the strings that: - have at least length 1 - have at most length 128 - the first character must be between a-z,A-Z or underscore - the other characters must be either the above or digits.

Examples of allowed strings:

SimpleId(\"an_identifier\") 'an_identifier'

Examples of not allowed strings:

SimpleId(\"0an_identifier\") Traceback (most recent call last): ... ValueError: Value 0an_identifier does not match the regular expression re.compile('[a-zA-Z_][a-zA-Z0-9_]{0,127}')

SimpleId(\"an identifier\") Traceback (most recent call last): ... ValueError: Value an identifier does not match the regular expression re.compile('[a-zA-Z_][a-zA-Z0-9_]{0,127}')

SimpleId(\"\") Traceback (most recent call last): ... ValueError: Value does not match the regular expression re.compile('[a-zA-Z_][a-zA-Z0-9_]{0,127}')

"},{"location":"aea-framework-documentation/api/helpers/base/#cd","title":"cd","text":"
@contextlib.contextmanager\ndef cd(path: PathLike) -> Generator\n

Change working directory temporarily.

"},{"location":"aea-framework-documentation/api/helpers/base/#get_logger_method","title":"get_logger_method","text":"
def get_logger_method(fn: Callable,\nlogger_method: Union[str, Callable]) -> Callable\n

Get logger method for function.

Get logger in fn definition module or creates logger is module.name. Or return logger_method if it's callable.

Arguments:

  • fn: function to get logger for.
  • logger_method: logger name or callable.

Returns:

callable to write log with

"},{"location":"aea-framework-documentation/api/helpers/base/#try_decorator","title":"try_decorator","text":"
def try_decorator(error_message: str,\ndefault_return: Callable = None,\nlogger_method: Any = \"error\") -> Callable\n

Run function, log and return default value on exception.

Does not support async or coroutines!

Arguments:

  • error_message: message template with one {} for exception
  • default_return: value to return on exception, by default None
  • logger_method: name of the logger method or callable to print logs

Returns:

the callable

"},{"location":"aea-framework-documentation/api/helpers/base/#maxretrieserror-objects","title":"MaxRetriesError Objects","text":"
class MaxRetriesError(Exception)\n

Exception for retry decorator.

"},{"location":"aea-framework-documentation/api/helpers/base/#retry_decorator","title":"retry_decorator","text":"
def retry_decorator(number_of_retries: int,\nerror_message: str,\ndelay: float = 0,\nlogger_method: str = \"error\") -> Callable\n

Run function with several attempts.

Does not support async or coroutines!

Arguments:

  • number_of_retries: amount of attempts
  • error_message: message template with one {} for exception
  • delay: number of seconds to sleep between retries. default 0
  • logger_method: name of the logger method or callable to print logs

Returns:

the callable

"},{"location":"aea-framework-documentation/api/helpers/base/#exception_log_and_reraise","title":"exception_log_and_reraise","text":"
@contextlib.contextmanager\ndef exception_log_and_reraise(log_method: Callable, message: str) -> Generator\n

Run code in context to log and re raise exception.

Arguments:

  • log_method: function to print log
  • message: message template to add error text.

Returns:

the generator

"},{"location":"aea-framework-documentation/api/helpers/base/#recursive_update","title":"recursive_update","text":"
def recursive_update(to_update: Dict,\nnew_values: Dict,\nallow_new_values: bool = False) -> None\n

Update a dictionary by replacing conflicts with the new values.

It does side-effects to the first dictionary.

to_update = dict(a=1, b=2, subdict=dict(subfield1=1)) new_values = dict(b=3, subdict=dict(subfield1=2)) recursive_update(to_update, new_values) to_update {'a': 1, 'b': 3, 'subdict': {'subfield1': 2}}

Arguments:

  • to_update: the dictionary to update.
  • new_values: the dictionary of new values to replace.
  • allow_new_values: whether or not to allow new values.

"},{"location":"aea-framework-documentation/api/helpers/base/#find_topological_order","title":"find_topological_order","text":"
def find_topological_order(adjacency_list: Dict[T, Set[T]]) -> List[T]\n

Compute the topological order of a graph (using Kahn's algorithm).

Arguments:

  • adjacency_list: the adjacency list of the graph.

Raises:

  • ValueError: if the graph contains a cycle.

Returns:

the topological order for the graph (as a sequence of nodes)

"},{"location":"aea-framework-documentation/api/helpers/base/#reachable_nodes","title":"reachable_nodes","text":"
def reachable_nodes(adjacency_list: Dict[T, Set[T]],\nstarting_nodes: Set[T]) -> Dict[T, Set[T]]\n

Find the reachable subgraph induced by a set of starting nodes.

Arguments:

  • adjacency_list: the adjacency list of the full graph.
  • starting_nodes: the starting nodes of the new graph.

Returns:

the adjacency list of the subgraph.

"},{"location":"aea-framework-documentation/api/helpers/base/#cached_property-objects","title":"cached_property Objects","text":"
class cached_property()\n

Cached property from python3.8 functools.

"},{"location":"aea-framework-documentation/api/helpers/base/#__init___1","title":"__init__","text":"
def __init__(func: Callable) -> None\n

Init cached property.

"},{"location":"aea-framework-documentation/api/helpers/base/#__set_name__","title":"__set_name__","text":"
def __set_name__(_: Any, name: Any) -> None\n

Set name.

"},{"location":"aea-framework-documentation/api/helpers/base/#__get__","title":"__get__","text":"
def __get__(instance: Any, _: Optional[Any] = None) -> Any\n

Get instance.

"},{"location":"aea-framework-documentation/api/helpers/base/#ensure_dir","title":"ensure_dir","text":"
def ensure_dir(dir_path: str) -> None\n

Check if dir_path is a directory or create it.

"},{"location":"aea-framework-documentation/api/helpers/base/#dict_to_path_value","title":"dict_to_path_value","text":"
def dict_to_path_value(\ndata: Mapping,\npath: Optional[List] = None) -> Iterable[Tuple[List[str], Any]]\n

Convert dict to sequence of terminal path build of keys and value.

"},{"location":"aea-framework-documentation/api/helpers/base/#parse_datetime_from_str","title":"parse_datetime_from_str","text":"
def parse_datetime_from_str(date_string: str) -> datetime.datetime\n

Parse datetime from string.

"},{"location":"aea-framework-documentation/api/helpers/base/#certrequest-objects","title":"CertRequest Objects","text":"
class CertRequest()\n

Certificate request for proof of representation.

"},{"location":"aea-framework-documentation/api/helpers/base/#__init___2","title":"__init__","text":"
def __init__(public_key: str, identifier: SimpleIdOrStr,\nledger_id: SimpleIdOrStr, not_before: str, not_after: str,\nmessage_format: str, save_path: str) -> None\n

Initialize the certificate request.

Arguments:

  • public_key: the public key, or the key id.
  • identifier: certificate identifier.
  • ledger_id: ledger identifier the request is referring to.
  • not_before: specify the lower bound for certificate validity. If it is a string, it must follow the format: 'YYYY-MM-DD'. It will be interpreted as timezone UTC-0.
  • not_after: specify the lower bound for certificate validity. If it is a string, it must follow the format: 'YYYY-MM-DD'. It will be interpreted as timezone UTC-0.
  • message_format: message format used for signing
  • save_path: the save_path where to save the certificate.

"},{"location":"aea-framework-documentation/api/helpers/base/#public_key","title":"public_key","text":"
@property\ndef public_key() -> Optional[str]\n

Get the public key.

"},{"location":"aea-framework-documentation/api/helpers/base/#ledger_id","title":"ledger_id","text":"
@property\ndef ledger_id() -> str\n

Get the ledger id.

"},{"location":"aea-framework-documentation/api/helpers/base/#key_identifier","title":"key_identifier","text":"
@property\ndef key_identifier() -> Optional[str]\n

Get the key identifier.

"},{"location":"aea-framework-documentation/api/helpers/base/#identifier","title":"identifier","text":"
@property\ndef identifier() -> str\n

Get the identifier.

"},{"location":"aea-framework-documentation/api/helpers/base/#not_before_string","title":"not_before_string","text":"
@property\ndef not_before_string() -> str\n

Get the not_before field as string.

"},{"location":"aea-framework-documentation/api/helpers/base/#not_after_string","title":"not_after_string","text":"
@property\ndef not_after_string() -> str\n

Get the not_after field as string.

"},{"location":"aea-framework-documentation/api/helpers/base/#not_before","title":"not_before","text":"
@property\ndef not_before() -> datetime.datetime\n

Get the not_before field.

"},{"location":"aea-framework-documentation/api/helpers/base/#not_after","title":"not_after","text":"
@property\ndef not_after() -> datetime.datetime\n

Get the not_after field.

"},{"location":"aea-framework-documentation/api/helpers/base/#message_format","title":"message_format","text":"
@property\ndef message_format() -> str\n

Get the message format.

"},{"location":"aea-framework-documentation/api/helpers/base/#save_path","title":"save_path","text":"
@property\ndef save_path() -> Path\n

Get the save path for the certificate.

Note: if the path is not absolute, then the actual save path might depend on the context.

Returns:

the save path

"},{"location":"aea-framework-documentation/api/helpers/base/#get_absolute_save_path","title":"get_absolute_save_path","text":"
def get_absolute_save_path(path_prefix: Optional[PathLike] = None) -> Path\n

Get the absolute save path.

If save_path is an absolute path, then the prefix is ignored. Otherwise, the path prefix is prepended.

Arguments:

  • path_prefix: the (absolute) path to prepend to the save path.

Returns:

the actual save path.

"},{"location":"aea-framework-documentation/api/helpers/base/#public_key_or_identifier","title":"public_key_or_identifier","text":"
@property\ndef public_key_or_identifier() -> str\n

Get the public key or identifier.

"},{"location":"aea-framework-documentation/api/helpers/base/#get_message","title":"get_message","text":"
def get_message(public_key: str) -> bytes\n

Get the message to sign.

"},{"location":"aea-framework-documentation/api/helpers/base/#construct_message","title":"construct_message","text":"
@classmethod\ndef construct_message(cls, public_key: str, identifier: SimpleIdOrStr,\nnot_before_string: str, not_after_string: str,\nmessage_format: str) -> bytes\n

Construct message for singning.

Arguments:

  • public_key: the public key
  • identifier: identifier to be signed
  • not_before_string: signature not valid before
  • not_after_string: signature not valid after
  • message_format: message format used for signing

Returns:

the message

"},{"location":"aea-framework-documentation/api/helpers/base/#get_signature","title":"get_signature","text":"
def get_signature(path_prefix: Optional[PathLike] = None) -> str\n

Get signature from save_path.

Arguments:

  • path_prefix: the path prefix to be prepended to save_path. Defaults to cwd.

Returns:

the signature.

"},{"location":"aea-framework-documentation/api/helpers/base/#json","title":"json","text":"
@property\ndef json() -> Dict\n

Compute the JSON representation.

"},{"location":"aea-framework-documentation/api/helpers/base/#from_json","title":"from_json","text":"
@classmethod\ndef from_json(cls, obj: Dict) -> \"CertRequest\"\n

Compute the JSON representation.

"},{"location":"aea-framework-documentation/api/helpers/base/#__eq__","title":"__eq__","text":"
def __eq__(other: Any) -> bool\n

Check equality.

"},{"location":"aea-framework-documentation/api/helpers/base/#compute_specifier_from_version","title":"compute_specifier_from_version","text":"
def compute_specifier_from_version(version: Version) -> str\n

Compute the specifier set from a version.

version specifier is: >=major.minor.0, <next_major.0.0

Arguments:

  • version: the version

Returns:

the specifier set

"},{"location":"aea-framework-documentation/api/helpers/base/#decorator_with_optional_params","title":"decorator_with_optional_params","text":"
def decorator_with_optional_params(decorator: Callable) -> Callable\n

Make a decorator usable either with or without parameters.

In other words, if a decorator \"mydecorator\" is decorated with this decorator, It can be used both as:

@mydecorator def myfunction(): ...

or as:

@mydecorator(arg1, kwarg1=\"value\") def myfunction(): ...

Arguments:

  • decorator: a decorator callable

Returns:

a decorator callable

"},{"location":"aea-framework-documentation/api/helpers/base/#delete_directory_contents","title":"delete_directory_contents","text":"
def delete_directory_contents(directory: Path) -> None\n

Delete the content of a directory, without deleting it.

"},{"location":"aea-framework-documentation/api/helpers/base/#prepend_if_not_absolute","title":"prepend_if_not_absolute","text":"
def prepend_if_not_absolute(path: PathLike, prefix: PathLike) -> PathLike\n

Prepend a path with a prefix, but only if not absolute

Arguments:

  • path: the path to process.
  • prefix: the path prefix.

Returns:

the same path if absolute, else the prepended path.

"},{"location":"aea-framework-documentation/api/helpers/constants/","title":"Constants","text":""},{"location":"aea-framework-documentation/api/helpers/constants/#aeahelpersconstants","title":"aea.helpers.constants","text":"

Module with helpers constants.

"},{"location":"aea-framework-documentation/api/helpers/env_vars/","title":"Env Vars","text":""},{"location":"aea-framework-documentation/api/helpers/env_vars/#aeahelpersenv_vars","title":"aea.helpers.env_vars","text":"

Implementation of the environment variables support.

"},{"location":"aea-framework-documentation/api/helpers/env_vars/#is_env_variable","title":"is_env_variable","text":"
def is_env_variable(value: Any) -> bool\n

Check is variable string with env variable pattern.

"},{"location":"aea-framework-documentation/api/helpers/env_vars/#replace_with_env_var","title":"replace_with_env_var","text":"
def replace_with_env_var(value: str,\nenv_variables: dict,\ndefault_value: Any = NotSet) -> JSON_TYPES\n

Replace env var with value.

"},{"location":"aea-framework-documentation/api/helpers/env_vars/#apply_env_variables","title":"apply_env_variables","text":"
def apply_env_variables(data: Union[Dict, List[Dict]],\nenv_variables: Mapping[str, Any],\ndefault_value: Any = NotSet) -> JSON_TYPES\n

Create new resulting dict with env variables applied.

"},{"location":"aea-framework-documentation/api/helpers/env_vars/#convert_value_str_to_type","title":"convert_value_str_to_type","text":"
def convert_value_str_to_type(value: str, type_str: str) -> JSON_TYPES\n

Convert value by type name to native python type.

"},{"location":"aea-framework-documentation/api/helpers/exception_policy/","title":"Exception Policy","text":""},{"location":"aea-framework-documentation/api/helpers/exception_policy/#aeahelpersexception_policy","title":"aea.helpers.exception_policy","text":"

This module contains enum of aea exception policies.

"},{"location":"aea-framework-documentation/api/helpers/exception_policy/#exceptionpolicyenum-objects","title":"ExceptionPolicyEnum Objects","text":"
class ExceptionPolicyEnum(Enum)\n

AEA Exception policies.

"},{"location":"aea-framework-documentation/api/helpers/exec_timeout/","title":"Exec Timeout","text":""},{"location":"aea-framework-documentation/api/helpers/exec_timeout/#aeahelpersexec_timeout","title":"aea.helpers.exec_timeout","text":"

Python code execution time limit tools.

"},{"location":"aea-framework-documentation/api/helpers/exec_timeout/#timeoutresult-objects","title":"TimeoutResult Objects","text":"
class TimeoutResult()\n

Result of ExecTimeout context manager.

"},{"location":"aea-framework-documentation/api/helpers/exec_timeout/#__init__","title":"__init__","text":"
def __init__() -> None\n

Init.

"},{"location":"aea-framework-documentation/api/helpers/exec_timeout/#set_cancelled_by_timeout","title":"set_cancelled_by_timeout","text":"
def set_cancelled_by_timeout() -> None\n

Set code was terminated cause timeout.

"},{"location":"aea-framework-documentation/api/helpers/exec_timeout/#is_cancelled_by_timeout","title":"is_cancelled_by_timeout","text":"
def is_cancelled_by_timeout() -> bool\n

Return True if code was terminated by ExecTimeout cause timeout.

Returns:

bool

"},{"location":"aea-framework-documentation/api/helpers/exec_timeout/#timeoutexception-objects","title":"TimeoutException Objects","text":"
class TimeoutException(BaseException)\n

TimeoutException raised by ExecTimeout context managers in thread with limited execution time.

Used internally, does not propagated outside of context manager

"},{"location":"aea-framework-documentation/api/helpers/exec_timeout/#baseexectimeout-objects","title":"BaseExecTimeout Objects","text":"
class BaseExecTimeout(ABC)\n

Base class for implementing context managers to limit python code execution time.

exception_class - is exception type to raise in code controlled in case of timeout.

"},{"location":"aea-framework-documentation/api/helpers/exec_timeout/#__init___1","title":"__init__","text":"
def __init__(timeout: float = 0.0) -> None\n

Init.

Arguments:

  • timeout: number of seconds to execute code before interruption

"},{"location":"aea-framework-documentation/api/helpers/exec_timeout/#__enter__","title":"__enter__","text":"
def __enter__() -> TimeoutResult\n

Enter context manager.

Returns:

TimeoutResult

"},{"location":"aea-framework-documentation/api/helpers/exec_timeout/#__exit__","title":"__exit__","text":"
def __exit__(exc_type: Type[Exception], exc_val: Exception,\nexc_tb: TracebackType) -> None\n

Exit context manager.

Arguments:

  • exc_type: the exception type
  • exc_val: the exception
  • exc_tb: the traceback

"},{"location":"aea-framework-documentation/api/helpers/exec_timeout/#exectimeoutsigalarm-objects","title":"ExecTimeoutSigAlarm Objects","text":"
class ExecTimeoutSigAlarm(BaseExecTimeout)\n

ExecTimeout context manager implementation using signals and SIGALARM.

Does not support threads, have to be used only in main thread.

"},{"location":"aea-framework-documentation/api/helpers/exec_timeout/#exectimeoutthreadguard-objects","title":"ExecTimeoutThreadGuard Objects","text":"
class ExecTimeoutThreadGuard(BaseExecTimeout)\n

ExecTimeout context manager implementation using threads and PyThreadState_SetAsyncExc.

Support threads. Requires supervisor thread start/stop to control execution time control. Possible will be not accurate in case of long c functions used inside code controlled.

"},{"location":"aea-framework-documentation/api/helpers/exec_timeout/#__init___2","title":"__init__","text":"
def __init__(timeout: float = 0.0) -> None\n

Init ExecTimeoutThreadGuard variables.

Arguments:

  • timeout: number of seconds to execute code before interruption

"},{"location":"aea-framework-documentation/api/helpers/exec_timeout/#start","title":"start","text":"
@classmethod\ndef start(cls) -> None\n

Start supervisor thread to check timeouts.

Supervisor starts once but number of start counted.

"},{"location":"aea-framework-documentation/api/helpers/exec_timeout/#stop","title":"stop","text":"
@classmethod\ndef stop(cls, force: bool = False) -> None\n

Stop supervisor thread.

Actual stop performed on force == True or if number of stops == number of starts

Arguments:

  • force: force stop regardless number of start.
"},{"location":"aea-framework-documentation/api/helpers/file_io/","title":"File IO","text":""},{"location":"aea-framework-documentation/api/helpers/file_io/#aeahelpersfile_io","title":"aea.helpers.file_io","text":"

Read to and write from file with envelopes.

"},{"location":"aea-framework-documentation/api/helpers/file_io/#lock_file","title":"lock_file","text":"
@contextmanager\ndef lock_file(file_descriptor: IO[bytes],\nlogger: Logger = _default_logger) -> Generator\n

Lock file in context manager.

Arguments:

  • file_descriptor: file descriptor of file to lock.
  • logger: the logger.

Returns:

generator

"},{"location":"aea-framework-documentation/api/helpers/file_io/#write_envelope","title":"write_envelope","text":"
def write_envelope(envelope: Envelope,\nfile_pointer: IO[bytes],\nseparator: bytes = SEPARATOR,\nlogger: Logger = _default_logger) -> None\n

Write envelope to file.

"},{"location":"aea-framework-documentation/api/helpers/file_io/#write_with_lock","title":"write_with_lock","text":"
def write_with_lock(file_pointer: IO[bytes],\ndata: Union[bytes],\nlogger: Logger = _default_logger) -> None\n

Write bytes to file protected with file lock.

"},{"location":"aea-framework-documentation/api/helpers/file_io/#envelope_from_bytes","title":"envelope_from_bytes","text":"
def envelope_from_bytes(\nbytes_: bytes,\nseparator: bytes = SEPARATOR,\nlogger: Logger = _default_logger) -> Optional[Envelope]\n

Decode bytes to get the envelope.

Arguments:

  • bytes_: the encoded envelope
  • separator: the separator used
  • logger: the logger

Returns:

Envelope

"},{"location":"aea-framework-documentation/api/helpers/file_lock/","title":"File Lock","text":""},{"location":"aea-framework-documentation/api/helpers/file_lock/#aeahelpersfile_lock","title":"aea.helpers.file_lock","text":"

Patch of 'fnctl' to make it compatible with Windows.

"},{"location":"aea-framework-documentation/api/helpers/http_requests/","title":"HttpRequests","text":""},{"location":"aea-framework-documentation/api/helpers/http_requests/#aeahelpershttp_requests","title":"aea.helpers.http_requests","text":"

Wrapper over requests library.

"},{"location":"aea-framework-documentation/api/helpers/http_requests/#add_default_timeout","title":"add_default_timeout","text":"
def add_default_timeout(fn: Callable, timeout: float) -> Callable\n

Add default timeout for requests methods.

"},{"location":"aea-framework-documentation/api/helpers/install_dependency/","title":"Install Dependency","text":""},{"location":"aea-framework-documentation/api/helpers/install_dependency/#aeahelpersinstall_dependency","title":"aea.helpers.install_dependency","text":"

Helper to install python dependencies.

"},{"location":"aea-framework-documentation/api/helpers/install_dependency/#install_dependency","title":"install_dependency","text":"
def install_dependency(dependency_name: str,\ndependency: Dependency,\nlogger: Logger,\ninstall_timeout: float = 300) -> None\n

Install python dependency to the current python environment.

Arguments:

  • dependency_name: name of the python package
  • dependency: Dependency specification
  • logger: the logger
  • install_timeout: timeout to wait pip to install

"},{"location":"aea-framework-documentation/api/helpers/install_dependency/#install_dependencies","title":"install_dependencies","text":"
def install_dependencies(dependencies: List[Dependency],\nlogger: Logger,\ninstall_timeout: float = 300) -> None\n

Install python dependencies to the current python environment.

Arguments:

  • dependencies: dict of dependency name and specification
  • logger: the logger
  • install_timeout: timeout to wait pip to install

"},{"location":"aea-framework-documentation/api/helpers/install_dependency/#call_pip","title":"call_pip","text":"
def call_pip(pip_args: List[str],\ntimeout: float = 300,\nretry: bool = False) -> None\n

Run pip install command.

Arguments:

  • pip_args: list strings of the command
  • timeout: timeout to wait pip to install
  • retry: bool, try one more time if command failed

"},{"location":"aea-framework-documentation/api/helpers/install_dependency/#run_install_subprocess","title":"run_install_subprocess","text":"
def run_install_subprocess(install_command: List[str],\ninstall_timeout: float = 300) -> int\n

Try executing install command.

Arguments:

  • install_command: list strings of the command
  • install_timeout: timeout to wait pip to install

Returns:

the return code of the subprocess

"},{"location":"aea-framework-documentation/api/helpers/io/","title":"IO","text":""},{"location":"aea-framework-documentation/api/helpers/io/#aeahelpersio","title":"aea.helpers.io","text":""},{"location":"aea-framework-documentation/api/helpers/logging/","title":"Logging","text":""},{"location":"aea-framework-documentation/api/helpers/logging/#aeahelperslogging","title":"aea.helpers.logging","text":"

Logging helpers.

"},{"location":"aea-framework-documentation/api/helpers/logging/#get_logger","title":"get_logger","text":"
def get_logger(module_path: str, agent_name: str) -> Logger\n

Get the logger based on a module path and agent name.

"},{"location":"aea-framework-documentation/api/helpers/logging/#agentloggeradapter-objects","title":"AgentLoggerAdapter Objects","text":"
class AgentLoggerAdapter(LoggerAdapter)\n

This class is a logger adapter that prepends the agent name to log messages.

"},{"location":"aea-framework-documentation/api/helpers/logging/#__init__","title":"__init__","text":"
def __init__(logger: Logger, agent_name: str) -> None\n

Initialize the logger adapter.

Arguments:

  • logger: the logger.
  • agent_name: the agent name.

"},{"location":"aea-framework-documentation/api/helpers/logging/#process","title":"process","text":"
def process(\nmsg: Any,\nkwargs: MutableMapping[str,\nAny]) -> Tuple[Any, MutableMapping[str, Any]]\n

Prepend the agent name to every log message.

"},{"location":"aea-framework-documentation/api/helpers/logging/#withlogger-objects","title":"WithLogger Objects","text":"
class WithLogger()\n

Interface to endow subclasses with a logger.

"},{"location":"aea-framework-documentation/api/helpers/logging/#__init___1","title":"__init__","text":"
def __init__(logger: Optional[Logger] = None,\ndefault_logger_name: str = \"aea\") -> None\n

Initialize the logger.

Arguments:

  • logger: the logger object.
  • default_logger_name: the default logger name, if a logger is not provided.

"},{"location":"aea-framework-documentation/api/helpers/logging/#logger","title":"logger","text":"
@property\ndef logger() -> Logger\n

Get the component logger.

"},{"location":"aea-framework-documentation/api/helpers/logging/#logger_1","title":"logger","text":"
@logger.setter\ndef logger(logger: Optional[Logger]) -> None\n

Set the logger.

"},{"location":"aea-framework-documentation/api/helpers/multiple_executor/","title":"MultipleExecutor","text":""},{"location":"aea-framework-documentation/api/helpers/multiple_executor/#aeahelpersmultiple_executor","title":"aea.helpers.multiple_executor","text":"

This module contains the helpers to run multiple stoppable tasks in different modes: async, threaded, multiprocess .

"},{"location":"aea-framework-documentation/api/helpers/multiple_executor/#executorexceptionpolicies-objects","title":"ExecutorExceptionPolicies Objects","text":"
class ExecutorExceptionPolicies(Enum)\n

Runner exception policy modes.

"},{"location":"aea-framework-documentation/api/helpers/multiple_executor/#abstractexecutortask-objects","title":"AbstractExecutorTask Objects","text":"
class AbstractExecutorTask(ABC)\n

Abstract task class to create Task classes.

"},{"location":"aea-framework-documentation/api/helpers/multiple_executor/#__init__","title":"__init__","text":"
def __init__() -> None\n

Init task.

"},{"location":"aea-framework-documentation/api/helpers/multiple_executor/#future","title":"future","text":"
@property\ndef future() -> Optional[TaskAwaitable]\n

Return awaitable to get result of task execution.

"},{"location":"aea-framework-documentation/api/helpers/multiple_executor/#future_1","title":"future","text":"
@future.setter\ndef future(future: TaskAwaitable) -> None\n

Set awaitable to get result of task execution.

"},{"location":"aea-framework-documentation/api/helpers/multiple_executor/#start","title":"start","text":"
@abstractmethod\ndef start() -> Tuple[Callable, Sequence[Any]]\n

Implement start task function here.

"},{"location":"aea-framework-documentation/api/helpers/multiple_executor/#stop","title":"stop","text":"
@abstractmethod\ndef stop() -> None\n

Implement stop task function here.

"},{"location":"aea-framework-documentation/api/helpers/multiple_executor/#create_async_task","title":"create_async_task","text":"
@abstractmethod\ndef create_async_task(loop: AbstractEventLoop) -> TaskAwaitable\n

Create asyncio task for task run in asyncio loop.

Arguments:

  • loop: the event loop

Returns:

task to run in asyncio loop.

"},{"location":"aea-framework-documentation/api/helpers/multiple_executor/#id","title":"id","text":"
@property\ndef id() -> Any\n

Return task id.

"},{"location":"aea-framework-documentation/api/helpers/multiple_executor/#failed","title":"failed","text":"
@property\ndef failed() -> bool\n

Return was exception failed or not.

If it's running it's not failed.

Returns:

bool

"},{"location":"aea-framework-documentation/api/helpers/multiple_executor/#abstractmultiprocessexecutortask-objects","title":"AbstractMultiprocessExecutorTask Objects","text":"
class AbstractMultiprocessExecutorTask(AbstractExecutorTask)\n

Task for multiprocess executor.

"},{"location":"aea-framework-documentation/api/helpers/multiple_executor/#start_1","title":"start","text":"
@abstractmethod\ndef start() -> Tuple[Callable, Sequence[Any]]\n

Return function and arguments to call within subprocess.

"},{"location":"aea-framework-documentation/api/helpers/multiple_executor/#create_async_task_1","title":"create_async_task","text":"
def create_async_task(loop: AbstractEventLoop) -> TaskAwaitable\n

Create asyncio task for task run in asyncio loop.

Raise error, cause async mode is not supported, cause this task for multiprocess executor only.

Arguments:

  • loop: the event loop

Raises:

  • ValueError: async task construction not possible

"},{"location":"aea-framework-documentation/api/helpers/multiple_executor/#abstractmultipleexecutor-objects","title":"AbstractMultipleExecutor Objects","text":"
class AbstractMultipleExecutor(ABC)\n

Abstract class to create multiple executors classes.

"},{"location":"aea-framework-documentation/api/helpers/multiple_executor/#__init___1","title":"__init__","text":"
def __init__(\ntasks: Sequence[AbstractExecutorTask],\ntask_fail_policy: ExecutorExceptionPolicies = ExecutorExceptionPolicies.\npropagate\n) -> None\n

Init executor.

Arguments:

  • tasks: sequence of AbstractExecutorTask instances to run.
  • task_fail_policy: the exception policy of all the tasks

"},{"location":"aea-framework-documentation/api/helpers/multiple_executor/#is_running","title":"is_running","text":"
@property\ndef is_running() -> bool\n

Return running state of the executor.

"},{"location":"aea-framework-documentation/api/helpers/multiple_executor/#start_2","title":"start","text":"
def start() -> None\n

Start tasks.

"},{"location":"aea-framework-documentation/api/helpers/multiple_executor/#stop_1","title":"stop","text":"
def stop() -> None\n

Stop tasks.

"},{"location":"aea-framework-documentation/api/helpers/multiple_executor/#num_failed","title":"num_failed","text":"
@property\ndef num_failed() -> int\n

Return number of failed tasks.

"},{"location":"aea-framework-documentation/api/helpers/multiple_executor/#failed_tasks","title":"failed_tasks","text":"
@property\ndef failed_tasks() -> Sequence[AbstractExecutorTask]\n

Return sequence failed tasks.

"},{"location":"aea-framework-documentation/api/helpers/multiple_executor/#not_failed_tasks","title":"not_failed_tasks","text":"
@property\ndef not_failed_tasks() -> Sequence[AbstractExecutorTask]\n

Return sequence successful tasks.

"},{"location":"aea-framework-documentation/api/helpers/multiple_executor/#threadexecutor-objects","title":"ThreadExecutor Objects","text":"
class ThreadExecutor(AbstractMultipleExecutor)\n

Thread based executor to run multiple agents in threads.

"},{"location":"aea-framework-documentation/api/helpers/multiple_executor/#processexecutor-objects","title":"ProcessExecutor Objects","text":"
class ProcessExecutor(ThreadExecutor)\n

Subprocess based executor to run multiple agents in threads.

"},{"location":"aea-framework-documentation/api/helpers/multiple_executor/#asyncexecutor-objects","title":"AsyncExecutor Objects","text":"
class AsyncExecutor(AbstractMultipleExecutor)\n

Thread based executor to run multiple agents in threads.

"},{"location":"aea-framework-documentation/api/helpers/multiple_executor/#abstractmultiplerunner-objects","title":"AbstractMultipleRunner Objects","text":"
class AbstractMultipleRunner()\n

Abstract multiple runner to create classes to launch tasks with selected mode.

"},{"location":"aea-framework-documentation/api/helpers/multiple_executor/#__init___2","title":"__init__","text":"
def __init__(\nmode: str,\nfail_policy: ExecutorExceptionPolicies = ExecutorExceptionPolicies.\npropagate\n) -> None\n

Init with selected executor mode.

Arguments:

  • mode: one of supported executor modes
  • fail_policy: one of ExecutorExceptionPolicies to be used with Executor

"},{"location":"aea-framework-documentation/api/helpers/multiple_executor/#is_running_1","title":"is_running","text":"
@property\ndef is_running() -> bool\n

Return state of the executor.

"},{"location":"aea-framework-documentation/api/helpers/multiple_executor/#start_3","title":"start","text":"
def start(threaded: bool = False) -> None\n

Run agents.

Arguments:

  • threaded: run in dedicated thread without blocking current thread.

"},{"location":"aea-framework-documentation/api/helpers/multiple_executor/#stop_2","title":"stop","text":"
def stop(timeout: Optional[float] = None) -> None\n

Stop agents.

Arguments:

  • timeout: timeout in seconds to wait thread stopped, only if started in thread mode.

"},{"location":"aea-framework-documentation/api/helpers/multiple_executor/#num_failed_1","title":"num_failed","text":"
@property\ndef num_failed() -> int\n

Return number of failed tasks.

"},{"location":"aea-framework-documentation/api/helpers/multiple_executor/#failed_1","title":"failed","text":"
@property\ndef failed() -> Sequence[Task]\n

Return sequence failed tasks.

"},{"location":"aea-framework-documentation/api/helpers/multiple_executor/#not_failed","title":"not_failed","text":"
@property\ndef not_failed() -> Sequence[Task]\n

Return sequence successful tasks.

"},{"location":"aea-framework-documentation/api/helpers/multiple_executor/#try_join_thread","title":"try_join_thread","text":"
def try_join_thread() -> None\n

Try to join thread if running in thread mode.

"},{"location":"aea-framework-documentation/api/helpers/pipe/","title":"Pipe","text":""},{"location":"aea-framework-documentation/api/helpers/pipe/#aeahelperspipe","title":"aea.helpers.pipe","text":"

Portable pipe implementation for Linux, MacOS, and Windows.

"},{"location":"aea-framework-documentation/api/helpers/pipe/#ipcchannelclient-objects","title":"IPCChannelClient Objects","text":"
class IPCChannelClient(ABC)\n

Multi-platform interprocess communication channel for the client side.

"},{"location":"aea-framework-documentation/api/helpers/pipe/#connect","title":"connect","text":"
@abstractmethod\nasync def connect(timeout: float = PIPE_CONN_TIMEOUT) -> bool\n

Connect to communication channel

Arguments:

  • timeout: timeout for other end to connect

Returns:

connection status

"},{"location":"aea-framework-documentation/api/helpers/pipe/#write","title":"write","text":"
@abstractmethod\nasync def write(data: bytes) -> None\n

Write data bytes to the other end of the channel

Will first write the size than the actual data

Arguments:

  • data: bytes to write

"},{"location":"aea-framework-documentation/api/helpers/pipe/#read","title":"read","text":"
@abstractmethod\nasync def read() -> Optional[bytes]\n

Read bytes from the other end of the channel

Will first read the size than the actual data

Returns:

read bytes

"},{"location":"aea-framework-documentation/api/helpers/pipe/#close","title":"close","text":"
@abstractmethod\nasync def close() -> None\n

Close the communication channel.

"},{"location":"aea-framework-documentation/api/helpers/pipe/#ipcchannel-objects","title":"IPCChannel Objects","text":"
class IPCChannel(IPCChannelClient)\n

Multi-platform interprocess communication channel.

"},{"location":"aea-framework-documentation/api/helpers/pipe/#in_path","title":"in_path","text":"
@property\n@abstractmethod\ndef in_path() -> str\n

Rendezvous point for incoming communication.

Returns:

path

"},{"location":"aea-framework-documentation/api/helpers/pipe/#out_path","title":"out_path","text":"
@property\n@abstractmethod\ndef out_path() -> str\n

Rendezvous point for outgoing communication.

Returns:

path

"},{"location":"aea-framework-documentation/api/helpers/pipe/#posixnamedpipeprotocol-objects","title":"PosixNamedPipeProtocol Objects","text":"
class PosixNamedPipeProtocol()\n

Posix named pipes async wrapper communication protocol.

"},{"location":"aea-framework-documentation/api/helpers/pipe/#__init__","title":"__init__","text":"
def __init__(in_path: str,\nout_path: str,\nlogger: logging.Logger = _default_logger,\nloop: Optional[AbstractEventLoop] = None) -> None\n

Initialize a new posix named pipe.

Arguments:

  • in_path: rendezvous point for incoming data
  • out_path: rendezvous point for outgoing data
  • logger: the logger
  • loop: the event loop

"},{"location":"aea-framework-documentation/api/helpers/pipe/#connect_1","title":"connect","text":"
async def connect(timeout: float = PIPE_CONN_TIMEOUT) -> bool\n

Connect to the other end of the pipe

Arguments:

  • timeout: timeout before failing

Returns:

connection success

"},{"location":"aea-framework-documentation/api/helpers/pipe/#write_1","title":"write","text":"
async def write(data: bytes) -> None\n

Write to pipe.

Arguments:

  • data: bytes to write to pipe

"},{"location":"aea-framework-documentation/api/helpers/pipe/#read_1","title":"read","text":"
async def read() -> Optional[bytes]\n

Read from pipe.

Returns:

read bytes

"},{"location":"aea-framework-documentation/api/helpers/pipe/#close_1","title":"close","text":"
async def close() -> None\n

Disconnect pipe.

"},{"location":"aea-framework-documentation/api/helpers/pipe/#tcpsocketprotocol-objects","title":"TCPSocketProtocol Objects","text":"
class TCPSocketProtocol()\n

TCP socket communication protocol.

"},{"location":"aea-framework-documentation/api/helpers/pipe/#__init___1","title":"__init__","text":"
def __init__(reader: asyncio.StreamReader,\nwriter: asyncio.StreamWriter,\nlogger: logging.Logger = _default_logger,\nloop: Optional[AbstractEventLoop] = None) -> None\n

Initialize the tcp socket protocol.

Arguments:

  • reader: established asyncio reader
  • writer: established asyncio writer
  • logger: the logger
  • loop: the event loop

"},{"location":"aea-framework-documentation/api/helpers/pipe/#writer","title":"writer","text":"
@property\ndef writer() -> StreamWriter\n

Get a writer associated with protocol.

"},{"location":"aea-framework-documentation/api/helpers/pipe/#write_2","title":"write","text":"
async def write(data: bytes) -> None\n

Write to socket.

Arguments:

  • data: bytes to write

"},{"location":"aea-framework-documentation/api/helpers/pipe/#read_2","title":"read","text":"
async def read() -> Optional[bytes]\n

Read from socket.

Returns:

read bytes

"},{"location":"aea-framework-documentation/api/helpers/pipe/#close_2","title":"close","text":"
async def close() -> None\n

Disconnect socket.

"},{"location":"aea-framework-documentation/api/helpers/pipe/#tcpsocketchannel-objects","title":"TCPSocketChannel Objects","text":"
class TCPSocketChannel(IPCChannel)\n

Interprocess communication channel implementation using tcp sockets.

"},{"location":"aea-framework-documentation/api/helpers/pipe/#__init___2","title":"__init__","text":"
def __init__(logger: logging.Logger = _default_logger,\nloop: Optional[AbstractEventLoop] = None) -> None\n

Initialize tcp socket interprocess communication channel.

"},{"location":"aea-framework-documentation/api/helpers/pipe/#connect_2","title":"connect","text":"
async def connect(timeout: float = PIPE_CONN_TIMEOUT) -> bool\n

Setup communication channel and wait for other end to connect.

Arguments:

  • timeout: timeout for the connection to be established

Returns:

connection status

"},{"location":"aea-framework-documentation/api/helpers/pipe/#write_3","title":"write","text":"
async def write(data: bytes) -> None\n

Write to channel.

Arguments:

  • data: bytes to write

"},{"location":"aea-framework-documentation/api/helpers/pipe/#read_3","title":"read","text":"
async def read() -> Optional[bytes]\n

Read from channel.

Returns:

read bytes

"},{"location":"aea-framework-documentation/api/helpers/pipe/#close_3","title":"close","text":"
async def close() -> None\n

Disconnect from channel and clean it up.

"},{"location":"aea-framework-documentation/api/helpers/pipe/#in_path_1","title":"in_path","text":"
@property\ndef in_path() -> str\n

Rendezvous point for incoming communication.

"},{"location":"aea-framework-documentation/api/helpers/pipe/#out_path_1","title":"out_path","text":"
@property\ndef out_path() -> str\n

Rendezvous point for outgoing communication.

"},{"location":"aea-framework-documentation/api/helpers/pipe/#posixnamedpipechannel-objects","title":"PosixNamedPipeChannel Objects","text":"
class PosixNamedPipeChannel(IPCChannel)\n

Interprocess communication channel implementation using Posix named pipes.

"},{"location":"aea-framework-documentation/api/helpers/pipe/#__init___3","title":"__init__","text":"
def __init__(logger: logging.Logger = _default_logger,\nloop: Optional[AbstractEventLoop] = None) -> None\n

Initialize posix named pipe interprocess communication channel.

"},{"location":"aea-framework-documentation/api/helpers/pipe/#connect_3","title":"connect","text":"
async def connect(timeout: float = PIPE_CONN_TIMEOUT) -> bool\n

Setup communication channel and wait for other end to connect.

Arguments:

  • timeout: timeout for connection to be established

Returns:

bool, indicating success

"},{"location":"aea-framework-documentation/api/helpers/pipe/#write_4","title":"write","text":"
async def write(data: bytes) -> None\n

Write to the channel.

Arguments:

  • data: data to write to channel

"},{"location":"aea-framework-documentation/api/helpers/pipe/#read_4","title":"read","text":"
async def read() -> Optional[bytes]\n

Read from the channel.

Returns:

read bytes

"},{"location":"aea-framework-documentation/api/helpers/pipe/#close_4","title":"close","text":"
async def close() -> None\n

Close the channel and clean it up.

"},{"location":"aea-framework-documentation/api/helpers/pipe/#in_path_2","title":"in_path","text":"
@property\ndef in_path() -> str\n

Rendezvous point for incoming communication.

"},{"location":"aea-framework-documentation/api/helpers/pipe/#out_path_2","title":"out_path","text":"
@property\ndef out_path() -> str\n

Rendezvous point for outgoing communication.

"},{"location":"aea-framework-documentation/api/helpers/pipe/#tcpsocketchannelclient-objects","title":"TCPSocketChannelClient Objects","text":"
class TCPSocketChannelClient(IPCChannelClient)\n

Interprocess communication channel client using tcp sockets.

"},{"location":"aea-framework-documentation/api/helpers/pipe/#__init___4","title":"__init__","text":"
def __init__(in_path: str,\nout_path: str,\nlogger: logging.Logger = _default_logger,\nloop: Optional[AbstractEventLoop] = None) -> None\n

Initialize a tcp socket communication channel client.

Arguments:

  • in_path: rendezvous point for incoming data
  • out_path: rendezvous point for outgoing data
  • logger: the logger
  • loop: the event loop

"},{"location":"aea-framework-documentation/api/helpers/pipe/#connect_4","title":"connect","text":"
async def connect(timeout: float = PIPE_CONN_TIMEOUT) -> bool\n

Connect to the other end of the communication channel.

Arguments:

  • timeout: timeout for connection to be established

Returns:

connection status

"},{"location":"aea-framework-documentation/api/helpers/pipe/#write_5","title":"write","text":"
async def write(data: bytes) -> None\n

Write data to channel.

Arguments:

  • data: bytes to write

"},{"location":"aea-framework-documentation/api/helpers/pipe/#read_5","title":"read","text":"
async def read() -> Optional[bytes]\n

Read data from channel.

Returns:

read bytes

"},{"location":"aea-framework-documentation/api/helpers/pipe/#close_5","title":"close","text":"
async def close() -> None\n

Disconnect from communication channel.

"},{"location":"aea-framework-documentation/api/helpers/pipe/#posixnamedpipechannelclient-objects","title":"PosixNamedPipeChannelClient Objects","text":"
class PosixNamedPipeChannelClient(IPCChannelClient)\n

Interprocess communication channel client using Posix named pipes.

"},{"location":"aea-framework-documentation/api/helpers/pipe/#__init___5","title":"__init__","text":"
def __init__(in_path: str,\nout_path: str,\nlogger: logging.Logger = _default_logger,\nloop: Optional[AbstractEventLoop] = None) -> None\n

Initialize a posix named pipe communication channel client.

Arguments:

  • in_path: rendezvous point for incoming data
  • out_path: rendezvous point for outgoing data
  • logger: the logger
  • loop: the event loop

"},{"location":"aea-framework-documentation/api/helpers/pipe/#connect_5","title":"connect","text":"
async def connect(timeout: float = PIPE_CONN_TIMEOUT) -> bool\n

Connect to the other end of the communication channel.

Arguments:

  • timeout: timeout for connection to be established

Returns:

connection status

"},{"location":"aea-framework-documentation/api/helpers/pipe/#write_6","title":"write","text":"
async def write(data: bytes) -> None\n

Write data to channel.

Arguments:

  • data: bytes to write

"},{"location":"aea-framework-documentation/api/helpers/pipe/#read_6","title":"read","text":"
async def read() -> Optional[bytes]\n

Read data from channel.

Returns:

read bytes

"},{"location":"aea-framework-documentation/api/helpers/pipe/#close_6","title":"close","text":"
async def close() -> None\n

Disconnect from communication channel.

"},{"location":"aea-framework-documentation/api/helpers/pipe/#make_ipc_channel","title":"make_ipc_channel","text":"
def make_ipc_channel(logger: logging.Logger = _default_logger,\nloop: Optional[AbstractEventLoop] = None) -> IPCChannel\n

Build a portable bidirectional InterProcess Communication channel

Arguments:

  • logger: the logger
  • loop: the loop

Returns:

IPCChannel

"},{"location":"aea-framework-documentation/api/helpers/pipe/#make_ipc_channel_client","title":"make_ipc_channel_client","text":"
def make_ipc_channel_client(\nin_path: str,\nout_path: str,\nlogger: logging.Logger = _default_logger,\nloop: Optional[AbstractEventLoop] = None) -> IPCChannelClient\n

Build a portable bidirectional InterProcess Communication client channel

Arguments:

  • in_path: rendezvous point for incoming communication
  • out_path: rendezvous point for outgoing outgoing
  • logger: the logger
  • loop: the loop

Returns:

IPCChannel

"},{"location":"aea-framework-documentation/api/helpers/profiling/","title":"Profiling","text":""},{"location":"aea-framework-documentation/api/helpers/profiling/#aeahelpersprofiling","title":"aea.helpers.profiling","text":"

Implementation of background profiling daemon.

"},{"location":"aea-framework-documentation/api/helpers/profiling/#profiling-objects","title":"Profiling Objects","text":"
class Profiling(Runnable)\n

Profiling service.

"},{"location":"aea-framework-documentation/api/helpers/profiling/#__init__","title":"__init__","text":"
def __init__(\nperiod: int = 0,\nobjects_instances_to_count: List[Type] = None,\nobjects_created_to_count: List[Type] = None,\noutput_function: Callable[[str], None] = lambda x: print(x, flush=True)\n) -> None\n

Init profiler.

Arguments:

  • period: delay between profiling output in seconds.
  • objects_instances_to_count: object to count
  • objects_created_to_count: object created to count
  • output_function: function to display output, one str argument.

"},{"location":"aea-framework-documentation/api/helpers/profiling/#set_counters","title":"set_counters","text":"
def set_counters() -> None\n

Modify obj.new to count objects created created.

"},{"location":"aea-framework-documentation/api/helpers/profiling/#run","title":"run","text":"
async def run() -> None\n

Run profiling.

"},{"location":"aea-framework-documentation/api/helpers/profiling/#output_profile_data","title":"output_profile_data","text":"
def output_profile_data() -> None\n

Render profiling data and call output_function.

"},{"location":"aea-framework-documentation/api/helpers/profiling/#get_profile_data","title":"get_profile_data","text":"
def get_profile_data() -> Dict\n

Get profiling data dict.

"},{"location":"aea-framework-documentation/api/helpers/profiling/#get_objects_instances","title":"get_objects_instances","text":"
def get_objects_instances() -> Dict\n

Return dict with counted object instances present now.

"},{"location":"aea-framework-documentation/api/helpers/profiling/#get_objecst_created","title":"get_objecst_created","text":"
def get_objecst_created() -> Dict\n

Return dict with counted object instances created.

"},{"location":"aea-framework-documentation/api/helpers/serializers/","title":"Serializers","text":""},{"location":"aea-framework-documentation/api/helpers/serializers/#aeahelpersserializers","title":"aea.helpers.serializers","text":"

This module contains Serializers that can be used for custom types.

"},{"location":"aea-framework-documentation/api/helpers/serializers/#dictprotobufstructserializer-objects","title":"DictProtobufStructSerializer Objects","text":"
class DictProtobufStructSerializer()\n

Serialize python dictionaries of type DictType = Dict[str, ValueType] recursively conserving their dynamic type, using google.protobuf.Struct

ValueType = PrimitiveType | DictType | List[ValueType]] PrimitiveType = bool | int | float | str | bytes

"},{"location":"aea-framework-documentation/api/helpers/serializers/#encode","title":"encode","text":"
@classmethod\ndef encode(cls, dictionary: Dict[str, Any]) -> bytes\n

Serialize compatible dictionary to bytes.

Copies entire dictionary in the process.

Arguments:

  • dictionary: the dictionary to serialize

Returns:

serialized bytes string

"},{"location":"aea-framework-documentation/api/helpers/serializers/#decode","title":"decode","text":"
@classmethod\ndef decode(cls, buffer: bytes) -> Dict[str, Any]\n

Deserialize a compatible dictionary

"},{"location":"aea-framework-documentation/api/helpers/sym_link/","title":"Sym Link","text":""},{"location":"aea-framework-documentation/api/helpers/sym_link/#aeahelperssym_link","title":"aea.helpers.sym_link","text":"

Sym link implementation for Linux, MacOS, and Windows.

"},{"location":"aea-framework-documentation/api/helpers/sym_link/#make_symlink","title":"make_symlink","text":"
def make_symlink(link_name: str, target: str) -> None\n

Make a symbolic link, cross platform.

Arguments:

  • link_name: the link name.
  • target: the target.

"},{"location":"aea-framework-documentation/api/helpers/sym_link/#cd","title":"cd","text":"
@contextlib.contextmanager\ndef cd(path: Path) -> Generator\n

Change directory with context manager.

"},{"location":"aea-framework-documentation/api/helpers/sym_link/#create_symlink","title":"create_symlink","text":"
def create_symlink(link_path: Path, target_path: Path, root_path: Path) -> int\n

Change directory and call the cross-platform script.

The working directory must be the parent of the symbolic link name when executing 'create_symlink_crossplatform.sh'. Hence, we need to translate target_path into the relative path from the symbolic link directory to the target directory.

So: 1) from link_path, extract the number of jumps to the parent directory in order to reach the repository root directory, and chain many \"../\" paths. 2) from target_path, compute the relative path to the root 3) relative_target_path is just the concatenation of the results from step (1) and (2).

For instance, given - link_path: './directory_1//symbolic_link - target_path: './directory_2/target_path

we want to compute: - link_path: 'symbolic_link' (just the last bit) - relative_target_path: '../../directory_1/target_path'

The resulting command on UNIX systems will be:

cd directory_1 && ln -s ../../directory_1/target_path symbolic_link\n

Arguments:

  • link_path: the source path
  • target_path: the target path
  • root_path: the root path

Returns:

exit code

"},{"location":"aea-framework-documentation/api/helpers/win32/","title":"Win32","text":""},{"location":"aea-framework-documentation/api/helpers/win32/#aeahelperswin32","title":"aea.helpers.win32","text":"

Helpers for Windows.

"},{"location":"aea-framework-documentation/api/helpers/win32/#enable_ctrl_c_support","title":"enable_ctrl_c_support","text":"
def enable_ctrl_c_support() -> None\n

Enable ctrl+c support for aea.cli command to be tested on windows platform.

"},{"location":"aea-framework-documentation/api/helpers/yaml_utils/","title":"YamlUtils","text":""},{"location":"aea-framework-documentation/api/helpers/yaml_utils/#aeahelpersyaml_utils","title":"aea.helpers.yaml_utils","text":"

Helper functions related to YAML loading/dumping.

"},{"location":"aea-framework-documentation/api/helpers/yaml_utils/#_aeayamlloader-objects","title":"_AEAYamlLoader Objects","text":"
class _AEAYamlLoader(yaml.SafeLoader)\n

Custom yaml.SafeLoader for the AEA framework.

It extends the default SafeLoader in two ways: - loads YAML configurations while remembering the order of the fields; - resolves the environment variables at loading time.

This class is for internal usage only; please use the public functions of the module 'yaml_load' and 'yaml_load_all'.

"},{"location":"aea-framework-documentation/api/helpers/yaml_utils/#__init__","title":"__init__","text":"
def __init__(*args: Any, **kwargs: Any) -> None\n

Initialize the AEAYamlLoader.

It adds a YAML Loader constructor to use 'OderedDict' to load the files.

Arguments:

  • args: the positional arguments.
  • kwargs: the keyword arguments.

"},{"location":"aea-framework-documentation/api/helpers/yaml_utils/#_aeayamldumper-objects","title":"_AEAYamlDumper Objects","text":"
class _AEAYamlDumper(yaml.SafeDumper)\n

Custom yaml.SafeDumper for the AEA framework.

It extends the default SafeDumper so to dump YAML configurations while following the order of the fields.

This class is for internal usage only; please use the public functions of the module 'yaml_dump' and 'yaml_dump_all'.

"},{"location":"aea-framework-documentation/api/helpers/yaml_utils/#__init___1","title":"__init__","text":"
def __init__(*args: Any, **kwargs: Any) -> None\n

Initialize the AEAYamlDumper.

It adds a YAML Dumper representer to use 'OderedDict' to dump the files.

Arguments:

  • args: the positional arguments.
  • kwargs: the keyword arguments.

"},{"location":"aea-framework-documentation/api/helpers/yaml_utils/#yaml_load","title":"yaml_load","text":"
def yaml_load(stream: TextIO) -> Dict[str, Any]\n

Load a yaml from a file pointer in an ordered way.

Arguments:

  • stream: file pointer to the input file.

Returns:

the dictionary object with the YAML file content.

"},{"location":"aea-framework-documentation/api/helpers/yaml_utils/#yaml_load_all","title":"yaml_load_all","text":"
def yaml_load_all(stream: TextIO) -> List[Dict[str, Any]]\n

Load a multi-paged yaml from a file pointer in an ordered way.

Arguments:

  • stream: file pointer to the input file.

Returns:

the list of dictionary objects with the (multi-paged) YAML file content.

"},{"location":"aea-framework-documentation/api/helpers/yaml_utils/#yaml_dump","title":"yaml_dump","text":"
def yaml_dump(data: Dict, stream: Optional[TextIO] = None) -> None\n

Dump YAML data to a yaml file in an ordered way.

Arguments:

  • data: the data to write.
  • stream: (optional) the file to write on.

"},{"location":"aea-framework-documentation/api/helpers/yaml_utils/#yaml_dump_all","title":"yaml_dump_all","text":"
def yaml_dump_all(data: Sequence[Dict],\nstream: Optional[TextIO] = None) -> None\n

Dump YAML data to a yaml file in an ordered way.

Arguments:

  • data: the data to write.
  • stream: (optional) the file to write on.
"},{"location":"aea-framework-documentation/api/helpers/acn/agent_record/","title":"Agent Record","text":""},{"location":"aea-framework-documentation/api/helpers/acn/agent_record/#aeahelpersacnagent_record","title":"aea.helpers.acn.agent_record","text":"

This module contains types and helpers for ACN Proof-of-Representation.

"},{"location":"aea-framework-documentation/api/helpers/acn/agent_record/#agentrecord-objects","title":"AgentRecord Objects","text":"
class AgentRecord()\n

Agent Proof-of-Representation to representative.

"},{"location":"aea-framework-documentation/api/helpers/acn/agent_record/#__init__","title":"__init__","text":"
def __init__(address: str, representative_public_key: str,\nidentifier: SimpleIdOrStr, ledger_id: SimpleIdOrStr,\nnot_before: str, not_after: str, message_format: str,\nsignature: str) -> None\n

Initialize the AgentRecord

Arguments:

  • address: agent address
  • representative_public_key: representative's public key
  • identifier: certificate identifier.
  • ledger_id: ledger identifier the request is referring to.
  • not_before: specify the lower bound for certificate validity. If it is a string, it must follow the format: 'YYYY-MM-DD'. It will be interpreted as timezone UTC-0.
  • not_after: specify the lower bound for certificate validity. If it is a string, it must follow the format: 'YYYY-MM-DD'. It will be interpreted as timezone UTC-0.
  • message_format: message format used for signing
  • signature: proof-of-representation of this AgentRecord

"},{"location":"aea-framework-documentation/api/helpers/acn/agent_record/#address","title":"address","text":"
@property\ndef address() -> str\n

Get agent address

"},{"location":"aea-framework-documentation/api/helpers/acn/agent_record/#public_key","title":"public_key","text":"
@property\ndef public_key() -> str\n

Get agent public key

"},{"location":"aea-framework-documentation/api/helpers/acn/agent_record/#representative_public_key","title":"representative_public_key","text":"
@property\ndef representative_public_key() -> str\n

Get agent representative's public key

"},{"location":"aea-framework-documentation/api/helpers/acn/agent_record/#signature","title":"signature","text":"
@property\ndef signature() -> str\n

Get record signature

"},{"location":"aea-framework-documentation/api/helpers/acn/agent_record/#message","title":"message","text":"
@property\ndef message() -> bytes\n

Get the message.

"},{"location":"aea-framework-documentation/api/helpers/acn/agent_record/#identifier","title":"identifier","text":"
@property\ndef identifier() -> SimpleIdOrStr\n

Get the identifier.

"},{"location":"aea-framework-documentation/api/helpers/acn/agent_record/#ledger_id","title":"ledger_id","text":"
@property\ndef ledger_id() -> SimpleIdOrStr\n

Get ledger id.

"},{"location":"aea-framework-documentation/api/helpers/acn/agent_record/#not_before","title":"not_before","text":"
@property\ndef not_before() -> str\n

Get the not_before field.

"},{"location":"aea-framework-documentation/api/helpers/acn/agent_record/#not_after","title":"not_after","text":"
@property\ndef not_after() -> str\n

Get the not_after field.

"},{"location":"aea-framework-documentation/api/helpers/acn/agent_record/#message_format","title":"message_format","text":"
@property\ndef message_format() -> str\n

Get the message format.

"},{"location":"aea-framework-documentation/api/helpers/acn/agent_record/#__str__","title":"__str__","text":"
def __str__() -> str\n

Get string representation.

"},{"location":"aea-framework-documentation/api/helpers/acn/agent_record/#from_cert_request","title":"from_cert_request","text":"
@classmethod\ndef from_cert_request(cls,\ncert_request: CertRequest,\naddress: str,\nrepresentative_public_key: str,\ndata_dir: Optional[PathLike] = None) -> \"AgentRecord\"\n

Get agent record from cert request.

"},{"location":"aea-framework-documentation/api/helpers/acn/uri/","title":"URI","text":""},{"location":"aea-framework-documentation/api/helpers/acn/uri/#aeahelpersacnuri","title":"aea.helpers.acn.uri","text":"

This module contains types and helpers for libp2p connections Uris.

"},{"location":"aea-framework-documentation/api/helpers/acn/uri/#uri-objects","title":"Uri Objects","text":"
class Uri()\n

Holds a node address in format \"host:port\".

"},{"location":"aea-framework-documentation/api/helpers/acn/uri/#__init__","title":"__init__","text":"
def __init__(uri: Optional[str] = None,\nhost: Optional[str] = None,\nport: Optional[int] = None) -> None\n

Initialise Uri.

"},{"location":"aea-framework-documentation/api/helpers/acn/uri/#__str__","title":"__str__","text":"
def __str__() -> str\n

Get string representation.

"},{"location":"aea-framework-documentation/api/helpers/acn/uri/#__repr__","title":"__repr__","text":"
def __repr__() -> str\n

Get object representation.

"},{"location":"aea-framework-documentation/api/helpers/acn/uri/#host","title":"host","text":"
@property\ndef host() -> str\n

Get host.

"},{"location":"aea-framework-documentation/api/helpers/acn/uri/#port","title":"port","text":"
@property\ndef port() -> int\n

Get port.

"},{"location":"aea-framework-documentation/api/helpers/ipfs/base/","title":"Base","text":""},{"location":"aea-framework-documentation/api/helpers/ipfs/base/#aeahelpersipfsbase","title":"aea.helpers.ipfs.base","text":"

This module contains helper methods and classes for the 'aea' package.

"},{"location":"aea-framework-documentation/api/helpers/ipfs/base/#chunks","title":"chunks","text":"
def chunks(data: Sized, size: int) -> Generator\n

Yield successivesize chunks from data.

"},{"location":"aea-framework-documentation/api/helpers/ipfs/base/#ipfshashonly-objects","title":"IPFSHashOnly Objects","text":"
class IPFSHashOnly()\n

A helper class which allows construction of an IPFS hash without interacting with an IPFS daemon.

"},{"location":"aea-framework-documentation/api/helpers/ipfs/base/#get","title":"get","text":"
def get(file_path: str) -> str\n

Get the IPFS hash for a single file.

Arguments:

  • file_path: the file path

Returns:

the ipfs hash

"},{"location":"aea-framework-documentation/api/helpers/ipfs/utils/","title":"Utils","text":""},{"location":"aea-framework-documentation/api/helpers/ipfs/utils/#aeahelpersipfsutils","title":"aea.helpers.ipfs.utils","text":"

This module contains utility methods for ipfs helpers.

"},{"location":"aea-framework-documentation/api/helpers/multiaddr/base/","title":"Base","text":""},{"location":"aea-framework-documentation/api/helpers/multiaddr/base/#aeahelpersmultiaddrbase","title":"aea.helpers.multiaddr.base","text":"

This module contains multiaddress class.

"},{"location":"aea-framework-documentation/api/helpers/multiaddr/base/#multiaddr-objects","title":"MultiAddr Objects","text":"
class MultiAddr()\n

Protocol Labs' Multiaddress representation of a network address.

"},{"location":"aea-framework-documentation/api/helpers/multiaddr/base/#__init__","title":"__init__","text":"
def __init__(host: str,\nport: int,\npublic_key: Optional[str] = None,\nmultihash_id: Optional[str] = None) -> None\n

Initialize a multiaddress.

Arguments:

  • host: ip host of the address
  • port: port number of the address
  • public_key: hex encoded public key. Must conform to Bitcoin EC encoding standard for Secp256k1
  • multihash_id: a multihash of the public key

"},{"location":"aea-framework-documentation/api/helpers/multiaddr/base/#compute_peerid","title":"compute_peerid","text":"
@staticmethod\ndef compute_peerid(public_key: str) -> str\n

Compute the peer id from a public key.

In particular, compute the base58 representation of libp2p PeerID from Bitcoin EC encoded Secp256k1 public key.

Arguments:

  • public_key: the public key.

Returns:

the peer id.

"},{"location":"aea-framework-documentation/api/helpers/multiaddr/base/#from_string","title":"from_string","text":"
@classmethod\ndef from_string(cls, maddr: str) -> \"MultiAddr\"\n

Construct a MultiAddr object from its string format

Arguments:

  • maddr: multiaddress string

Returns:

multiaddress object

"},{"location":"aea-framework-documentation/api/helpers/multiaddr/base/#public_key","title":"public_key","text":"
@property\ndef public_key() -> str\n

Get the public key.

"},{"location":"aea-framework-documentation/api/helpers/multiaddr/base/#peer_id","title":"peer_id","text":"
@property\ndef peer_id() -> str\n

Get the peer id.

"},{"location":"aea-framework-documentation/api/helpers/multiaddr/base/#host","title":"host","text":"
@property\ndef host() -> str\n

Get the peer host.

"},{"location":"aea-framework-documentation/api/helpers/multiaddr/base/#port","title":"port","text":"
@property\ndef port() -> int\n

Get the peer port.

"},{"location":"aea-framework-documentation/api/helpers/multiaddr/base/#format","title":"format","text":"
def format() -> str\n

Canonical representation of a multiaddress.

"},{"location":"aea-framework-documentation/api/helpers/multiaddr/base/#__str__","title":"__str__","text":"
def __str__() -> str\n

Default string representation of a multiaddress.

"},{"location":"aea-framework-documentation/api/helpers/preference_representations/base/","title":"Base","text":""},{"location":"aea-framework-documentation/api/helpers/preference_representations/base/#aeahelperspreference_representationsbase","title":"aea.helpers.preference_representations.base","text":"

Preference representation helpers.

"},{"location":"aea-framework-documentation/api/helpers/preference_representations/base/#logarithmic_utility","title":"logarithmic_utility","text":"
def logarithmic_utility(utility_params_by_good_id: Dict[str, float],\nquantities_by_good_id: Dict[str, int],\nquantity_shift: int = 100) -> float\n

Compute agent's utility given her utility function params and a good bundle.

Arguments:

  • utility_params_by_good_id: utility params by good identifier
  • quantities_by_good_id: quantities by good identifier
  • quantity_shift: a non-negative factor to shift the quantities in the utility function (to ensure the natural logarithm can be used on the entire range of quantities)

Returns:

utility value

"},{"location":"aea-framework-documentation/api/helpers/preference_representations/base/#linear_utility","title":"linear_utility","text":"
def linear_utility(exchange_params_by_currency_id: Dict[str, float],\nbalance_by_currency_id: Dict[str, int]) -> float\n

Compute agent's utility given her utility function params and a good bundle.

Arguments:

  • exchange_params_by_currency_id: exchange params by currency
  • balance_by_currency_id: balance by currency

Returns:

utility value

"},{"location":"aea-framework-documentation/api/helpers/search/generic/","title":"Generic","text":""},{"location":"aea-framework-documentation/api/helpers/search/generic/#aeahelperssearchgeneric","title":"aea.helpers.search.generic","text":"

This module contains a generic data model.

"},{"location":"aea-framework-documentation/api/helpers/search/generic/#genericdatamodel-objects","title":"GenericDataModel Objects","text":"
class GenericDataModel(DataModel)\n

Generic data model.

"},{"location":"aea-framework-documentation/api/helpers/search/generic/#__init__","title":"__init__","text":"
def __init__(data_model_name: str, data_model_attributes: Dict[str,\nAny]) -> None\n

Initialise the dataModel.

"},{"location":"aea-framework-documentation/api/helpers/storage/generic_storage/","title":"GenericStorage","text":""},{"location":"aea-framework-documentation/api/helpers/storage/generic_storage/#aeahelpersstoragegeneric_storage","title":"aea.helpers.storage.generic_storage","text":"

This module contains the storage implementation.

"},{"location":"aea-framework-documentation/api/helpers/storage/generic_storage/#asynccollection-objects","title":"AsyncCollection Objects","text":"
class AsyncCollection()\n

Async collection.

"},{"location":"aea-framework-documentation/api/helpers/storage/generic_storage/#__init__","title":"__init__","text":"
def __init__(storage_backend: AbstractStorageBackend,\ncollection_name: str) -> None\n

Init collection object.

Arguments:

  • storage_backend: storage backed to use.
  • collection_name: str

"},{"location":"aea-framework-documentation/api/helpers/storage/generic_storage/#put","title":"put","text":"
async def put(object_id: str, object_body: JSON_TYPES) -> None\n

Put object into collection.

Arguments:

  • object_id: str object id
  • object_body: python dict, json compatible.

Returns:

None

"},{"location":"aea-framework-documentation/api/helpers/storage/generic_storage/#get","title":"get","text":"
async def get(object_id: str) -> Optional[JSON_TYPES]\n

Get object from the collection.

Arguments:

  • object_id: str object id

Returns:

dict if object exists in collection otherwise None

"},{"location":"aea-framework-documentation/api/helpers/storage/generic_storage/#remove","title":"remove","text":"
async def remove(object_id: str) -> None\n

Remove object from the collection.

Arguments:

  • object_id: str object id

Returns:

None

"},{"location":"aea-framework-documentation/api/helpers/storage/generic_storage/#find","title":"find","text":"
async def find(field: str, equals: EQUALS_TYPE) -> List[OBJECT_ID_AND_BODY]\n

Get objects from the collection by filtering by field value.

Arguments:

  • field: field name to search: example \"parent.field\"
  • equals: value field should be equal to

Returns:

None

"},{"location":"aea-framework-documentation/api/helpers/storage/generic_storage/#list","title":"list","text":"
async def list() -> List[OBJECT_ID_AND_BODY]\n

List all objects with keys from the collection.

Returns:

Tuple of objects keys, bodies.

"},{"location":"aea-framework-documentation/api/helpers/storage/generic_storage/#synccollection-objects","title":"SyncCollection Objects","text":"
class SyncCollection()\n

Async collection.

"},{"location":"aea-framework-documentation/api/helpers/storage/generic_storage/#__init___1","title":"__init__","text":"
def __init__(async_collection_coro: Coroutine,\nloop: asyncio.AbstractEventLoop) -> None\n

Init collection object.

Arguments:

  • async_collection_coro: coroutine returns async collection.
  • loop: abstract event loop where storage is running.

"},{"location":"aea-framework-documentation/api/helpers/storage/generic_storage/#put_1","title":"put","text":"
def put(object_id: str, object_body: JSON_TYPES) -> None\n

Put object into collection.

Arguments:

  • object_id: str object id
  • object_body: python dict, json compatible.

Returns:

None

"},{"location":"aea-framework-documentation/api/helpers/storage/generic_storage/#get_1","title":"get","text":"
def get(object_id: str) -> Optional[JSON_TYPES]\n

Get object from the collection.

Arguments:

  • object_id: str object id

Returns:

dict if object exists in collection otherwise None

"},{"location":"aea-framework-documentation/api/helpers/storage/generic_storage/#remove_1","title":"remove","text":"
def remove(object_id: str) -> None\n

Remove object from the collection.

Arguments:

  • object_id: str object id

Returns:

None

"},{"location":"aea-framework-documentation/api/helpers/storage/generic_storage/#find_1","title":"find","text":"
def find(field: str, equals: EQUALS_TYPE) -> List[OBJECT_ID_AND_BODY]\n

Get objects from the collection by filtering by field value.

Arguments:

  • field: field name to search: example \"parent.field\"
  • equals: value field should be equal to

Returns:

List of object bodies

"},{"location":"aea-framework-documentation/api/helpers/storage/generic_storage/#list_1","title":"list","text":"
def list() -> List[OBJECT_ID_AND_BODY]\n

List all objects with keys from the collection.

Returns:

Tuple of objects keys, bodies.

"},{"location":"aea-framework-documentation/api/helpers/storage/generic_storage/#storage-objects","title":"Storage Objects","text":"
class Storage(Runnable)\n

Generic storage.

"},{"location":"aea-framework-documentation/api/helpers/storage/generic_storage/#__init___2","title":"__init__","text":"
def __init__(storage_uri: str,\nloop: asyncio.AbstractEventLoop = None,\nthreaded: bool = False) -> None\n

Init storage.

Arguments:

  • storage_uri: configuration string for storage.
  • loop: asyncio event loop to use.
  • threaded: bool. start in thread if True.

"},{"location":"aea-framework-documentation/api/helpers/storage/generic_storage/#wait_connected","title":"wait_connected","text":"
async def wait_connected() -> None\n

Wait generic storage is connected.

"},{"location":"aea-framework-documentation/api/helpers/storage/generic_storage/#is_connected","title":"is_connected","text":"
@property\ndef is_connected() -> bool\n

Get running state of the storage.

"},{"location":"aea-framework-documentation/api/helpers/storage/generic_storage/#run","title":"run","text":"
async def run() -> None\n

Connect storage.

"},{"location":"aea-framework-documentation/api/helpers/storage/generic_storage/#get_collection","title":"get_collection","text":"
async def get_collection(collection_name: str) -> AsyncCollection\n

Get async collection.

"},{"location":"aea-framework-documentation/api/helpers/storage/generic_storage/#get_sync_collection","title":"get_sync_collection","text":"
def get_sync_collection(collection_name: str) -> SyncCollection\n

Get sync collection.

"},{"location":"aea-framework-documentation/api/helpers/storage/generic_storage/#__repr__","title":"__repr__","text":"
def __repr__() -> str\n

Get string representation of the storage.

"},{"location":"aea-framework-documentation/api/helpers/storage/backends/base/","title":"Base","text":""},{"location":"aea-framework-documentation/api/helpers/storage/backends/base/#aeahelpersstoragebackendsbase","title":"aea.helpers.storage.backends.base","text":"

This module contains storage abstract backend class.

"},{"location":"aea-framework-documentation/api/helpers/storage/backends/base/#abstractstoragebackend-objects","title":"AbstractStorageBackend Objects","text":"
class AbstractStorageBackend(ABC)\n

Abstract base class for storage backend.

"},{"location":"aea-framework-documentation/api/helpers/storage/backends/base/#__init__","title":"__init__","text":"
def __init__(uri: str) -> None\n

Init backend.

"},{"location":"aea-framework-documentation/api/helpers/storage/backends/base/#connect","title":"connect","text":"
@abstractmethod\nasync def connect() -> None\n

Connect to backend.

"},{"location":"aea-framework-documentation/api/helpers/storage/backends/base/#disconnect","title":"disconnect","text":"
@abstractmethod\nasync def disconnect() -> None\n

Disconnect the backend.

"},{"location":"aea-framework-documentation/api/helpers/storage/backends/base/#ensure_collection","title":"ensure_collection","text":"
@abstractmethod\nasync def ensure_collection(collection_name: str) -> None\n

Create collection if not exits.

Arguments:

  • collection_name: str.

Returns:

None

"},{"location":"aea-framework-documentation/api/helpers/storage/backends/base/#put","title":"put","text":"
@abstractmethod\nasync def put(collection_name: str, object_id: str,\nobject_body: JSON_TYPES) -> None\n

Put object into collection.

Arguments:

  • collection_name: str.
  • object_id: str object id
  • object_body: python dict, json compatible.

Returns:

None

"},{"location":"aea-framework-documentation/api/helpers/storage/backends/base/#get","title":"get","text":"
@abstractmethod\nasync def get(collection_name: str, object_id: str) -> Optional[JSON_TYPES]\n

Get object from the collection.

Arguments:

  • collection_name: str.
  • object_id: str object id

Returns:

dict if object exists in collection otherwise None

"},{"location":"aea-framework-documentation/api/helpers/storage/backends/base/#remove","title":"remove","text":"
@abstractmethod\nasync def remove(collection_name: str, object_id: str) -> None\n

Remove object from the collection.

Arguments:

  • collection_name: str.
  • object_id: str object id

Returns:

None

"},{"location":"aea-framework-documentation/api/helpers/storage/backends/base/#find","title":"find","text":"
@abstractmethod\nasync def find(collection_name: str, field: str,\nequals: EQUALS_TYPE) -> List[OBJECT_ID_AND_BODY]\n

Get objects from the collection by filtering by field value.

Arguments:

  • collection_name: str.
  • field: field name to search: example \"parent.field\"
  • equals: value field should be equal to

Returns:

list of objects bodies

"},{"location":"aea-framework-documentation/api/helpers/storage/backends/base/#list","title":"list","text":"
@abstractmethod\nasync def list(collection_name: str) -> List[OBJECT_ID_AND_BODY]\n

List all objects with keys from the collection.

Arguments:

  • collection_name: str.

Returns:

Tuple of objects keys, bodies.

"},{"location":"aea-framework-documentation/api/helpers/storage/backends/sqlite/","title":"Sqlite","text":""},{"location":"aea-framework-documentation/api/helpers/storage/backends/sqlite/#aeahelpersstoragebackendssqlite","title":"aea.helpers.storage.backends.sqlite","text":"

This module contains sqlite storage backend implementation.

"},{"location":"aea-framework-documentation/api/helpers/storage/backends/sqlite/#sqlitestoragebackend-objects","title":"SqliteStorageBackend Objects","text":"
class SqliteStorageBackend(AbstractStorageBackend)\n

Sqlite storage backend.

"},{"location":"aea-framework-documentation/api/helpers/storage/backends/sqlite/#__init__","title":"__init__","text":"
def __init__(uri: str) -> None\n

Init backend.

"},{"location":"aea-framework-documentation/api/helpers/storage/backends/sqlite/#connect","title":"connect","text":"
async def connect() -> None\n

Connect to backend.

"},{"location":"aea-framework-documentation/api/helpers/storage/backends/sqlite/#disconnect","title":"disconnect","text":"
async def disconnect() -> None\n

Disconnect the backend.

"},{"location":"aea-framework-documentation/api/helpers/storage/backends/sqlite/#ensure_collection","title":"ensure_collection","text":"
async def ensure_collection(collection_name: str) -> None\n

Create collection if not exits.

Arguments:

  • collection_name: name of the collection.

"},{"location":"aea-framework-documentation/api/helpers/storage/backends/sqlite/#put","title":"put","text":"
async def put(collection_name: str, object_id: str,\nobject_body: JSON_TYPES) -> None\n

Put object into collection.

Arguments:

  • collection_name: str.
  • object_id: str object id
  • object_body: python dict, json compatible.

"},{"location":"aea-framework-documentation/api/helpers/storage/backends/sqlite/#get","title":"get","text":"
async def get(collection_name: str, object_id: str) -> Optional[JSON_TYPES]\n

Get object from the collection.

Arguments:

  • collection_name: str.
  • object_id: str object id

Returns:

dict if object exists in collection otherwise None

"},{"location":"aea-framework-documentation/api/helpers/storage/backends/sqlite/#remove","title":"remove","text":"
async def remove(collection_name: str, object_id: str) -> None\n

Remove object from the collection.

Arguments:

  • collection_name: str.
  • object_id: str object id

"},{"location":"aea-framework-documentation/api/helpers/storage/backends/sqlite/#find","title":"find","text":"
async def find(collection_name: str, field: str,\nequals: EQUALS_TYPE) -> List[OBJECT_ID_AND_BODY]\n

Get objects from the collection by filtering by field value.

Arguments:

  • collection_name: str.
  • field: field name to search: example \"parent.field\"
  • equals: value field should be equal to

Returns:

list of object ids and body

"},{"location":"aea-framework-documentation/api/helpers/storage/backends/sqlite/#list","title":"list","text":"
async def list(collection_name: str) -> List[OBJECT_ID_AND_BODY]\n

List all objects with keys from the collection.

Arguments:

  • collection_name: str.

Returns:

Tuple of objects keys, bodies.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/","title":"Base","text":""},{"location":"aea-framework-documentation/api/helpers/transaction/base/#aeahelperstransactionbase","title":"aea.helpers.transaction.base","text":"

This module contains terms related classes.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#rawtransaction-objects","title":"RawTransaction Objects","text":"
class RawTransaction()\n

This class represents an instance of RawTransaction.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#__init__","title":"__init__","text":"
def __init__(ledger_id: str, body: JSONLike) -> None\n

Initialise an instance of RawTransaction.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#ledger_id","title":"ledger_id","text":"
@property\ndef ledger_id() -> str\n

Get the id of the ledger on which the terms are to be settled.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#body","title":"body","text":"
@property\ndef body() -> JSONLike\n

Get the body.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#encode","title":"encode","text":"
@staticmethod\ndef encode(raw_transaction_protobuf_object: Any,\nraw_transaction_object: \"RawTransaction\") -> None\n

Encode an instance of this class into the protocol buffer object.

The protocol buffer object in the raw_transaction_protobuf_object argument must be matched with the instance of this class in the 'raw_transaction_object' argument.

Arguments:

  • raw_transaction_protobuf_object: the protocol buffer object whose type corresponds with this class.
  • raw_transaction_object: an instance of this class to be encoded in the protocol buffer object.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#decode","title":"decode","text":"
@classmethod\ndef decode(cls, raw_transaction_protobuf_object: Any) -> \"RawTransaction\"\n

Decode a protocol buffer object that corresponds with this class into an instance of this class.

A new instance of this class must be created that matches the protocol buffer object in the 'raw_transaction_protobuf_object' argument.

Arguments:

  • raw_transaction_protobuf_object: the protocol buffer object whose type corresponds with this class.

Returns:

A new instance of this class that matches the protocol buffer object in the 'raw_transaction_protobuf_object' argument.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#__eq__","title":"__eq__","text":"
def __eq__(other: Any) -> bool\n

Check equality.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#__str__","title":"__str__","text":"
def __str__() -> str\n

Get string representation.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#rawmessage-objects","title":"RawMessage Objects","text":"
class RawMessage()\n

This class represents an instance of RawMessage.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#__init___1","title":"__init__","text":"
def __init__(ledger_id: str,\nbody: bytes,\nis_deprecated_mode: bool = False) -> None\n

Initialise an instance of RawMessage.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#ledger_id_1","title":"ledger_id","text":"
@property\ndef ledger_id() -> str\n

Get the id of the ledger on which the terms are to be settled.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#body_1","title":"body","text":"
@property\ndef body() -> bytes\n

Get the body.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#is_deprecated_mode","title":"is_deprecated_mode","text":"
@property\ndef is_deprecated_mode() -> bool\n

Get the is_deprecated_mode.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#encode_1","title":"encode","text":"
@staticmethod\ndef encode(raw_message_protobuf_object: Any,\nraw_message_object: \"RawMessage\") -> None\n

Encode an instance of this class into the protocol buffer object.

The protocol buffer object in the raw_message_protobuf_object argument must be matched with the instance of this class in the 'raw_message_object' argument.

Arguments:

  • raw_message_protobuf_object: the protocol buffer object whose type corresponds with this class.
  • raw_message_object: an instance of this class to be encoded in the protocol buffer object.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#decode_1","title":"decode","text":"
@classmethod\ndef decode(cls, raw_message_protobuf_object: Any) -> \"RawMessage\"\n

Decode a protocol buffer object that corresponds with this class into an instance of this class.

A new instance of this class must be created that matches the protocol buffer object in the 'raw_message_protobuf_object' argument.

Arguments:

  • raw_message_protobuf_object: the protocol buffer object whose type corresponds with this class.

Returns:

A new instance of this class that matches the protocol buffer object in the 'raw_message_protobuf_object' argument.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#__eq___1","title":"__eq__","text":"
def __eq__(other: Any) -> bool\n

Check equality.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#__str___1","title":"__str__","text":"
def __str__() -> str\n

Get string representation.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#signedtransaction-objects","title":"SignedTransaction Objects","text":"
class SignedTransaction()\n

This class represents an instance of SignedTransaction.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#__init___2","title":"__init__","text":"
def __init__(ledger_id: str, body: JSONLike) -> None\n

Initialise an instance of SignedTransaction.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#ledger_id_2","title":"ledger_id","text":"
@property\ndef ledger_id() -> str\n

Get the id of the ledger on which the terms are to be settled.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#body_2","title":"body","text":"
@property\ndef body() -> JSONLike\n

Get the body.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#encode_2","title":"encode","text":"
@staticmethod\ndef encode(signed_transaction_protobuf_object: Any,\nsigned_transaction_object: \"SignedTransaction\") -> None\n

Encode an instance of this class into the protocol buffer object.

The protocol buffer object in the signed_transaction_protobuf_object argument must be matched with the instance of this class in the 'signed_transaction_object' argument.

Arguments:

  • signed_transaction_protobuf_object: the protocol buffer object whose type corresponds with this class.
  • signed_transaction_object: an instance of this class to be encoded in the protocol buffer object.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#decode_2","title":"decode","text":"
@classmethod\ndef decode(cls,\nsigned_transaction_protobuf_object: Any) -> \"SignedTransaction\"\n

Decode a protocol buffer object that corresponds with this class into an instance of this class.

A new instance of this class must be created that matches the protocol buffer object in the 'signed_transaction_protobuf_object' argument.

Arguments:

  • signed_transaction_protobuf_object: the protocol buffer object whose type corresponds with this class.

Returns:

A new instance of this class that matches the protocol buffer object in the 'signed_transaction_protobuf_object' argument.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#__eq___2","title":"__eq__","text":"
def __eq__(other: Any) -> bool\n

Check equality.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#__str___2","title":"__str__","text":"
def __str__() -> str\n

Get string representation.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#signedmessage-objects","title":"SignedMessage Objects","text":"
class SignedMessage()\n

This class represents an instance of RawMessage.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#__init___3","title":"__init__","text":"
def __init__(ledger_id: str,\nbody: str,\nis_deprecated_mode: bool = False) -> None\n

Initialise an instance of SignedMessage.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#ledger_id_3","title":"ledger_id","text":"
@property\ndef ledger_id() -> str\n

Get the id of the ledger on which the terms are to be settled.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#body_3","title":"body","text":"
@property\ndef body() -> str\n

Get the body.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#is_deprecated_mode_1","title":"is_deprecated_mode","text":"
@property\ndef is_deprecated_mode() -> bool\n

Get the is_deprecated_mode.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#encode_3","title":"encode","text":"
@staticmethod\ndef encode(signed_message_protobuf_object: Any,\nsigned_message_object: \"SignedMessage\") -> None\n

Encode an instance of this class into the protocol buffer object.

The protocol buffer object in the signed_message_protobuf_object argument must be matched with the instance of this class in the 'signed_message_object' argument.

Arguments:

  • signed_message_protobuf_object: the protocol buffer object whose type corresponds with this class.
  • signed_message_object: an instance of this class to be encoded in the protocol buffer object.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#decode_3","title":"decode","text":"
@classmethod\ndef decode(cls, signed_message_protobuf_object: Any) -> \"SignedMessage\"\n

Decode a protocol buffer object that corresponds with this class into an instance of this class.

A new instance of this class must be created that matches the protocol buffer object in the 'signed_message_protobuf_object' argument.

Arguments:

  • signed_message_protobuf_object: the protocol buffer object whose type corresponds with this class.

Returns:

A new instance of this class that matches the protocol buffer object in the 'signed_message_protobuf_object' argument.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#__eq___3","title":"__eq__","text":"
def __eq__(other: Any) -> bool\n

Check equality.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#__str___3","title":"__str__","text":"
def __str__() -> str\n

Get string representation.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#state-objects","title":"State Objects","text":"
class State()\n

This class represents an instance of State.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#__init___4","title":"__init__","text":"
def __init__(ledger_id: str, body: JSONLike) -> None\n

Initialise an instance of State.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#ledger_id_4","title":"ledger_id","text":"
@property\ndef ledger_id() -> str\n

Get the id of the ledger on which the terms are to be settled.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#body_4","title":"body","text":"
@property\ndef body() -> JSONLike\n

Get the body.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#encode_4","title":"encode","text":"
@staticmethod\ndef encode(state_protobuf_object: Any, state_object: \"State\") -> None\n

Encode an instance of this class into the protocol buffer object.

The protocol buffer object in the state_protobuf_object argument must be matched with the instance of this class in the 'state_object' argument.

Arguments:

  • state_protobuf_object: the protocol buffer object whose type corresponds with this class.
  • state_object: an instance of this class to be encoded in the protocol buffer object.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#decode_4","title":"decode","text":"
@classmethod\ndef decode(cls, state_protobuf_object: Any) -> \"State\"\n

Decode a protocol buffer object that corresponds with this class into an instance of this class.

A new instance of this class must be created that matches the protocol buffer object in the 'state_protobuf_object' argument.

Arguments:

  • state_protobuf_object: the protocol buffer object whose type corresponds with this class.

Returns:

A new instance of this class that matches the protocol buffer object in the 'state_protobuf_object' argument.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#__eq___4","title":"__eq__","text":"
def __eq__(other: Any) -> bool\n

Check equality.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#__str___4","title":"__str__","text":"
def __str__() -> str\n

Get string representation.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#terms-objects","title":"Terms Objects","text":"
class Terms()\n

Class to represent the terms of a multi-currency & multi-token ledger transaction.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#__init___5","title":"__init__","text":"
def __init__(ledger_id: str,\nsender_address: Address,\ncounterparty_address: Address,\namount_by_currency_id: Dict[str, int],\nquantities_by_good_id: Dict[str, int],\nnonce: str,\nis_sender_payable_tx_fee: bool = True,\nfee_by_currency_id: Optional[Dict[str, int]] = None,\nis_strict: bool = False,\n**kwargs: Any) -> None\n

Instantiate terms of a transaction.

Arguments:

  • ledger_id: the ledger on which the terms are to be settled.
  • sender_address: the sender address of the transaction.
  • counterparty_address: the counterparty address of the transaction.
  • amount_by_currency_id: the amount by the currency of the transaction.
  • quantities_by_good_id: a map from good id to the quantity of that good involved in the transaction.
  • nonce: nonce to be included in transaction to discriminate otherwise identical transactions.
  • is_sender_payable_tx_fee: whether the sender or counterparty pays the tx fee.
  • fee_by_currency_id: the fee associated with the transaction.
  • is_strict: whether or not terms must have quantities and amounts of opposite signs.
  • kwargs: keyword arguments

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#id","title":"id","text":"
@property\ndef id() -> str\n

Get hash of the terms.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#sender_hash","title":"sender_hash","text":"
@property\ndef sender_hash() -> str\n

Get the sender hash.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#counterparty_hash","title":"counterparty_hash","text":"
@property\ndef counterparty_hash() -> str\n

Get the sender hash.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#ledger_id_5","title":"ledger_id","text":"
@property\ndef ledger_id() -> str\n

Get the id of the ledger on which the terms are to be settled.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#sender_address","title":"sender_address","text":"
@property\ndef sender_address() -> Address\n

Get the sender address.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#counterparty_address","title":"counterparty_address","text":"
@property\ndef counterparty_address() -> Address\n

Get the counterparty address.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#counterparty_address_1","title":"counterparty_address","text":"
@counterparty_address.setter\ndef counterparty_address(counterparty_address: Address) -> None\n

Set the counterparty address.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#amount_by_currency_id","title":"amount_by_currency_id","text":"
@property\ndef amount_by_currency_id() -> Dict[str, int]\n

Get the amount by currency id.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#is_sender_payable_tx_fee","title":"is_sender_payable_tx_fee","text":"
@property\ndef is_sender_payable_tx_fee() -> bool\n

Bool indicating whether the tx fee is paid by sender or counterparty.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#is_single_currency","title":"is_single_currency","text":"
@property\ndef is_single_currency() -> bool\n

Check whether a single currency is used for payment.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#is_empty_currency","title":"is_empty_currency","text":"
@property\ndef is_empty_currency() -> bool\n

Check whether a single currency is used for payment.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#currency_id","title":"currency_id","text":"
@property\ndef currency_id() -> str\n

Get the amount the sender must pay.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#sender_payable_amount","title":"sender_payable_amount","text":"
@property\ndef sender_payable_amount() -> int\n

Get the amount the sender must pay.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#sender_payable_amount_incl_fee","title":"sender_payable_amount_incl_fee","text":"
@property\ndef sender_payable_amount_incl_fee() -> int\n

Get the amount the sender must pay inclusive fee.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#counterparty_payable_amount","title":"counterparty_payable_amount","text":"
@property\ndef counterparty_payable_amount() -> int\n

Get the amount the counterparty must pay.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#counterparty_payable_amount_incl_fee","title":"counterparty_payable_amount_incl_fee","text":"
@property\ndef counterparty_payable_amount_incl_fee() -> int\n

Get the amount the counterparty must pay.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#quantities_by_good_id","title":"quantities_by_good_id","text":"
@property\ndef quantities_by_good_id() -> Dict[str, int]\n

Get the quantities by good id.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#good_ids","title":"good_ids","text":"
@property\ndef good_ids() -> List[str]\n

Get the (ordered) good ids.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#sender_supplied_quantities","title":"sender_supplied_quantities","text":"
@property\ndef sender_supplied_quantities() -> List[int]\n

Get the (ordered) quantities supplied by the sender.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#counterparty_supplied_quantities","title":"counterparty_supplied_quantities","text":"
@property\ndef counterparty_supplied_quantities() -> List[int]\n

Get the (ordered) quantities supplied by the counterparty.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#nonce","title":"nonce","text":"
@property\ndef nonce() -> str\n

Get the nonce.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#has_fee","title":"has_fee","text":"
@property\ndef has_fee() -> bool\n

Check if fee is set.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#fee","title":"fee","text":"
@property\ndef fee() -> int\n

Get the fee.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#sender_fee","title":"sender_fee","text":"
@property\ndef sender_fee() -> int\n

Get the sender fee.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#counterparty_fee","title":"counterparty_fee","text":"
@property\ndef counterparty_fee() -> int\n

Get the counterparty fee.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#fee_by_currency_id","title":"fee_by_currency_id","text":"
@property\ndef fee_by_currency_id() -> Dict[str, int]\n

Get fee by currency.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#kwargs","title":"kwargs","text":"
@property\ndef kwargs() -> JSONLike\n

Get the kwargs.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#is_strict","title":"is_strict","text":"
@property\ndef is_strict() -> bool\n

Get is_strict.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#get_hash","title":"get_hash","text":"
@staticmethod\ndef get_hash(ledger_id: str, sender_address: str, counterparty_address: str,\ngood_ids: List[str], sender_supplied_quantities: List[int],\ncounterparty_supplied_quantities: List[int],\nsender_payable_amount: int, counterparty_payable_amount: int,\nnonce: str) -> str\n

Generate a hash from transaction information.

Arguments:

  • ledger_id: the ledger id
  • sender_address: the sender address
  • counterparty_address: the counterparty address
  • good_ids: the list of good ids
  • sender_supplied_quantities: the quantities supplied by the sender (must all be positive)
  • counterparty_supplied_quantities: the quantities supplied by the counterparty (must all be positive)
  • sender_payable_amount: the amount payable by the sender
  • counterparty_payable_amount: the amount payable by the counterparty
  • nonce: the nonce of the transaction

Returns:

the hash

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#encode_5","title":"encode","text":"
@staticmethod\ndef encode(terms_protobuf_object: Any, terms_object: \"Terms\") -> None\n

Encode an instance of this class into the protocol buffer object.

The protocol buffer object in the terms_protobuf_object argument must be matched with the instance of this class in the 'terms_object' argument.

Arguments:

  • terms_protobuf_object: the protocol buffer object whose type corresponds with this class.
  • terms_object: an instance of this class to be encoded in the protocol buffer object.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#decode_5","title":"decode","text":"
@classmethod\ndef decode(cls, terms_protobuf_object: Any) -> \"Terms\"\n

Decode a protocol buffer object that corresponds with this class into an instance of this class.

A new instance of this class must be created that matches the protocol buffer object in the 'terms_protobuf_object' argument.

Arguments:

  • terms_protobuf_object: the protocol buffer object whose type corresponds with this class.

Returns:

A new instance of this class that matches the protocol buffer object in the 'terms_protobuf_object' argument.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#__eq___5","title":"__eq__","text":"
def __eq__(other: Any) -> bool\n

Check equality.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#__str___5","title":"__str__","text":"
def __str__() -> str\n

Get string representation.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#transactiondigest-objects","title":"TransactionDigest Objects","text":"
class TransactionDigest()\n

This class represents an instance of TransactionDigest.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#__init___6","title":"__init__","text":"
def __init__(ledger_id: str, body: str) -> None\n

Initialise an instance of TransactionDigest.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#ledger_id_6","title":"ledger_id","text":"
@property\ndef ledger_id() -> str\n

Get the id of the ledger on which the terms are to be settled.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#body_5","title":"body","text":"
@property\ndef body() -> str\n

Get the receipt.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#encode_6","title":"encode","text":"
@staticmethod\ndef encode(transaction_digest_protobuf_object: Any,\ntransaction_digest_object: \"TransactionDigest\") -> None\n

Encode an instance of this class into the protocol buffer object.

The protocol buffer object in the transaction_digest_protobuf_object argument must be matched with the instance of this class in the 'transaction_digest_object' argument.

Arguments:

  • transaction_digest_protobuf_object: the protocol buffer object whose type corresponds with this class.
  • transaction_digest_object: an instance of this class to be encoded in the protocol buffer object.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#decode_6","title":"decode","text":"
@classmethod\ndef decode(cls,\ntransaction_digest_protobuf_object: Any) -> \"TransactionDigest\"\n

Decode a protocol buffer object that corresponds with this class into an instance of this class.

A new instance of this class must be created that matches the protocol buffer object in the 'transaction_digest_protobuf_object' argument.

Arguments:

  • transaction_digest_protobuf_object: the protocol buffer object whose type corresponds with this class.

Returns:

A new instance of this class that matches the protocol buffer object in the 'transaction_digest_protobuf_object' argument.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#__eq___6","title":"__eq__","text":"
def __eq__(other: Any) -> bool\n

Check equality.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#__str___6","title":"__str__","text":"
def __str__() -> str\n

Get string representation.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#transactionreceipt-objects","title":"TransactionReceipt Objects","text":"
class TransactionReceipt()\n

This class represents an instance of TransactionReceipt.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#__init___7","title":"__init__","text":"
def __init__(ledger_id: str, receipt: JSONLike, transaction: JSONLike) -> None\n

Initialise an instance of TransactionReceipt.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#ledger_id_7","title":"ledger_id","text":"
@property\ndef ledger_id() -> str\n

Get the id of the ledger on which the terms are to be settled.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#receipt","title":"receipt","text":"
@property\ndef receipt() -> JSONLike\n

Get the receipt.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#transaction","title":"transaction","text":"
@property\ndef transaction() -> JSONLike\n

Get the transaction.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#encode_7","title":"encode","text":"
@staticmethod\ndef encode(transaction_receipt_protobuf_object: Any,\ntransaction_receipt_object: \"TransactionReceipt\") -> None\n

Encode an instance of this class into the protocol buffer object.

The protocol buffer object in the transaction_receipt_protobuf_object argument must be matched with the instance of this class in the 'transaction_receipt_object' argument.

Arguments:

  • transaction_receipt_protobuf_object: the protocol buffer object whose type corresponds with this class.
  • transaction_receipt_object: an instance of this class to be encoded in the protocol buffer object.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#decode_7","title":"decode","text":"
@classmethod\ndef decode(cls,\ntransaction_receipt_protobuf_object: Any) -> \"TransactionReceipt\"\n

Decode a protocol buffer object that corresponds with this class into an instance of this class.

A new instance of this class must be created that matches the protocol buffer object in the 'transaction_receipt_protobuf_object' argument.

Arguments:

  • transaction_receipt_protobuf_object: the protocol buffer object whose type corresponds with this class.

Returns:

A new instance of this class that matches the protocol buffer object in the 'transaction_receipt_protobuf_object' argument.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#__eq___7","title":"__eq__","text":"
def __eq__(other: Any) -> bool\n

Check equality.

"},{"location":"aea-framework-documentation/api/helpers/transaction/base/#__str___7","title":"__str__","text":"
def __str__() -> str\n

Get string representation.

"},{"location":"aea-framework-documentation/api/identity/base/","title":"Identity","text":""},{"location":"aea-framework-documentation/api/identity/base/#aeaidentitybase","title":"aea.identity.base","text":"

This module contains the identity class.

"},{"location":"aea-framework-documentation/api/identity/base/#identity-objects","title":"Identity Objects","text":"
class Identity()\n

The identity holds the public elements identifying an agent.

It includes:

  • the agent name
  • the addresses, a map from address identifier to address (can be a single key-value pair)

"},{"location":"aea-framework-documentation/api/identity/base/#__init__","title":"__init__","text":"
def __init__(name: SimpleIdOrStr,\naddress: Optional[str] = None,\npublic_key: Optional[str] = None,\naddresses: Optional[Dict[str, Address]] = None,\npublic_keys: Optional[Dict[str, str]] = None,\ndefault_address_key: str = DEFAULT_LEDGER) -> None\n

Instantiate the identity.

Arguments:

  • name: the name of the agent.
  • address: the default address of the agent.
  • public_key: the public key of the agent.
  • addresses: the addresses of the agent.
  • public_keys: the public keys of the agent.
  • default_address_key: the key for the default address.

"},{"location":"aea-framework-documentation/api/identity/base/#default_address_key","title":"default_address_key","text":"
@property\ndef default_address_key() -> str\n

Get the default address key.

"},{"location":"aea-framework-documentation/api/identity/base/#name","title":"name","text":"
@property\ndef name() -> str\n

Get the agent name.

"},{"location":"aea-framework-documentation/api/identity/base/#addresses","title":"addresses","text":"
@property\ndef addresses() -> Dict[str, Address]\n

Get the addresses.

"},{"location":"aea-framework-documentation/api/identity/base/#address","title":"address","text":"
@property\ndef address() -> Address\n

Get the default address.

"},{"location":"aea-framework-documentation/api/identity/base/#public_keys","title":"public_keys","text":"
@property\ndef public_keys() -> Dict[str, str]\n

Get the public keys.

"},{"location":"aea-framework-documentation/api/identity/base/#public_key","title":"public_key","text":"
@property\ndef public_key() -> str\n

Get the default public key.

"},{"location":"aea-framework-documentation/api/mail/base/","title":"Mail","text":""},{"location":"aea-framework-documentation/api/mail/base/#aeamailbase","title":"aea.mail.base","text":"

Mail module abstract base classes.

"},{"location":"aea-framework-documentation/api/mail/base/#uri-objects","title":"URI Objects","text":"
class URI()\n

URI following RFC3986.

"},{"location":"aea-framework-documentation/api/mail/base/#__init__","title":"__init__","text":"
def __init__(uri_raw: str) -> None\n

Initialize the URI.

Must follow: https://tools.ietf.org/html/rfc3986.html

Arguments:

  • uri_raw: the raw form uri

"},{"location":"aea-framework-documentation/api/mail/base/#scheme","title":"scheme","text":"
@property\ndef scheme() -> str\n

Get the scheme.

"},{"location":"aea-framework-documentation/api/mail/base/#netloc","title":"netloc","text":"
@property\ndef netloc() -> str\n

Get the netloc.

"},{"location":"aea-framework-documentation/api/mail/base/#path","title":"path","text":"
@property\ndef path() -> str\n

Get the path.

"},{"location":"aea-framework-documentation/api/mail/base/#params","title":"params","text":"
@property\ndef params() -> str\n

Get the params.

"},{"location":"aea-framework-documentation/api/mail/base/#query","title":"query","text":"
@property\ndef query() -> str\n

Get the query.

"},{"location":"aea-framework-documentation/api/mail/base/#fragment","title":"fragment","text":"
@property\ndef fragment() -> str\n

Get the fragment.

"},{"location":"aea-framework-documentation/api/mail/base/#username","title":"username","text":"
@property\ndef username() -> Optional[str]\n

Get the username.

"},{"location":"aea-framework-documentation/api/mail/base/#password","title":"password","text":"
@property\ndef password() -> Optional[str]\n

Get the password.

"},{"location":"aea-framework-documentation/api/mail/base/#host","title":"host","text":"
@property\ndef host() -> Optional[str]\n

Get the host.

"},{"location":"aea-framework-documentation/api/mail/base/#port","title":"port","text":"
@property\ndef port() -> Optional[int]\n

Get the port.

"},{"location":"aea-framework-documentation/api/mail/base/#__str__","title":"__str__","text":"
def __str__() -> str\n

Get string representation.

"},{"location":"aea-framework-documentation/api/mail/base/#__eq__","title":"__eq__","text":"
def __eq__(other: Any) -> bool\n

Compare with another object.

"},{"location":"aea-framework-documentation/api/mail/base/#envelopecontext-objects","title":"EnvelopeContext Objects","text":"
class EnvelopeContext()\n

Contains context information of an envelope.

"},{"location":"aea-framework-documentation/api/mail/base/#__init___1","title":"__init__","text":"
def __init__(connection_id: Optional[PublicId] = None,\nuri: Optional[URI] = None) -> None\n

Initialize the envelope context.

Arguments:

  • connection_id: the connection id used for routing the outgoing envelope in the multiplexer.
  • uri: the URI sent with the envelope.

"},{"location":"aea-framework-documentation/api/mail/base/#uri","title":"uri","text":"
@property\ndef uri() -> Optional[URI]\n

Get the URI.

"},{"location":"aea-framework-documentation/api/mail/base/#connection_id","title":"connection_id","text":"
@property\ndef connection_id() -> Optional[PublicId]\n

Get the connection id to route the envelope.

"},{"location":"aea-framework-documentation/api/mail/base/#connection_id_1","title":"connection_id","text":"
@connection_id.setter\ndef connection_id(connection_id: PublicId) -> None\n

Set the 'via' connection id.

"},{"location":"aea-framework-documentation/api/mail/base/#__str___1","title":"__str__","text":"
def __str__() -> str\n

Get the string representation.

"},{"location":"aea-framework-documentation/api/mail/base/#__eq___1","title":"__eq__","text":"
def __eq__(other: Any) -> bool\n

Compare with another object.

"},{"location":"aea-framework-documentation/api/mail/base/#aeaconnectionerror-objects","title":"AEAConnectionError Objects","text":"
class AEAConnectionError(Exception)\n

Exception class for connection errors.

"},{"location":"aea-framework-documentation/api/mail/base/#empty-objects","title":"Empty Objects","text":"
class Empty(Exception)\n

Exception for when the inbox is empty.

"},{"location":"aea-framework-documentation/api/mail/base/#envelopeserializer-objects","title":"EnvelopeSerializer Objects","text":"
class EnvelopeSerializer(ABC)\n

Abstract class to specify the serialization layer for the envelope.

"},{"location":"aea-framework-documentation/api/mail/base/#encode","title":"encode","text":"
@abstractmethod\ndef encode(envelope: \"Envelope\") -> bytes\n

Encode the envelope.

Arguments:

  • envelope: the envelope to encode

Returns:

the encoded envelope

"},{"location":"aea-framework-documentation/api/mail/base/#decode","title":"decode","text":"
@abstractmethod\ndef decode(envelope_bytes: bytes) -> \"Envelope\"\n

Decode the envelope.

Arguments:

  • envelope_bytes: the encoded envelope

Returns:

the envelope

"},{"location":"aea-framework-documentation/api/mail/base/#protobufenvelopeserializer-objects","title":"ProtobufEnvelopeSerializer Objects","text":"
class ProtobufEnvelopeSerializer(EnvelopeSerializer)\n

Envelope serializer using Protobuf.

"},{"location":"aea-framework-documentation/api/mail/base/#encode_1","title":"encode","text":"
def encode(envelope: \"Envelope\") -> bytes\n

Encode the envelope.

Arguments:

  • envelope: the envelope to encode

Returns:

the encoded envelope

"},{"location":"aea-framework-documentation/api/mail/base/#decode_1","title":"decode","text":"
def decode(envelope_bytes: bytes) -> \"Envelope\"\n

Decode the envelope.

The default serializer doesn't decode the message field.

Arguments:

  • envelope_bytes: the encoded envelope

Returns:

the envelope

"},{"location":"aea-framework-documentation/api/mail/base/#envelope-objects","title":"Envelope Objects","text":"
class Envelope()\n

The top level message class for agent to agent communication.

"},{"location":"aea-framework-documentation/api/mail/base/#__init___2","title":"__init__","text":"
def __init__(to: Address,\nsender: Address,\nmessage: Union[Message, bytes],\ncontext: Optional[EnvelopeContext] = None,\nprotocol_specification_id: Optional[PublicId] = None) -> None\n

Initialize a Message object.

Arguments:

  • to: the address of the receiver.
  • sender: the address of the sender.
  • message: the protocol-specific message.
  • context: the optional envelope context.
  • protocol_specification_id: the protocol specification id (wire id).

"},{"location":"aea-framework-documentation/api/mail/base/#to","title":"to","text":"
@property\ndef to() -> Address\n

Get address of receiver.

"},{"location":"aea-framework-documentation/api/mail/base/#to_1","title":"to","text":"
@to.setter\ndef to(to: Address) -> None\n

Set address of receiver.

"},{"location":"aea-framework-documentation/api/mail/base/#sender","title":"sender","text":"
@property\ndef sender() -> Address\n

Get address of sender.

"},{"location":"aea-framework-documentation/api/mail/base/#sender_1","title":"sender","text":"
@sender.setter\ndef sender(sender: Address) -> None\n

Set address of sender.

"},{"location":"aea-framework-documentation/api/mail/base/#protocol_specification_id","title":"protocol_specification_id","text":"
@property\ndef protocol_specification_id() -> PublicId\n

Get protocol_specification_id.

"},{"location":"aea-framework-documentation/api/mail/base/#message","title":"message","text":"
@property\ndef message() -> Union[Message, bytes]\n

Get the protocol-specific message.

"},{"location":"aea-framework-documentation/api/mail/base/#message_1","title":"message","text":"
@message.setter\ndef message(message: Union[Message, bytes]) -> None\n

Set the protocol-specific message.

"},{"location":"aea-framework-documentation/api/mail/base/#message_bytes","title":"message_bytes","text":"
@property\ndef message_bytes() -> bytes\n

Get the protocol-specific message.

"},{"location":"aea-framework-documentation/api/mail/base/#context","title":"context","text":"
@property\ndef context() -> Optional[EnvelopeContext]\n

Get the envelope context.

"},{"location":"aea-framework-documentation/api/mail/base/#to_as_public_id","title":"to_as_public_id","text":"
@property\ndef to_as_public_id() -> Optional[PublicId]\n

Get to as public id.

"},{"location":"aea-framework-documentation/api/mail/base/#is_sender_public_id","title":"is_sender_public_id","text":"
@property\ndef is_sender_public_id() -> bool\n

Check if sender is a public id.

"},{"location":"aea-framework-documentation/api/mail/base/#is_to_public_id","title":"is_to_public_id","text":"
@property\ndef is_to_public_id() -> bool\n

Check if to is a public id.

"},{"location":"aea-framework-documentation/api/mail/base/#is_component_to_component_message","title":"is_component_to_component_message","text":"
@property\ndef is_component_to_component_message() -> bool\n

Whether or not the message contained is component to component.

"},{"location":"aea-framework-documentation/api/mail/base/#__eq___2","title":"__eq__","text":"
def __eq__(other: Any) -> bool\n

Compare with another object.

"},{"location":"aea-framework-documentation/api/mail/base/#encode_2","title":"encode","text":"
def encode(serializer: Optional[EnvelopeSerializer] = None) -> bytes\n

Encode the envelope.

Arguments:

  • serializer: the serializer that implements the encoding procedure.

Returns:

the encoded envelope.

"},{"location":"aea-framework-documentation/api/mail/base/#decode_2","title":"decode","text":"
@classmethod\ndef decode(cls,\nenvelope_bytes: bytes,\nserializer: Optional[EnvelopeSerializer] = None) -> \"Envelope\"\n

Decode the envelope.

Arguments:

  • envelope_bytes: the bytes to be decoded.
  • serializer: the serializer that implements the decoding procedure.

Returns:

the decoded envelope.

"},{"location":"aea-framework-documentation/api/mail/base/#__str___2","title":"__str__","text":"
def __str__() -> str\n

Get the string representation of an envelope.

"},{"location":"aea-framework-documentation/api/manager/manager/","title":"Manager","text":""},{"location":"aea-framework-documentation/api/manager/manager/#aeamanagermanager","title":"aea.manager.manager","text":"

This module contains the implementation of AEA agents manager.

"},{"location":"aea-framework-documentation/api/manager/manager/#projectnotfounderror-objects","title":"ProjectNotFoundError Objects","text":"
class ProjectNotFoundError(ValueError)\n

Project not found exception.

"},{"location":"aea-framework-documentation/api/manager/manager/#projectcheckerror-objects","title":"ProjectCheckError Objects","text":"
class ProjectCheckError(ValueError)\n

Project check error exception.

"},{"location":"aea-framework-documentation/api/manager/manager/#__init__","title":"__init__","text":"
def __init__(msg: str, source_exception: Exception)\n

Init exception.

"},{"location":"aea-framework-documentation/api/manager/manager/#projectpackageconsistencycheckerror-objects","title":"ProjectPackageConsistencyCheckError Objects","text":"
class ProjectPackageConsistencyCheckError(ValueError)\n

Check consistency of package versions against already added project.

"},{"location":"aea-framework-documentation/api/manager/manager/#__init___1","title":"__init__","text":"
def __init__(agent_project_id: PublicId,\nconflicting_packages: List[Tuple[PackageIdPrefix, str, str,\nSet[PublicId]]])\n

Initialize the exception.

Arguments:

  • agent_project_id: the agent project id whose addition has failed.
  • conflicting_packages: the conflicting packages.

"},{"location":"aea-framework-documentation/api/manager/manager/#baseagentruntask-objects","title":"BaseAgentRunTask Objects","text":"
class BaseAgentRunTask(ABC)\n

Base abstract class for agent run tasks.

"},{"location":"aea-framework-documentation/api/manager/manager/#start","title":"start","text":"
@abstractmethod\ndef start() -> None\n

Start task.

"},{"location":"aea-framework-documentation/api/manager/manager/#wait","title":"wait","text":"
@abstractmethod\ndef wait() -> asyncio.Future\n

Return future to wait task completed.

"},{"location":"aea-framework-documentation/api/manager/manager/#stop","title":"stop","text":"
@abstractmethod\ndef stop() -> None\n

Stop task.

"},{"location":"aea-framework-documentation/api/manager/manager/#is_running","title":"is_running","text":"
@property\n@abstractmethod\ndef is_running() -> bool\n

Return is task running.

"},{"location":"aea-framework-documentation/api/manager/manager/#agentrunasynctask-objects","title":"AgentRunAsyncTask Objects","text":"
class AgentRunAsyncTask(BaseAgentRunTask)\n

Async task wrapper for agent.

"},{"location":"aea-framework-documentation/api/manager/manager/#__init___2","title":"__init__","text":"
def __init__(agent: AEA, loop: asyncio.AbstractEventLoop) -> None\n

Init task with agent alias and loop.

"},{"location":"aea-framework-documentation/api/manager/manager/#create_run_loop","title":"create_run_loop","text":"
def create_run_loop() -> None\n

Create run loop.

"},{"location":"aea-framework-documentation/api/manager/manager/#start_1","title":"start","text":"
def start() -> None\n

Start task.

"},{"location":"aea-framework-documentation/api/manager/manager/#wait_1","title":"wait","text":"
def wait() -> asyncio.Future\n

Return future to wait task completed.

"},{"location":"aea-framework-documentation/api/manager/manager/#stop_1","title":"stop","text":"
def stop() -> None\n

Stop task.

"},{"location":"aea-framework-documentation/api/manager/manager/#run","title":"run","text":"
async def run() -> None\n

Run task body.

"},{"location":"aea-framework-documentation/api/manager/manager/#is_running_1","title":"is_running","text":"
@property\ndef is_running() -> bool\n

Return is task running.

"},{"location":"aea-framework-documentation/api/manager/manager/#agentrunthreadtask-objects","title":"AgentRunThreadTask Objects","text":"
class AgentRunThreadTask(AgentRunAsyncTask)\n

Threaded wrapper to run agent.

"},{"location":"aea-framework-documentation/api/manager/manager/#__init___3","title":"__init__","text":"
def __init__(agent: AEA, loop: asyncio.AbstractEventLoop) -> None\n

Init task with agent alias and loop.

"},{"location":"aea-framework-documentation/api/manager/manager/#create_run_loop_1","title":"create_run_loop","text":"
def create_run_loop() -> None\n

Create run loop.

"},{"location":"aea-framework-documentation/api/manager/manager/#start_2","title":"start","text":"
def start() -> None\n

Run task in a dedicated thread.

"},{"location":"aea-framework-documentation/api/manager/manager/#stop_2","title":"stop","text":"
def stop() -> None\n

Stop the task.

"},{"location":"aea-framework-documentation/api/manager/manager/#agentrunprocesstask-objects","title":"AgentRunProcessTask Objects","text":"
class AgentRunProcessTask(BaseAgentRunTask)\n

Subprocess wrapper to run agent.

"},{"location":"aea-framework-documentation/api/manager/manager/#__init___4","title":"__init__","text":"
def __init__(agent_alias: AgentAlias, loop: asyncio.AbstractEventLoop) -> None\n

Init task with agent alias and loop.

"},{"location":"aea-framework-documentation/api/manager/manager/#start_3","title":"start","text":"
def start() -> None\n

Run task in a dedicated process.

"},{"location":"aea-framework-documentation/api/manager/manager/#wait_2","title":"wait","text":"
def wait() -> asyncio.Future\n

Return future to wait task completed.

"},{"location":"aea-framework-documentation/api/manager/manager/#stop_3","title":"stop","text":"
def stop() -> None\n

Stop the task.

"},{"location":"aea-framework-documentation/api/manager/manager/#is_running_2","title":"is_running","text":"
@property\ndef is_running() -> bool\n

Is agent running.

"},{"location":"aea-framework-documentation/api/manager/manager/#multiagentmanager-objects","title":"MultiAgentManager Objects","text":"
class MultiAgentManager()\n

Multi agents manager.

"},{"location":"aea-framework-documentation/api/manager/manager/#__init___5","title":"__init__","text":"
def __init__(working_dir: str,\nmode: str = \"async\",\nregistry_path: str = DEFAULT_REGISTRY_NAME,\nauto_add_remove_project: bool = False,\npassword: Optional[str] = None) -> None\n

Initialize manager.

Arguments:

  • working_dir: directory to store base agents.
  • mode: str. async or threaded
  • registry_path: str. path to the local packages registry
  • auto_add_remove_project: bool. add/remove project on the first agent add/last agent remove
  • password: the password to encrypt/decrypt the private key.

"},{"location":"aea-framework-documentation/api/manager/manager/#data_dir","title":"data_dir","text":"
@property\ndef data_dir() -> str\n

Get the certs directory.

"},{"location":"aea-framework-documentation/api/manager/manager/#get_data_dir_of_agent","title":"get_data_dir_of_agent","text":"
def get_data_dir_of_agent(agent_name: str) -> str\n

Get the data directory of a specific agent.

"},{"location":"aea-framework-documentation/api/manager/manager/#is_running_3","title":"is_running","text":"
@property\ndef is_running() -> bool\n

Is manager running.

"},{"location":"aea-framework-documentation/api/manager/manager/#dict_state","title":"dict_state","text":"
@property\ndef dict_state() -> Dict[str, Any]\n

Create MultiAgentManager dist state.

"},{"location":"aea-framework-documentation/api/manager/manager/#projects","title":"projects","text":"
@property\ndef projects() -> Dict[PublicId, Project]\n

Get all projects.

"},{"location":"aea-framework-documentation/api/manager/manager/#add_error_callback","title":"add_error_callback","text":"
def add_error_callback(\nerror_callback: Callable[[str, BaseException],\nNone]) -> \"MultiAgentManager\"\n

Add error callback to call on error raised.

"},{"location":"aea-framework-documentation/api/manager/manager/#start_manager","title":"start_manager","text":"
def start_manager(local: bool = False,\nremote: bool = False) -> \"MultiAgentManager\"\n

Start manager.

If local = False and remote = False, then the packages are fetched in mixed mode (i.e. first try from local registry, and then from remote registry in case of failure).

Arguments:

  • local: whether or not to fetch from local registry.
  • remote: whether or not to fetch from remote registry.

Returns:

the MultiAgentManager instance.

"},{"location":"aea-framework-documentation/api/manager/manager/#last_start_status","title":"last_start_status","text":"
@property\ndef last_start_status() -> Tuple[bool, Dict[PublicId, List[Dict]], List[Tuple[\nPublicId, List[Dict], Exception]], ]\n

Get status of the last agents start loading state.

"},{"location":"aea-framework-documentation/api/manager/manager/#stop_manager","title":"stop_manager","text":"
def stop_manager(cleanup: bool = True,\nsave: bool = False) -> \"MultiAgentManager\"\n

Stop manager.

Stops all running agents and stop agent.

Arguments:

  • cleanup: bool is cleanup on stop.
  • save: bool is save state to file on stop.

Returns:

None

"},{"location":"aea-framework-documentation/api/manager/manager/#add_project","title":"add_project","text":"
def add_project(public_id: PublicId,\nlocal: bool = False,\nremote: bool = False,\nrestore: bool = False) -> \"MultiAgentManager\"\n

Fetch agent project and all dependencies to working_dir.

If local = False and remote = False, then the packages are fetched in mixed mode (i.e. first try from local registry, and then from remote registry in case of failure).

Arguments:

  • public_id: the public if of the agent project.
  • local: whether or not to fetch from local registry.
  • remote: whether or not to fetch from remote registry.
  • restore: bool flag for restoring already fetched agent.

Returns:

self

"},{"location":"aea-framework-documentation/api/manager/manager/#remove_project","title":"remove_project","text":"
def remove_project(public_id: PublicId,\nkeep_files: bool = False) -> \"MultiAgentManager\"\n

Remove agent project.

"},{"location":"aea-framework-documentation/api/manager/manager/#list_projects","title":"list_projects","text":"
def list_projects() -> List[PublicId]\n

List all agents projects added.

Returns:

list of public ids of projects

"},{"location":"aea-framework-documentation/api/manager/manager/#add_agent","title":"add_agent","text":"
def add_agent(public_id: PublicId,\nagent_name: Optional[str] = None,\nagent_overrides: Optional[dict] = None,\ncomponent_overrides: Optional[List[dict]] = None,\nlocal: bool = False,\nremote: bool = False,\nrestore: bool = False) -> \"MultiAgentManager\"\n

Create new agent configuration based on project with config overrides applied.

Alias is stored in memory only!

Arguments:

  • public_id: base agent project public id
  • agent_name: unique name for the agent
  • agent_overrides: overrides for agent config.
  • component_overrides: overrides for component section.
  • local: whether or not to fetch from local registry.
  • remote: whether or not to fetch from remote registry.
  • restore: bool flag for restoring already fetched agent.

Returns:

self

"},{"location":"aea-framework-documentation/api/manager/manager/#add_agent_with_config","title":"add_agent_with_config","text":"
def add_agent_with_config(\npublic_id: PublicId,\nconfig: List[dict],\nagent_name: Optional[str] = None) -> \"MultiAgentManager\"\n

Create new agent configuration based on project with config provided.

Alias is stored in memory only!

Arguments:

  • public_id: base agent project public id
  • agent_name: unique name for the agent
  • config: agent config (used for agent re-creation).

Returns:

manager

"},{"location":"aea-framework-documentation/api/manager/manager/#get_agent_overridables","title":"get_agent_overridables","text":"
def get_agent_overridables(agent_name: str) -> Tuple[Dict, List[Dict]]\n

Get agent config overridables.

Arguments:

  • agent_name: str

Returns:

Tuple of agent overridables dict and and list of component overridables dict.

"},{"location":"aea-framework-documentation/api/manager/manager/#set_agent_overrides","title":"set_agent_overrides","text":"
def set_agent_overrides(\nagent_name: str, agent_overides: Optional[Dict],\ncomponents_overrides: Optional[List[Dict]]) -> \"MultiAgentManager\"\n

Set agent overrides.

Arguments:

  • agent_name: str
  • agent_overides: optional dict of agent config overrides
  • components_overrides: optional list of dict of components overrides

Returns:

self

"},{"location":"aea-framework-documentation/api/manager/manager/#list_agents_info","title":"list_agents_info","text":"
def list_agents_info() -> List[Dict[str, Any]]\n

List agents detailed info.

Returns:

list of dicts that represents agent info: public_id, name, is_running.

"},{"location":"aea-framework-documentation/api/manager/manager/#list_agents","title":"list_agents","text":"
def list_agents(running_only: bool = False) -> List[str]\n

List all agents.

Arguments:

  • running_only: returns only running if set to True

Returns:

list of agents names

"},{"location":"aea-framework-documentation/api/manager/manager/#remove_agent","title":"remove_agent","text":"
def remove_agent(\nagent_name: str,\nskip_project_auto_remove: bool = False) -> \"MultiAgentManager\"\n

Remove agent alias definition from registry.

Arguments:

  • agent_name: agent name to remove
  • skip_project_auto_remove: disable auto project remove on last agent removed.

Returns:

None

"},{"location":"aea-framework-documentation/api/manager/manager/#start_agent","title":"start_agent","text":"
def start_agent(agent_name: str) -> \"MultiAgentManager\"\n

Start selected agent.

Arguments:

  • agent_name: agent name to start

Returns:

None

"},{"location":"aea-framework-documentation/api/manager/manager/#start_all_agents","title":"start_all_agents","text":"
def start_all_agents() -> \"MultiAgentManager\"\n

Start all not started agents.

Returns:

None

"},{"location":"aea-framework-documentation/api/manager/manager/#stop_agent","title":"stop_agent","text":"
def stop_agent(agent_name: str) -> \"MultiAgentManager\"\n

Stop running agent.

Arguments:

  • agent_name: agent name to stop

Returns:

self

"},{"location":"aea-framework-documentation/api/manager/manager/#stop_all_agents","title":"stop_all_agents","text":"
def stop_all_agents() -> \"MultiAgentManager\"\n

Stop all agents running.

Returns:

self

"},{"location":"aea-framework-documentation/api/manager/manager/#stop_agents","title":"stop_agents","text":"
def stop_agents(agent_names: List[str]) -> \"MultiAgentManager\"\n

Stop specified agents.

Arguments:

  • agent_names: names of agents

Returns:

self

"},{"location":"aea-framework-documentation/api/manager/manager/#start_agents","title":"start_agents","text":"
def start_agents(agent_names: List[str]) -> \"MultiAgentManager\"\n

Stop specified agents.

Arguments:

  • agent_names: names of agents

Returns:

self

"},{"location":"aea-framework-documentation/api/manager/manager/#get_agent_alias","title":"get_agent_alias","text":"
def get_agent_alias(agent_name: str) -> AgentAlias\n

Return details about agent alias definition.

Arguments:

  • agent_name: name of agent

Returns:

AgentAlias

"},{"location":"aea-framework-documentation/api/manager/project/","title":"Project","text":""},{"location":"aea-framework-documentation/api/manager/project/#aeamanagerproject","title":"aea.manager.project","text":"

This module contains the implementation of AEA agents project configuration.

"},{"location":"aea-framework-documentation/api/manager/project/#_base-objects","title":"_Base Objects","text":"
class _Base()\n

Base class to share some methods.

"},{"location":"aea-framework-documentation/api/manager/project/#builder","title":"builder","text":"
@property\ndef builder() -> AEABuilder\n

Get AEABuilder instance.

"},{"location":"aea-framework-documentation/api/manager/project/#install_pypi_dependencies","title":"install_pypi_dependencies","text":"
def install_pypi_dependencies() -> None\n

Install python dependencies for the project.

"},{"location":"aea-framework-documentation/api/manager/project/#project-objects","title":"Project Objects","text":"
class Project(_Base)\n

Agent project representation.

"},{"location":"aea-framework-documentation/api/manager/project/#__init__","title":"__init__","text":"
def __init__(public_id: PublicId, path: str) -> None\n

Init project with public_id and project's path.

"},{"location":"aea-framework-documentation/api/manager/project/#build","title":"build","text":"
def build() -> None\n

Call all build entry points.

"},{"location":"aea-framework-documentation/api/manager/project/#load","title":"load","text":"
@classmethod\ndef load(cls,\nworking_dir: str,\npublic_id: PublicId,\nis_local: bool = False,\nis_remote: bool = False,\nis_restore: bool = False,\ncli_verbosity: str = \"INFO\",\nregistry_path: str = DEFAULT_REGISTRY_NAME,\nskip_consistency_check: bool = False,\nskip_aea_validation: bool = False) -> \"Project\"\n

Load project with given public_id to working_dir.

If local = False and remote = False, then the packages are fetched in mixed mode (i.e. first try from local registry, and then from remote registry in case of failure).

Arguments:

  • working_dir: the working directory
  • public_id: the public id
  • is_local: whether to fetch from local
  • is_remote: whether to fetch from remote
  • is_restore: whether to restore or not
  • cli_verbosity: the logging verbosity of the CLI
  • registry_path: the path to the registry locally
  • skip_consistency_check: consistency checks flag
  • skip_aea_validation: aea validation flag

Returns:

project

"},{"location":"aea-framework-documentation/api/manager/project/#remove","title":"remove","text":"
def remove() -> None\n

Remove project, do cleanup.

"},{"location":"aea-framework-documentation/api/manager/project/#agent_config","title":"agent_config","text":"
@property\ndef agent_config() -> AgentConfig\n

Get the agent configuration.

"},{"location":"aea-framework-documentation/api/manager/project/#builder_1","title":"builder","text":"
@property\ndef builder() -> AEABuilder\n

Get builder instance.

"},{"location":"aea-framework-documentation/api/manager/project/#check","title":"check","text":"
def check() -> None\n

Check we can still construct an AEA from the project with builder.build.

"},{"location":"aea-framework-documentation/api/manager/project/#agentalias-objects","title":"AgentAlias Objects","text":"
class AgentAlias(_Base)\n

Agent alias representation.

"},{"location":"aea-framework-documentation/api/manager/project/#__init___1","title":"__init__","text":"
def __init__(project: Project,\nagent_name: str,\ndata_dir: str,\npassword: Optional[str] = None)\n

Init agent alias with project, config, name, agent, builder.

"},{"location":"aea-framework-documentation/api/manager/project/#set_agent_config_from_data","title":"set_agent_config_from_data","text":"
def set_agent_config_from_data(json_data: List[Dict]) -> None\n

Set agent config instance constructed from json data.

Arguments:

  • json_data: agent config json data

"},{"location":"aea-framework-documentation/api/manager/project/#builder_2","title":"builder","text":"
@property\ndef builder() -> AEABuilder\n

Get builder instance.

"},{"location":"aea-framework-documentation/api/manager/project/#agent_config_1","title":"agent_config","text":"
@property\ndef agent_config() -> AgentConfig\n

Get agent config.

"},{"location":"aea-framework-documentation/api/manager/project/#remove_from_project","title":"remove_from_project","text":"
def remove_from_project() -> None\n

Remove agent alias from project.

"},{"location":"aea-framework-documentation/api/manager/project/#dict","title":"dict","text":"
@property\ndef dict() -> Dict[str, Any]\n

Convert AgentAlias to dict.

"},{"location":"aea-framework-documentation/api/manager/project/#config_json","title":"config_json","text":"
@property\ndef config_json() -> List[Dict]\n

Get agent config json data.

"},{"location":"aea-framework-documentation/api/manager/project/#get_aea_instance","title":"get_aea_instance","text":"
def get_aea_instance() -> AEA\n

Build new aea instance.

"},{"location":"aea-framework-documentation/api/manager/project/#issue_certificates","title":"issue_certificates","text":"
def issue_certificates() -> None\n

Issue the certificates for this agent.

"},{"location":"aea-framework-documentation/api/manager/project/#set_overrides","title":"set_overrides","text":"
def set_overrides(agent_overrides: Optional[Dict] = None,\ncomponent_overrides: Optional[List[Dict]] = None) -> None\n

Set override for this agent alias's config.

"},{"location":"aea-framework-documentation/api/manager/project/#agent_config_manager","title":"agent_config_manager","text":"
@property\ndef agent_config_manager() -> AgentConfigManager\n

Get agent configuration manager instance for the config.

"},{"location":"aea-framework-documentation/api/manager/project/#get_overridables","title":"get_overridables","text":"
def get_overridables() -> Tuple[Dict, List[Dict]]\n

Get all overridables for this agent alias's config.

"},{"location":"aea-framework-documentation/api/manager/project/#get_addresses","title":"get_addresses","text":"
def get_addresses() -> Dict[str, str]\n

Get addresses from private keys.

Returns:

dict with crypto id str as key and address str as value

"},{"location":"aea-framework-documentation/api/manager/project/#get_connections_addresses","title":"get_connections_addresses","text":"
def get_connections_addresses() -> Dict[str, str]\n

Get connections addresses from connections private keys.

Returns:

dict with crypto id str as key and address str as value

"},{"location":"aea-framework-documentation/api/manager/utils/","title":"Utils","text":""},{"location":"aea-framework-documentation/api/manager/utils/#aeamanagerutils","title":"aea.manager.utils","text":"

Multiagent manager utils.

"},{"location":"aea-framework-documentation/api/manager/utils/#get_lib_path","title":"get_lib_path","text":"
def get_lib_path(env_dir: str) -> str\n

Get librarty path for env dir.

"},{"location":"aea-framework-documentation/api/manager/utils/#make_venv","title":"make_venv","text":"
def make_venv(env_dir: str, set_env: bool = False) -> None\n

Make venv and update variable to use it.

Arguments:

  • env_dir: str, path for new env dir
  • set_env: bool. use evn within this python process (update, sys.executable and sys.path)

"},{"location":"aea-framework-documentation/api/manager/utils/#project_install_and_build","title":"project_install_and_build","text":"
def project_install_and_build(project: Project) -> None\n

Install project dependencies and build required components.

"},{"location":"aea-framework-documentation/api/manager/utils/#get_venv_dir_for_project","title":"get_venv_dir_for_project","text":"
def get_venv_dir_for_project(project: Project) -> str\n

Get virtual env directory for project specified.

"},{"location":"aea-framework-documentation/api/manager/utils/#project_check","title":"project_check","text":"
def project_check(project: Project) -> None\n

Perform project loads well.

"},{"location":"aea-framework-documentation/api/manager/utils/#run_in_venv","title":"run_in_venv","text":"
def run_in_venv(env_dir: str, fn: Callable, timeout: float, *args: Any) -> Any\n

Run python function in a dedicated process with virtual env specified.

"},{"location":"aea-framework-documentation/api/plugins/aea_cli_ipfs/core/","title":"API","text":""},{"location":"aea-framework-documentation/api/plugins/aea_cli_ipfs/core/#pluginsaea-cli-ipfsaea_cli_ipfscore","title":"plugins.aea-cli-ipfs.aea_cli_ipfs.core","text":"

Core components for ipfs cli command.

"},{"location":"aea-framework-documentation/api/plugins/aea_cli_ipfs/core/#ipfs","title":"ipfs","text":"
@click.group()\n@click.pass_context\ndef ipfs(click_context: click.Context) -> None\n

IPFS Commands

"},{"location":"aea-framework-documentation/api/plugins/aea_cli_ipfs/core/#process_result","title":"process_result","text":"
@ipfs.result_callback()\n@click.pass_context\ndef process_result(click_context: click.Context, *_: Any) -> None\n

Tear down command group.

"},{"location":"aea-framework-documentation/api/plugins/aea_cli_ipfs/core/#add","title":"add","text":"
@ipfs.command()\n@click.argument(\n\"dir_path\",\ntype=click.Path(exists=True,\ndir_okay=True,\nfile_okay=False,\nresolve_path=True,\nreadable=True),\nrequired=False,\n)\n@click.option(\"-p\", \"--publish\", is_flag=True)\n@click.option(\"--no-pin\", is_flag=True)\n@click.pass_context\ndef add(click_context: click.Context,\ndir_path: Optional[str],\npublish: bool = False,\nno_pin: bool = False) -> None\n

Add directory to ipfs, if not directory specified the current one will be added.

"},{"location":"aea-framework-documentation/api/plugins/aea_cli_ipfs/core/#remove","title":"remove","text":"
@ipfs.command()\n@click.argument(\n\"hash_\",\nmetavar=\"hash\",\ntype=str,\nrequired=True,\n)\n@click.pass_context\ndef remove(click_context: click.Context, hash_: str) -> None\n

Remove a directory from ipfs by it's hash.

"},{"location":"aea-framework-documentation/api/plugins/aea_cli_ipfs/core/#download","title":"download","text":"
@ipfs.command()\n@click.argument(\n\"hash_\",\nmetavar=\"hash\",\ntype=str,\nrequired=True,\n)\n@click.argument(\n\"target_dir\",\ntype=click.Path(dir_okay=True, file_okay=False, resolve_path=True),\nrequired=False,\n)\n@click.pass_context\ndef download(click_context: click.Context, hash_: str,\ntarget_dir: Optional[str]) -> None\n

Download directory by it's hash, if not target directory specified will use current one.

"},{"location":"aea-framework-documentation/api/plugins/aea_cli_ipfs/ipfs_utils/","title":"Utils","text":""},{"location":"aea-framework-documentation/api/plugins/aea_cli_ipfs/ipfs_utils/#pluginsaea-cli-ipfsaea_cli_ipfsipfs_utils","title":"plugins.aea-cli-ipfs.aea_cli_ipfs.ipfs_utils","text":"

Ipfs utils for ipfs cli command.

"},{"location":"aea-framework-documentation/api/plugins/aea_cli_ipfs/ipfs_utils/#ipfsdaemon-objects","title":"IPFSDaemon Objects","text":"
class IPFSDaemon()\n

Set up the IPFS daemon.

Raises:

  • Exception: if IPFS is not installed.

"},{"location":"aea-framework-documentation/api/plugins/aea_cli_ipfs/ipfs_utils/#__init__","title":"__init__","text":"
def __init__() -> None\n

Initialise IPFS daemon.

"},{"location":"aea-framework-documentation/api/plugins/aea_cli_ipfs/ipfs_utils/#is_started","title":"is_started","text":"
def is_started() -> bool\n

Check daemon was started.

"},{"location":"aea-framework-documentation/api/plugins/aea_cli_ipfs/ipfs_utils/#start","title":"start","text":"
def start() -> None\n

Run the ipfs daemon.

"},{"location":"aea-framework-documentation/api/plugins/aea_cli_ipfs/ipfs_utils/#stop","title":"stop","text":"
def stop() -> None\n

Terminate the ipfs daemon.

"},{"location":"aea-framework-documentation/api/plugins/aea_cli_ipfs/ipfs_utils/#baseipfstoolexception-objects","title":"BaseIPFSToolException Objects","text":"
class BaseIPFSToolException(Exception)\n

Base ipfs tool exception.

"},{"location":"aea-framework-documentation/api/plugins/aea_cli_ipfs/ipfs_utils/#removeerror-objects","title":"RemoveError Objects","text":"
class RemoveError(BaseIPFSToolException)\n

Exception on remove.

"},{"location":"aea-framework-documentation/api/plugins/aea_cli_ipfs/ipfs_utils/#publisherror-objects","title":"PublishError Objects","text":"
class PublishError(BaseIPFSToolException)\n

Exception on publish.

"},{"location":"aea-framework-documentation/api/plugins/aea_cli_ipfs/ipfs_utils/#nodeerror-objects","title":"NodeError Objects","text":"
class NodeError(BaseIPFSToolException)\n

Exception for node connection check.

"},{"location":"aea-framework-documentation/api/plugins/aea_cli_ipfs/ipfs_utils/#downloaderror-objects","title":"DownloadError Objects","text":"
class DownloadError(BaseIPFSToolException)\n

Exception on download failed.

"},{"location":"aea-framework-documentation/api/plugins/aea_cli_ipfs/ipfs_utils/#ipfstool-objects","title":"IPFSTool Objects","text":"
class IPFSTool()\n

IPFS tool to add, publish, remove, download directories.

"},{"location":"aea-framework-documentation/api/plugins/aea_cli_ipfs/ipfs_utils/#__init___1","title":"__init__","text":"
def __init__(client_options: Optional[Dict] = None)\n

Init tool.

Arguments:

  • client_options: dict, options for ipfshttpclient instance.

"},{"location":"aea-framework-documentation/api/plugins/aea_cli_ipfs/ipfs_utils/#add","title":"add","text":"
def add(dir_path: str, pin: bool = True) -> Tuple[str, str, List]\n

Add directory to ipfs.

It wraps into directory.

Arguments:

  • dir_path: str, path to dir to publish
  • pin: bool, pin object or not

Returns:

dir name published, hash, list of items processed

"},{"location":"aea-framework-documentation/api/plugins/aea_cli_ipfs/ipfs_utils/#remove","title":"remove","text":"
def remove(hash_id: str) -> Dict\n

Remove dir added by it's hash.

Arguments:

  • hash_id: str. hash of dir to remove

Returns:

dict with unlinked items.

"},{"location":"aea-framework-documentation/api/plugins/aea_cli_ipfs/ipfs_utils/#download","title":"download","text":"
def download(hash_id: str, target_dir: str, fix_path: bool = True) -> None\n

Download dir by it's hash.

Arguments:

  • hash_id: str. hash of file to download
  • target_dir: str. directory to place downloaded
  • fix_path: bool. default True. on download don't wrap result in to hash_id directory.

"},{"location":"aea-framework-documentation/api/plugins/aea_cli_ipfs/ipfs_utils/#publish","title":"publish","text":"
def publish(hash_id: str) -> Dict\n

Publish directory by it's hash id.

Arguments:

  • hash_id: hash of the directory to publish.

Returns:

dict of names it was publish for.

"},{"location":"aea-framework-documentation/api/plugins/aea_cli_ipfs/ipfs_utils/#chec_ipfs_node_running","title":"chec_ipfs_node_running","text":"
def chec_ipfs_node_running() -> None\n

Check ipfs node running.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/","title":"API","text":""},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#pluginsaea-ledger-cosmosaea_ledger_cosmoscosmos","title":"plugins.aea-ledger-cosmos.aea_ledger_cosmos.cosmos","text":"

Cosmos module wrapping the public and private key cryptography and ledger api.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#dataencrypt-objects","title":"DataEncrypt Objects","text":"
class DataEncrypt()\n

Class to encrypt/decrypt data strings with password provided.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#encrypt","title":"encrypt","text":"
@classmethod\ndef encrypt(cls, data: bytes, password: str) -> bytes\n

Encrypt data with password.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#bytes_encode","title":"bytes_encode","text":"
@staticmethod\ndef bytes_encode(data: bytes) -> str\n

Encode bytes to ascii friendly string.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#bytes_decode","title":"bytes_decode","text":"
@staticmethod\ndef bytes_decode(data: str) -> bytes\n

Decode ascii friendly string to bytes.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#decrypt","title":"decrypt","text":"
@classmethod\ndef decrypt(cls, encrypted_data: bytes, password: str) -> bytes\n

Decrypt data with password provided.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#cosmoshelper-objects","title":"CosmosHelper Objects","text":"
class CosmosHelper(Helper)\n

Helper class usable as Mixin for CosmosApi or as standalone class.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#is_transaction_settled","title":"is_transaction_settled","text":"
@staticmethod\ndef is_transaction_settled(tx_receipt: JSONLike) -> bool\n

Check whether a transaction is settled or not.

Arguments:

  • tx_receipt: the receipt of the transaction.

Returns:

True if the transaction has been settled, False o/w.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#get_code_id","title":"get_code_id","text":"
@classmethod\ndef get_code_id(cls, tx_receipt: JSONLike) -> Optional[int]\n

Retrieve the code_id from a transaction receipt.

Arguments:

  • tx_receipt: the receipt of the transaction.

Returns:

the code id, if present

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#get_event_attributes","title":"get_event_attributes","text":"
@staticmethod\ndef get_event_attributes(tx_receipt: JSONLike) -> Dict\n

Retrieve events attributes from tx receipt.

Arguments:

  • tx_receipt: the receipt of the transaction.

Returns:

dict

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#get_contract_address","title":"get_contract_address","text":"
@classmethod\ndef get_contract_address(cls, tx_receipt: JSONLike) -> Optional[str]\n

Retrieve the contract_address from a transaction receipt.

Arguments:

  • tx_receipt: the receipt of the transaction.

Returns:

the contract address, if present

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#is_transaction_valid","title":"is_transaction_valid","text":"
@staticmethod\ndef is_transaction_valid(tx: JSONLike, seller: Address, client: Address,\ntx_nonce: str, amount: int) -> bool\n

Check whether a transaction is valid or not.

Arguments:

  • tx: the transaction.
  • seller: the address of the seller.
  • client: the address of the client.
  • tx_nonce: the transaction nonce.
  • amount: the amount we expect to get from the transaction.

Returns:

True if the random_message is equals to tx['input']

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#generate_tx_nonce","title":"generate_tx_nonce","text":"
@staticmethod\ndef generate_tx_nonce(seller: Address, client: Address) -> str\n

Generate a unique hash to distinguish transactions with the same terms.

Arguments:

  • seller: the address of the seller.
  • client: the address of the client.

Returns:

return the hash in hex.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#get_address_from_public_key","title":"get_address_from_public_key","text":"
@classmethod\ndef get_address_from_public_key(cls, public_key: str) -> str\n

Get the address from the public key.

Arguments:

  • public_key: the public key

Returns:

str

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#recover_message","title":"recover_message","text":"
@classmethod\ndef recover_message(cls,\nmessage: bytes,\nsignature: str,\nis_deprecated_mode: bool = False) -> Tuple[Address, ...]\n

Recover the addresses from the hash.

Arguments:

  • message: the message we expect
  • signature: the transaction signature
  • is_deprecated_mode: if the deprecated signing was used

Returns:

the recovered addresses

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#recover_public_keys_from_message","title":"recover_public_keys_from_message","text":"
@classmethod\ndef recover_public_keys_from_message(\ncls,\nmessage: bytes,\nsignature: str,\nis_deprecated_mode: bool = False) -> Tuple[str, ...]\n

Get the public key used to produce the signature of the message

Arguments:

  • message: raw bytes used to produce signature
  • signature: signature of the message
  • is_deprecated_mode: if the deprecated signing was used

Returns:

the recovered public keys

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#get_hash","title":"get_hash","text":"
@staticmethod\ndef get_hash(message: bytes) -> str\n

Get the hash of a message.

Arguments:

  • message: the message to be hashed.

Returns:

the hash of the message.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#is_valid_address","title":"is_valid_address","text":"
@classmethod\ndef is_valid_address(cls, address: Address) -> bool\n

Check if the address is valid.

Arguments:

  • address: the address to validate

Returns:

whether address is valid or not

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#load_contract_interface","title":"load_contract_interface","text":"
@classmethod\ndef load_contract_interface(cls, file_path: Path) -> Dict[str, str]\n

Load contract interface.

Arguments:

  • file_path: the file path to the interface

Returns:

the interface

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#cosmoscrypto-objects","title":"CosmosCrypto Objects","text":"
class CosmosCrypto(Crypto[SigningKey])\n

Class wrapping the Account Generation from Ethereum ledger.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#__init__","title":"__init__","text":"
def __init__(private_key_path: Optional[str] = None,\npassword: Optional[str] = None) -> None\n

Instantiate an ethereum crypto object.

Arguments:

  • private_key_path: the private key path of the agent
  • password: the password to encrypt/decrypt the private key.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#private_key","title":"private_key","text":"
@property\ndef private_key() -> str\n

Return a private key.

Returns:

a private key string

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#public_key","title":"public_key","text":"
@property\ndef public_key() -> str\n

Return a public key in hex format.

Returns:

a public key string in hex format

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#address","title":"address","text":"
@property\ndef address() -> str\n

Return the address for the key pair.

Returns:

a display_address str

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#load_private_key_from_path","title":"load_private_key_from_path","text":"
@classmethod\ndef load_private_key_from_path(cls,\nfile_name: str,\npassword: Optional[str] = None) -> SigningKey\n

Load a private key in hex format from a file.

Arguments:

  • file_name: the path to the hex file.
  • password: the password to encrypt/decrypt the private key.

Returns:

the Entity.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#sign_message","title":"sign_message","text":"
def sign_message(message: bytes, is_deprecated_mode: bool = False) -> str\n

Sign a message in bytes string form.

Arguments:

  • message: the message to be signed
  • is_deprecated_mode: if the deprecated signing is used

Returns:

signature of the message in string form

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#sign_transaction","title":"sign_transaction","text":"
def sign_transaction(transaction: JSONLike) -> JSONLike\n

Sign a transaction in bytes string form.

Arguments:

  • transaction: the transaction to be signed

Returns:

signed transaction

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#generate_private_key","title":"generate_private_key","text":"
@classmethod\ndef generate_private_key(cls) -> SigningKey\n

Generate a key pair for cosmos network.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#encrypt_1","title":"encrypt","text":"
def encrypt(password: str) -> str\n

Encrypt the private key and return in json.

Arguments:

  • password: the password to decrypt.

Returns:

json string containing encrypted private key.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#decrypt_1","title":"decrypt","text":"
@classmethod\ndef decrypt(cls, keyfile_json: str, password: str) -> str\n

Decrypt the private key and return in raw form.

Arguments:

  • keyfile_json: json string containing encrypted private key.
  • password: the password to decrypt.

Returns:

the raw private key.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#_cosmosapi-objects","title":"_CosmosApi Objects","text":"
class _CosmosApi(LedgerApi)\n

Class to interact with the Cosmos SDK via a HTTP APIs.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#__init___1","title":"__init__","text":"
def __init__(**kwargs: Any) -> None\n

Initialize the Cosmos ledger APIs.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#api","title":"api","text":"
@property\ndef api() -> Any\n

Get the underlying API object.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#get_balance","title":"get_balance","text":"
def get_balance(address: Address) -> Optional[int]\n

Get the balance of a given account.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#get_state","title":"get_state","text":"
def get_state(callable_name: str, *args: Any,\n**kwargs: Any) -> Optional[JSONLike]\n

Call a specified function on the ledger API.

Based on the cosmos REST API specification, which takes a path (strings separated by '/'). The convention here is to define the root of the path (txs, blocks, etc.) as the callable_name and the rest of the path as args.

Arguments:

  • callable_name: name of the callable
  • args: positional arguments
  • kwargs: keyword arguments

Returns:

the transaction dictionary

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#get_deploy_transaction","title":"get_deploy_transaction","text":"
def get_deploy_transaction(contract_interface: Dict[str, str],\ndeployer_address: Address,\n**kwargs: Any) -> Optional[JSONLike]\n

Get the transaction to deploy the smart contract.

Dispatches to _get_storage_transaction and _get_init_transaction based on kwargs.

Arguments:

  • contract_interface: the contract interface.
  • deployer_address: The address that will deploy the contract.
  • kwargs: keyword arguments.

Returns:

the transaction dictionary.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#get_handle_transaction","title":"get_handle_transaction","text":"
def get_handle_transaction(\nsender_address: Address,\ncontract_address: Address,\nhandle_msg: Any,\namount: int,\ntx_fee: int,\ndenom: Optional[str] = None,\ngas: int = DEFAULT_GAS_AMOUNT,\nmemo: str = \"\",\nchain_id: Optional[str] = None,\naccount_number: Optional[int] = None,\nsequence: Optional[int] = None,\ntx_fee_denom: Optional[str] = None) -> Optional[JSONLike]\n

Create a CosmWasm HandleMsg transaction.

Arguments:

  • sender_address: the sender address of the message initiator.
  • contract_address: the address of the smart contract.
  • handle_msg: HandleMsg in JSON format.
  • amount: Funds amount sent with transaction.
  • tx_fee: the tx fee accepted.
  • denom: the name of the denomination of the contract funds
  • gas: Maximum amount of gas to be used on executing command.
  • memo: any string comment.
  • chain_id: the Chain ID of the CosmWasm transaction. Default is 1 (i.e. mainnet).
  • account_number: Account number
  • sequence: Sequence
  • tx_fee_denom: Denomination of tx_fee, identical with denom param when None

Returns:

the unsigned CosmWasm HandleMsg

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#execute_contract_query","title":"execute_contract_query","text":"
def execute_contract_query(contract_address: Address,\nquery_msg: JSONLike) -> Optional[JSONLike]\n

Execute a CosmWasm QueryMsg. QueryMsg doesn't require signing.

Arguments:

  • contract_address: the address of the smart contract.
  • query_msg: QueryMsg in JSON format.

Returns:

the message receipt

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#get_transfer_transaction","title":"get_transfer_transaction","text":"
def get_transfer_transaction(sender_address: Address,\ndestination_address: Address,\namount: int,\ntx_fee: int,\ntx_nonce: str,\ndenom: Optional[str] = None,\ngas: int = DEFAULT_GAS_AMOUNT,\nmemo: str = \"\",\nchain_id: Optional[str] = None,\naccount_number: Optional[int] = None,\nsequence: Optional[int] = None,\ntx_fee_denom: Optional[str] = None,\n**kwargs: Any) -> Optional[JSONLike]\n

Submit a transfer transaction to the ledger.

Arguments:

  • sender_address: the sender address of the payer.
  • destination_address: the destination address of the payee.
  • amount: the amount of wealth to be transferred.
  • tx_fee: the transaction fee.
  • tx_nonce: verifies the authenticity of the tx
  • denom: the denomination of tx fee and amount
  • gas: the gas used.
  • memo: memo to include in tx.
  • chain_id: the chain ID of the transaction.
  • account_number: Account number
  • sequence: Sequence
  • tx_fee_denom: Denomination of tx_fee, identical with denom param when None
  • kwargs: keyword arguments.

Returns:

the transfer transaction

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#get_packed_exec_msg","title":"get_packed_exec_msg","text":"
def get_packed_exec_msg(sender_address: Address,\ncontract_address: str,\nmsg: JSONLike,\nfunds: int = 0,\ndenom: Optional[str] = None) -> ProtoAny\n

Create and pack MsgExecuteContract

Arguments:

  • sender_address: Address of sender
  • contract_address: Address of contract
  • msg: Paramaters to be passed to smart contract
  • funds: Funds to be sent to smart contract
  • denom: the denomination of funds

Returns:

Packed MsgExecuteContract

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#get_packed_send_msg","title":"get_packed_send_msg","text":"
def get_packed_send_msg(from_address: Address,\nto_address: Address,\namount: int,\ndenom: Optional[str] = None) -> ProtoAny\n

Generate and pack MsgSend

Arguments:

  • from_address: Address of sender
  • to_address: Address of recipient
  • amount: amount of coins to be sent
  • denom: the denomination of and amount

Returns:

packer ProtoAny type message

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#get_multi_transaction","title":"get_multi_transaction","text":"
def get_multi_transaction(from_addresses: List[str],\npub_keys: Optional[List[bytes]],\nmsgs: List[ProtoAny],\ngas: int,\ntx_fee: int = 0,\nmemo: str = \"\",\nchain_id: Optional[str] = None,\ndenom: Optional[str] = None,\ntx_fee_denom: Optional[str] = None) -> JSONLike\n

Generate transaction with multiple messages

Arguments:

  • from_addresses: Addresses of signers
  • pub_keys: Public keys of signers
  • msgs: Messages to be included in transaction
  • gas: the gas used.
  • tx_fee: the transaction fee.
  • memo: memo to include in tx.
  • chain_id: the chain ID of the transaction.
  • denom: the denomination of tx fee
  • tx_fee_denom: Denomination of tx_fee, identical with denom param when None

Raises:

  • None: RuntimeError if number of pubkeys is not equal to number of from_addresses

Returns:

the transaction

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#send_signed_transaction","title":"send_signed_transaction","text":"
def send_signed_transaction(tx_signed: JSONLike) -> Optional[str]\n

Send a signed transaction and wait for confirmation.

Arguments:

  • tx_signed: the signed transaction

Returns:

tx_digest, if present

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#get_transaction_receipt","title":"get_transaction_receipt","text":"
def get_transaction_receipt(tx_digest: str) -> Optional[JSONLike]\n

Get the transaction receipt for a transaction digest.

Arguments:

  • tx_digest: the digest associated to the transaction.

Returns:

the tx receipt, if present

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#get_transaction","title":"get_transaction","text":"
def get_transaction(tx_digest: str) -> Optional[JSONLike]\n

Get the transaction for a transaction digest.

Arguments:

  • tx_digest: the digest associated to the transaction.

Returns:

the tx, if present

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#get_contract_instance","title":"get_contract_instance","text":"
def get_contract_instance(contract_interface: Dict[str, str],\ncontract_address: Optional[str] = None) -> Any\n

Get the instance of a contract.

Arguments:

  • contract_interface: the contract interface.
  • contract_address: the contract address.

Returns:

the contract instance

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#update_with_gas_estimate","title":"update_with_gas_estimate","text":"
def update_with_gas_estimate(transaction: JSONLike) -> JSONLike\n

Attempts to update the transaction with a gas estimate

Arguments:

  • transaction: the transaction

Raises:

  • None: NotImplementedError

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#cosmosapi-objects","title":"CosmosApi Objects","text":"
class CosmosApi(_CosmosApi, CosmosHelper)\n

Class to interact with the Cosmos SDK via a HTTP APIs.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#cosmosfaucetapi-objects","title":"CosmosFaucetApi Objects","text":"
class CosmosFaucetApi(FaucetApi)\n

Cosmos testnet faucet API.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#__init___2","title":"__init__","text":"
def __init__(poll_interval: Optional[float] = None,\nfinal_wait_interval: Optional[float] = None)\n

Initialize CosmosFaucetApi.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/#get_wealth","title":"get_wealth","text":"
def get_wealth(address: Address, url: Optional[str] = None) -> None\n

Get wealth from the faucet for the provided address.

Arguments:

  • address: the address.
  • url: the url

Raises:

  • None: RuntimeError of explicit faucet failures
"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/","title":"API","text":""},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#pluginsaea-ledger-ethereumaea_ledger_ethereumethereum","title":"plugins.aea-ledger-ethereum.aea_ledger_ethereum.ethereum","text":"

Ethereum module wrapping the public and private key cryptography and ledger api.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#get_gas_price_strategy","title":"get_gas_price_strategy","text":"
def get_gas_price_strategy(\ngas_price_strategy: Optional[str] = None,\napi_key: Optional[str] = None) -> Callable[[Web3, TxParams], Wei]\n

Get the gas price strategy.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#signedtransactiontranslator-objects","title":"SignedTransactionTranslator Objects","text":"
class SignedTransactionTranslator()\n

Translator for SignedTransaction.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#to_dict","title":"to_dict","text":"
@staticmethod\ndef to_dict(\nsigned_transaction: SignedTransaction) -> Dict[str, Union[str, int]]\n

Write SignedTransaction to dict.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#from_dict","title":"from_dict","text":"
@staticmethod\ndef from_dict(signed_transaction_dict: JSONLike) -> SignedTransaction\n

Get SignedTransaction from dict.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#attributedicttranslator-objects","title":"AttributeDictTranslator Objects","text":"
class AttributeDictTranslator()\n

Translator for AttributeDict.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#to_dict_1","title":"to_dict","text":"
@classmethod\ndef to_dict(cls, attr_dict: Union[AttributeDict, TxReceipt,\nTxData]) -> JSONLike\n

Simplify to dict.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#from_dict_1","title":"from_dict","text":"
@classmethod\ndef from_dict(cls, di: JSONLike) -> AttributeDict\n

Get back attribute dict.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#ethereumcrypto-objects","title":"EthereumCrypto Objects","text":"
class EthereumCrypto(Crypto[Account])\n

Class wrapping the Account Generation from Ethereum ledger.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#__init__","title":"__init__","text":"
def __init__(private_key_path: Optional[str] = None,\npassword: Optional[str] = None) -> None\n

Instantiate an ethereum crypto object.

Arguments:

  • private_key_path: the private key path of the agent
  • password: the password to encrypt/decrypt the private key.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#private_key","title":"private_key","text":"
@property\ndef private_key() -> str\n

Return a private key.

Returns:

a private key string

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#public_key","title":"public_key","text":"
@property\ndef public_key() -> str\n

Return a public key in hex format.

Returns:

a public key string in hex format

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#address","title":"address","text":"
@property\ndef address() -> str\n

Return the address for the key pair.

Returns:

a display_address str

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#load_private_key_from_path","title":"load_private_key_from_path","text":"
@classmethod\ndef load_private_key_from_path(cls,\nfile_name: str,\npassword: Optional[str] = None) -> Account\n

Load a private key in hex format from a file.

Arguments:

  • file_name: the path to the hex file.
  • password: the password to encrypt/decrypt the private key.

Returns:

the Entity.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#sign_message","title":"sign_message","text":"
def sign_message(message: bytes, is_deprecated_mode: bool = False) -> str\n

Sign a message in bytes string form.

Arguments:

  • message: the message to be signed
  • is_deprecated_mode: if the deprecated signing is used

Returns:

signature of the message in string form

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#sign_transaction","title":"sign_transaction","text":"
def sign_transaction(transaction: JSONLike) -> JSONLike\n

Sign a transaction in bytes string form.

Arguments:

  • transaction: the transaction to be signed

Returns:

signed transaction

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#generate_private_key","title":"generate_private_key","text":"
@classmethod\ndef generate_private_key(cls) -> Account\n

Generate a key pair for ethereum network.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#encrypt","title":"encrypt","text":"
def encrypt(password: str) -> str\n

Encrypt the private key and return in json.

Arguments:

  • password: the password to decrypt.

Returns:

json string containing encrypted private key.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#decrypt","title":"decrypt","text":"
@classmethod\ndef decrypt(cls, keyfile_json: str, password: str) -> str\n

Decrypt the private key and return in raw form.

Arguments:

  • keyfile_json: json str containing encrypted private key.
  • password: the password to decrypt.

Returns:

the raw private key.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#ethereumhelper-objects","title":"EthereumHelper Objects","text":"
class EthereumHelper(Helper)\n

Helper class usable as Mixin for EthereumApi or as standalone class.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#is_transaction_settled","title":"is_transaction_settled","text":"
@staticmethod\ndef is_transaction_settled(tx_receipt: JSONLike) -> bool\n

Check whether a transaction is settled or not.

Arguments:

  • tx_receipt: the receipt associated to the transaction.

Returns:

True if the transaction has been settled, False o/w.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#get_contract_address","title":"get_contract_address","text":"
@staticmethod\ndef get_contract_address(tx_receipt: JSONLike) -> Optional[str]\n

Retrieve the contract_address from a transaction receipt.

Arguments:

  • tx_receipt: the receipt of the transaction.

Returns:

the contract address, if present

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#is_transaction_valid","title":"is_transaction_valid","text":"
@staticmethod\ndef is_transaction_valid(tx: dict, seller: Address, client: Address,\ntx_nonce: str, amount: int) -> bool\n

Check whether a transaction is valid or not.

Arguments:

  • tx: the transaction.
  • seller: the address of the seller.
  • client: the address of the client.
  • tx_nonce: the transaction nonce.
  • amount: the amount we expect to get from the transaction.

Returns:

True if the random_message is equals to tx['input']

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#generate_tx_nonce","title":"generate_tx_nonce","text":"
@staticmethod\ndef generate_tx_nonce(seller: Address, client: Address) -> str\n

Generate a unique hash to distinguish transactions with the same terms.

Arguments:

  • seller: the address of the seller.
  • client: the address of the client.

Returns:

return the hash in hex.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#get_address_from_public_key","title":"get_address_from_public_key","text":"
@classmethod\ndef get_address_from_public_key(cls, public_key: str) -> str\n

Get the address from the public key.

Arguments:

  • public_key: the public key

Returns:

str

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#recover_message","title":"recover_message","text":"
@classmethod\ndef recover_message(cls,\nmessage: bytes,\nsignature: str,\nis_deprecated_mode: bool = False) -> Tuple[Address, ...]\n

Recover the addresses from the hash.

Arguments:

  • message: the message we expect
  • signature: the transaction signature
  • is_deprecated_mode: if the deprecated signing was used

Returns:

the recovered addresses

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#recover_public_keys_from_message","title":"recover_public_keys_from_message","text":"
@classmethod\ndef recover_public_keys_from_message(\ncls,\nmessage: bytes,\nsignature: str,\nis_deprecated_mode: bool = False) -> Tuple[str, ...]\n

Get the public key used to produce the signature of the message

Arguments:

  • message: raw bytes used to produce signature
  • signature: signature of the message
  • is_deprecated_mode: if the deprecated signing was used

Returns:

the recovered public keys

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#get_hash","title":"get_hash","text":"
@staticmethod\ndef get_hash(message: bytes) -> str\n

Get the hash of a message.

Arguments:

  • message: the message to be hashed.

Returns:

the hash of the message.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#load_contract_interface","title":"load_contract_interface","text":"
@classmethod\ndef load_contract_interface(cls, file_path: Path) -> Dict[str, str]\n

Load contract interface.

Arguments:

  • file_path: the file path to the interface

Returns:

the interface

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#ethereumapi-objects","title":"EthereumApi Objects","text":"
class EthereumApi(LedgerApi, EthereumHelper)\n

Class to interact with the Ethereum Web3 APIs.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#__init___1","title":"__init__","text":"
def __init__(**kwargs: Any)\n

Initialize the Ethereum ledger APIs.

Arguments:

  • kwargs: keyword arguments

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#api","title":"api","text":"
@property\ndef api() -> Web3\n

Get the underlying API object.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#get_balance","title":"get_balance","text":"
def get_balance(address: Address) -> Optional[int]\n

Get the balance of a given account.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#get_state","title":"get_state","text":"
def get_state(callable_name: str, *args: Any,\n**kwargs: Any) -> Optional[JSONLike]\n

Call a specified function on the ledger API.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#get_transfer_transaction","title":"get_transfer_transaction","text":"
def get_transfer_transaction(sender_address: Address,\ndestination_address: Address,\namount: int,\ntx_fee: int,\ntx_nonce: str,\nchain_id: Optional[int] = None,\ngas_price: Optional[str] = None,\ngas_price_strategy: Optional[str] = None,\n**kwargs: Any) -> Optional[JSONLike]\n

Submit a transfer transaction to the ledger.

Arguments:

  • sender_address: the sender address of the payer.
  • destination_address: the destination address of the payee.
  • amount: the amount of wealth to be transferred (in Wei).
  • tx_fee: the transaction fee (gas) to be used (in Wei).
  • tx_nonce: verifies the authenticity of the tx.
  • chain_id: the Chain ID of the Ethereum transaction.
  • gas_price: the gas price (in Wei)
  • gas_price_strategy: the gas price strategy to be used.
  • kwargs: keyword arguments

Returns:

the transfer transaction

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#update_with_gas_estimate","title":"update_with_gas_estimate","text":"
def update_with_gas_estimate(transaction: JSONLike) -> JSONLike\n

Attempts to update the transaction with a gas estimate

Arguments:

  • transaction: the transaction

Returns:

the updated transaction

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#send_signed_transaction","title":"send_signed_transaction","text":"
def send_signed_transaction(tx_signed: JSONLike) -> Optional[str]\n

Send a signed transaction and wait for confirmation.

Arguments:

  • tx_signed: the signed transaction

Returns:

tx_digest, if present

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#get_transaction_receipt","title":"get_transaction_receipt","text":"
def get_transaction_receipt(tx_digest: str) -> Optional[JSONLike]\n

Get the transaction receipt for a transaction digest.

Arguments:

  • tx_digest: the digest associated to the transaction.

Returns:

the tx receipt, if present

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#get_transaction","title":"get_transaction","text":"
def get_transaction(tx_digest: str) -> Optional[JSONLike]\n

Get the transaction for a transaction digest.

Arguments:

  • tx_digest: the digest associated to the transaction.

Returns:

the tx, if present

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#get_contract_instance","title":"get_contract_instance","text":"
def get_contract_instance(contract_interface: Dict[str, str],\ncontract_address: Optional[str] = None) -> Any\n

Get the instance of a contract.

Arguments:

  • contract_interface: the contract interface.
  • contract_address: the contract address.

Returns:

the contract instance

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#get_deploy_transaction","title":"get_deploy_transaction","text":"
def get_deploy_transaction(contract_interface: Dict[str, str],\ndeployer_address: Address,\nvalue: int = 0,\ngas: int = 0,\ngas_price: Optional[str] = None,\ngas_price_strategy: Optional[str] = None,\n**kwargs: Any) -> Optional[JSONLike]\n

Get the transaction to deploy the smart contract.

Arguments:

  • contract_interface: the contract interface.
  • deployer_address: The address that will deploy the contract.
  • value: value to send to contract (in Wei)
  • gas: the gas to be used (in Wei)
  • gas_price: the gas price (in Wei)
  • gas_price_strategy: the gas price strategy to be used.
  • kwargs: keyword arguments

Returns:

the transaction dictionary.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#is_valid_address","title":"is_valid_address","text":"
@classmethod\ndef is_valid_address(cls, address: Address) -> bool\n

Check if the address is valid.

Arguments:

  • address: the address to validate

Returns:

whether the address is valid

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#ethereumfaucetapi-objects","title":"EthereumFaucetApi Objects","text":"
class EthereumFaucetApi(FaucetApi)\n

Ethereum testnet faucet API.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#get_wealth","title":"get_wealth","text":"
def get_wealth(address: Address, url: Optional[str] = None) -> None\n

Get wealth from the faucet for the provided address.

Arguments:

  • address: the address.
  • url: the url

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#lrulockwrapper-objects","title":"LruLockWrapper Objects","text":"
class LruLockWrapper()\n

Wrapper for LRU with threading.Lock.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#__init___2","title":"__init__","text":"
def __init__(lru: LRU) -> None\n

Init wrapper.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#__getitem__","title":"__getitem__","text":"
def __getitem__(*args: Any, **kwargs: Any) -> Any\n

Get item

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#__setitem__","title":"__setitem__","text":"
def __setitem__(*args: Any, **kwargs: Any) -> Any\n

Set item.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#__contains__","title":"__contains__","text":"
def __contains__(*args: Any, **kwargs: Any) -> Any\n

Contain item.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#__delitem__","title":"__delitem__","text":"
def __delitem__(*args: Any, **kwargs: Any) -> Any\n

Del item.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/#set_wrapper_for_web3py_session_cache","title":"set_wrapper_for_web3py_session_cache","text":"
def set_wrapper_for_web3py_session_cache() -> None\n

Wrap web3py session cache with threading.Lock.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/","title":"Helper","text":""},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#pluginsaea-ledger-fetchaiaea_ledger_fetchai_cosmos","title":"plugins.aea-ledger-fetchai.aea_ledger_fetchai._cosmos","text":"

Cosmos module wrapping the public and private key cryptography and ledger api.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#dataencrypt-objects","title":"DataEncrypt Objects","text":"
class DataEncrypt()\n

Class to encrypt/decrypt data strings with password provided.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#encrypt","title":"encrypt","text":"
@classmethod\ndef encrypt(cls, data: bytes, password: str) -> bytes\n

Encrypt data with password.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#bytes_encode","title":"bytes_encode","text":"
@staticmethod\ndef bytes_encode(data: bytes) -> str\n

Encode bytes to ascii friendly string.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#bytes_decode","title":"bytes_decode","text":"
@staticmethod\ndef bytes_decode(data: str) -> bytes\n

Decode ascii friendly string to bytes.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#decrypt","title":"decrypt","text":"
@classmethod\ndef decrypt(cls, encrypted_data: bytes, password: str) -> bytes\n

Decrypt data with password provided.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#cosmoshelper-objects","title":"CosmosHelper Objects","text":"
class CosmosHelper(Helper)\n

Helper class usable as Mixin for CosmosApi or as standalone class.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#is_transaction_settled","title":"is_transaction_settled","text":"
@staticmethod\ndef is_transaction_settled(tx_receipt: JSONLike) -> bool\n

Check whether a transaction is settled or not.

Arguments:

  • tx_receipt: the receipt of the transaction.

Returns:

True if the transaction has been settled, False o/w.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#get_code_id","title":"get_code_id","text":"
@classmethod\ndef get_code_id(cls, tx_receipt: JSONLike) -> Optional[int]\n

Retrieve the code_id from a transaction receipt.

Arguments:

  • tx_receipt: the receipt of the transaction.

Returns:

the code id, if present

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#get_event_attributes","title":"get_event_attributes","text":"
@staticmethod\ndef get_event_attributes(tx_receipt: JSONLike) -> Dict\n

Retrieve events attributes from tx receipt.

Arguments:

  • tx_receipt: the receipt of the transaction.

Returns:

dict

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#get_contract_address","title":"get_contract_address","text":"
@classmethod\ndef get_contract_address(cls, tx_receipt: JSONLike) -> Optional[str]\n

Retrieve the contract_address from a transaction receipt.

Arguments:

  • tx_receipt: the receipt of the transaction.

Returns:

the contract address, if present

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#is_transaction_valid","title":"is_transaction_valid","text":"
@staticmethod\ndef is_transaction_valid(tx: JSONLike, seller: Address, client: Address,\ntx_nonce: str, amount: int) -> bool\n

Check whether a transaction is valid or not.

Arguments:

  • tx: the transaction.
  • seller: the address of the seller.
  • client: the address of the client.
  • tx_nonce: the transaction nonce.
  • amount: the amount we expect to get from the transaction.

Returns:

True if the random_message is equals to tx['input']

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#generate_tx_nonce","title":"generate_tx_nonce","text":"
@staticmethod\ndef generate_tx_nonce(seller: Address, client: Address) -> str\n

Generate a unique hash to distinguish transactions with the same terms.

Arguments:

  • seller: the address of the seller.
  • client: the address of the client.

Returns:

return the hash in hex.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#get_address_from_public_key","title":"get_address_from_public_key","text":"
@classmethod\ndef get_address_from_public_key(cls, public_key: str) -> str\n

Get the address from the public key.

Arguments:

  • public_key: the public key

Returns:

str

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#recover_message","title":"recover_message","text":"
@classmethod\ndef recover_message(cls,\nmessage: bytes,\nsignature: str,\nis_deprecated_mode: bool = False) -> Tuple[Address, ...]\n

Recover the addresses from the hash.

Arguments:

  • message: the message we expect
  • signature: the transaction signature
  • is_deprecated_mode: if the deprecated signing was used

Returns:

the recovered addresses

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#recover_public_keys_from_message","title":"recover_public_keys_from_message","text":"
@classmethod\ndef recover_public_keys_from_message(\ncls,\nmessage: bytes,\nsignature: str,\nis_deprecated_mode: bool = False) -> Tuple[str, ...]\n

Get the public key used to produce the signature of the message

Arguments:

  • message: raw bytes used to produce signature
  • signature: signature of the message
  • is_deprecated_mode: if the deprecated signing was used

Returns:

the recovered public keys

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#get_hash","title":"get_hash","text":"
@staticmethod\ndef get_hash(message: bytes) -> str\n

Get the hash of a message.

Arguments:

  • message: the message to be hashed.

Returns:

the hash of the message.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#is_valid_address","title":"is_valid_address","text":"
@classmethod\ndef is_valid_address(cls, address: Address) -> bool\n

Check if the address is valid.

Arguments:

  • address: the address to validate

Returns:

whether address is valid or not

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#load_contract_interface","title":"load_contract_interface","text":"
@classmethod\ndef load_contract_interface(cls, file_path: Path) -> Dict[str, str]\n

Load contract interface.

Arguments:

  • file_path: the file path to the interface

Returns:

the interface

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#cosmoscrypto-objects","title":"CosmosCrypto Objects","text":"
class CosmosCrypto(Crypto[SigningKey])\n

Class wrapping the Account Generation from Ethereum ledger.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#__init__","title":"__init__","text":"
def __init__(private_key_path: Optional[str] = None,\npassword: Optional[str] = None) -> None\n

Instantiate an ethereum crypto object.

Arguments:

  • private_key_path: the private key path of the agent
  • password: the password to encrypt/decrypt the private key.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#private_key","title":"private_key","text":"
@property\ndef private_key() -> str\n

Return a private key.

Returns:

a private key string

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#public_key","title":"public_key","text":"
@property\ndef public_key() -> str\n

Return a public key in hex format.

Returns:

a public key string in hex format

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#address","title":"address","text":"
@property\ndef address() -> str\n

Return the address for the key pair.

Returns:

a display_address str

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#load_private_key_from_path","title":"load_private_key_from_path","text":"
@classmethod\ndef load_private_key_from_path(cls,\nfile_name: str,\npassword: Optional[str] = None) -> SigningKey\n

Load a private key in hex format from a file.

Arguments:

  • file_name: the path to the hex file.
  • password: the password to encrypt/decrypt the private key.

Returns:

the Entity.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#sign_message","title":"sign_message","text":"
def sign_message(message: bytes, is_deprecated_mode: bool = False) -> str\n

Sign a message in bytes string form.

Arguments:

  • message: the message to be signed
  • is_deprecated_mode: if the deprecated signing is used

Returns:

signature of the message in string form

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#sign_transaction","title":"sign_transaction","text":"
def sign_transaction(transaction: JSONLike) -> JSONLike\n

Sign a transaction in bytes string form.

Arguments:

  • transaction: the transaction to be signed

Returns:

signed transaction

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#generate_private_key","title":"generate_private_key","text":"
@classmethod\ndef generate_private_key(cls) -> SigningKey\n

Generate a key pair for cosmos network.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#encrypt_1","title":"encrypt","text":"
def encrypt(password: str) -> str\n

Encrypt the private key and return in json.

Arguments:

  • password: the password to decrypt.

Returns:

json string containing encrypted private key.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#decrypt_1","title":"decrypt","text":"
@classmethod\ndef decrypt(cls, keyfile_json: str, password: str) -> str\n

Decrypt the private key and return in raw form.

Arguments:

  • keyfile_json: json string containing encrypted private key.
  • password: the password to decrypt.

Returns:

the raw private key.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#_cosmosapi-objects","title":"_CosmosApi Objects","text":"
class _CosmosApi(LedgerApi)\n

Class to interact with the Cosmos SDK via a HTTP APIs.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#__init___1","title":"__init__","text":"
def __init__(**kwargs: Any) -> None\n

Initialize the Cosmos ledger APIs.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#api","title":"api","text":"
@property\ndef api() -> Any\n

Get the underlying API object.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#get_balance","title":"get_balance","text":"
def get_balance(address: Address) -> Optional[int]\n

Get the balance of a given account.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#get_state","title":"get_state","text":"
def get_state(callable_name: str, *args: Any,\n**kwargs: Any) -> Optional[JSONLike]\n

Call a specified function on the ledger API.

Based on the cosmos REST API specification, which takes a path (strings separated by '/'). The convention here is to define the root of the path (txs, blocks, etc.) as the callable_name and the rest of the path as args.

Arguments:

  • callable_name: name of the callable
  • args: positional arguments
  • kwargs: keyword arguments

Returns:

the transaction dictionary

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#get_deploy_transaction","title":"get_deploy_transaction","text":"
def get_deploy_transaction(contract_interface: Dict[str, str],\ndeployer_address: Address,\n**kwargs: Any) -> Optional[JSONLike]\n

Get the transaction to deploy the smart contract.

Dispatches to _get_storage_transaction and _get_init_transaction based on kwargs.

Arguments:

  • contract_interface: the contract interface.
  • deployer_address: The address that will deploy the contract.
  • kwargs: keyword arguments.

Returns:

the transaction dictionary.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#get_handle_transaction","title":"get_handle_transaction","text":"
def get_handle_transaction(\nsender_address: Address,\ncontract_address: Address,\nhandle_msg: Any,\namount: int,\ntx_fee: int,\ndenom: Optional[str] = None,\ngas: int = DEFAULT_GAS_AMOUNT,\nmemo: str = \"\",\nchain_id: Optional[str] = None,\naccount_number: Optional[int] = None,\nsequence: Optional[int] = None,\ntx_fee_denom: Optional[str] = None) -> Optional[JSONLike]\n

Create a CosmWasm HandleMsg transaction.

Arguments:

  • sender_address: the sender address of the message initiator.
  • contract_address: the address of the smart contract.
  • handle_msg: HandleMsg in JSON format.
  • amount: Funds amount sent with transaction.
  • tx_fee: the tx fee accepted.
  • denom: the name of the denomination of the contract funds
  • gas: Maximum amount of gas to be used on executing command.
  • memo: any string comment.
  • chain_id: the Chain ID of the CosmWasm transaction. Default is 1 (i.e. mainnet).
  • account_number: Account number
  • sequence: Sequence
  • tx_fee_denom: Denomination of tx_fee, identical with denom param when None

Returns:

the unsigned CosmWasm HandleMsg

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#execute_contract_query","title":"execute_contract_query","text":"
def execute_contract_query(contract_address: Address,\nquery_msg: JSONLike) -> Optional[JSONLike]\n

Execute a CosmWasm QueryMsg. QueryMsg doesn't require signing.

Arguments:

  • contract_address: the address of the smart contract.
  • query_msg: QueryMsg in JSON format.

Returns:

the message receipt

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#get_transfer_transaction","title":"get_transfer_transaction","text":"
def get_transfer_transaction(sender_address: Address,\ndestination_address: Address,\namount: int,\ntx_fee: int,\ntx_nonce: str,\ndenom: Optional[str] = None,\ngas: int = DEFAULT_GAS_AMOUNT,\nmemo: str = \"\",\nchain_id: Optional[str] = None,\naccount_number: Optional[int] = None,\nsequence: Optional[int] = None,\ntx_fee_denom: Optional[str] = None,\n**kwargs: Any) -> Optional[JSONLike]\n

Submit a transfer transaction to the ledger.

Arguments:

  • sender_address: the sender address of the payer.
  • destination_address: the destination address of the payee.
  • amount: the amount of wealth to be transferred.
  • tx_fee: the transaction fee.
  • tx_nonce: verifies the authenticity of the tx
  • denom: the denomination of tx fee and amount
  • gas: the gas used.
  • memo: memo to include in tx.
  • chain_id: the chain ID of the transaction.
  • account_number: Account number
  • sequence: Sequence
  • tx_fee_denom: Denomination of tx_fee, identical with denom param when None
  • kwargs: keyword arguments.

Returns:

the transfer transaction

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#get_packed_exec_msg","title":"get_packed_exec_msg","text":"
def get_packed_exec_msg(sender_address: Address,\ncontract_address: str,\nmsg: JSONLike,\nfunds: int = 0,\ndenom: Optional[str] = None) -> ProtoAny\n

Create and pack MsgExecuteContract

Arguments:

  • sender_address: Address of sender
  • contract_address: Address of contract
  • msg: Paramaters to be passed to smart contract
  • funds: Funds to be sent to smart contract
  • denom: the denomination of funds

Returns:

Packed MsgExecuteContract

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#get_packed_send_msg","title":"get_packed_send_msg","text":"
def get_packed_send_msg(from_address: Address,\nto_address: Address,\namount: int,\ndenom: Optional[str] = None) -> ProtoAny\n

Generate and pack MsgSend

Arguments:

  • from_address: Address of sender
  • to_address: Address of recipient
  • amount: amount of coins to be sent
  • denom: the denomination of and amount

Returns:

packer ProtoAny type message

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#get_multi_transaction","title":"get_multi_transaction","text":"
def get_multi_transaction(from_addresses: List[str],\npub_keys: Optional[List[bytes]],\nmsgs: List[ProtoAny],\ngas: int,\ntx_fee: int = 0,\nmemo: str = \"\",\nchain_id: Optional[str] = None,\ndenom: Optional[str] = None,\ntx_fee_denom: Optional[str] = None) -> JSONLike\n

Generate transaction with multiple messages

Arguments:

  • from_addresses: Addresses of signers
  • pub_keys: Public keys of signers
  • msgs: Messages to be included in transaction
  • gas: the gas used.
  • tx_fee: the transaction fee.
  • memo: memo to include in tx.
  • chain_id: the chain ID of the transaction.
  • denom: the denomination of tx fee
  • tx_fee_denom: Denomination of tx_fee, identical with denom param when None

Raises:

  • None: RuntimeError if number of pubkeys is not equal to number of from_addresses

Returns:

the transaction

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#send_signed_transaction","title":"send_signed_transaction","text":"
def send_signed_transaction(tx_signed: JSONLike) -> Optional[str]\n

Send a signed transaction and wait for confirmation.

Arguments:

  • tx_signed: the signed transaction

Returns:

tx_digest, if present

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#get_transaction_receipt","title":"get_transaction_receipt","text":"
def get_transaction_receipt(tx_digest: str) -> Optional[JSONLike]\n

Get the transaction receipt for a transaction digest.

Arguments:

  • tx_digest: the digest associated to the transaction.

Returns:

the tx receipt, if present

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#get_transaction","title":"get_transaction","text":"
def get_transaction(tx_digest: str) -> Optional[JSONLike]\n

Get the transaction for a transaction digest.

Arguments:

  • tx_digest: the digest associated to the transaction.

Returns:

the tx, if present

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#get_contract_instance","title":"get_contract_instance","text":"
def get_contract_instance(contract_interface: Dict[str, str],\ncontract_address: Optional[str] = None) -> Any\n

Get the instance of a contract.

Arguments:

  • contract_interface: the contract interface.
  • contract_address: the contract address.

Returns:

the contract instance

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#update_with_gas_estimate","title":"update_with_gas_estimate","text":"
def update_with_gas_estimate(transaction: JSONLike) -> JSONLike\n

Attempts to update the transaction with a gas estimate

Arguments:

  • transaction: the transaction

Raises:

  • None: NotImplementedError

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#cosmosapi-objects","title":"CosmosApi Objects","text":"
class CosmosApi(_CosmosApi, CosmosHelper)\n

Class to interact with the Cosmos SDK via a HTTP APIs.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#cosmosfaucetapi-objects","title":"CosmosFaucetApi Objects","text":"
class CosmosFaucetApi(FaucetApi)\n

Cosmos testnet faucet API.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#__init___2","title":"__init__","text":"
def __init__(poll_interval: Optional[float] = None,\nfinal_wait_interval: Optional[float] = None)\n

Initialize CosmosFaucetApi.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/#get_wealth","title":"get_wealth","text":"
def get_wealth(address: Address, url: Optional[str] = None) -> None\n

Get wealth from the faucet for the provided address.

Arguments:

  • address: the address.
  • url: the url

Raises:

  • None: RuntimeError of explicit faucet failures
"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/fetchai/","title":"API","text":""},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/fetchai/#pluginsaea-ledger-fetchaiaea_ledger_fetchaifetchai","title":"plugins.aea-ledger-fetchai.aea_ledger_fetchai.fetchai","text":"

Fetchai module wrapping the public and private key cryptography and ledger api.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/fetchai/#fetchaihelper-objects","title":"FetchAIHelper Objects","text":"
class FetchAIHelper(CosmosHelper)\n

Helper class usable as Mixin for FetchAIApi or as standalone class.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/fetchai/#fetchaicrypto-objects","title":"FetchAICrypto Objects","text":"
class FetchAICrypto(CosmosCrypto)\n

Class wrapping the Entity Generation from Fetch.AI ledger.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/fetchai/#fetchaiapi-objects","title":"FetchAIApi Objects","text":"
class FetchAIApi(_CosmosApi, FetchAIHelper)\n

Class to interact with the Fetch ledger APIs.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/fetchai/#__init__","title":"__init__","text":"
def __init__(**kwargs: Any) -> None\n

Initialize the Fetch.ai ledger APIs.

"},{"location":"aea-framework-documentation/api/plugins/aea_ledger_fetchai/fetchai/#fetchaifaucetapi-objects","title":"FetchAIFaucetApi Objects","text":"
class FetchAIFaucetApi(CosmosFaucetApi)\n

Fetchai testnet faucet API.

"},{"location":"aea-framework-documentation/api/protocols/base/","title":"Base","text":""},{"location":"aea-framework-documentation/api/protocols/base/#aeaprotocolsbase","title":"aea.protocols.base","text":"

This module contains the base message and serialization definition.

"},{"location":"aea-framework-documentation/api/protocols/base/#message-objects","title":"Message Objects","text":"
class Message()\n

This class implements a message.

"},{"location":"aea-framework-documentation/api/protocols/base/#performative-objects","title":"Performative Objects","text":"
class Performative(Enum)\n

Performatives for the base message.

"},{"location":"aea-framework-documentation/api/protocols/base/#__str__","title":"__str__","text":"
def __str__() -> str\n

Get the string representation.

"},{"location":"aea-framework-documentation/api/protocols/base/#__init__","title":"__init__","text":"
def __init__(_body: Optional[Dict] = None, **kwargs: Any) -> None\n

Initialize a Message object.

Arguments:

  • _body: the dictionary of values to hold.
  • kwargs: any additional value to add to the body. It will overwrite the body values.

"},{"location":"aea-framework-documentation/api/protocols/base/#json","title":"json","text":"
def json() -> dict\n

Get json friendly str representation of the message.

"},{"location":"aea-framework-documentation/api/protocols/base/#from_json","title":"from_json","text":"
@classmethod\ndef from_json(cls, data: dict) -> \"Message\"\n

Construct message instance from json data.

"},{"location":"aea-framework-documentation/api/protocols/base/#valid_performatives","title":"valid_performatives","text":"
@property\ndef valid_performatives() -> Set[str]\n

Get valid performatives.

"},{"location":"aea-framework-documentation/api/protocols/base/#has_sender","title":"has_sender","text":"
@property\ndef has_sender() -> bool\n

Check if it has a sender.

"},{"location":"aea-framework-documentation/api/protocols/base/#sender","title":"sender","text":"
@property\ndef sender() -> Address\n

Get the sender of the message in Address form.

"},{"location":"aea-framework-documentation/api/protocols/base/#sender_1","title":"sender","text":"
@sender.setter\ndef sender(sender: Address) -> None\n

Set the sender of the message.

"},{"location":"aea-framework-documentation/api/protocols/base/#has_to","title":"has_to","text":"
@property\ndef has_to() -> bool\n

Check if it has a sender.

"},{"location":"aea-framework-documentation/api/protocols/base/#to","title":"to","text":"
@property\ndef to() -> Address\n

Get address of receiver.

"},{"location":"aea-framework-documentation/api/protocols/base/#to_1","title":"to","text":"
@to.setter\ndef to(to: Address) -> None\n

Set address of receiver.

"},{"location":"aea-framework-documentation/api/protocols/base/#dialogue_reference","title":"dialogue_reference","text":"
@property\ndef dialogue_reference() -> Tuple[str, str]\n

Get the dialogue_reference of the message.

"},{"location":"aea-framework-documentation/api/protocols/base/#message_id","title":"message_id","text":"
@property\ndef message_id() -> int\n

Get the message_id of the message.

"},{"location":"aea-framework-documentation/api/protocols/base/#performative","title":"performative","text":"
@property\ndef performative() -> \"Performative\"\n

Get the performative of the message.

"},{"location":"aea-framework-documentation/api/protocols/base/#target","title":"target","text":"
@property\ndef target() -> int\n

Get the target of the message.

"},{"location":"aea-framework-documentation/api/protocols/base/#set","title":"set","text":"
def set(key: str, value: Any) -> None\n

Set key and value pair.

Arguments:

  • key: the key.
  • value: the value.

"},{"location":"aea-framework-documentation/api/protocols/base/#get","title":"get","text":"
def get(key: str) -> Optional[Any]\n

Get value for key.

"},{"location":"aea-framework-documentation/api/protocols/base/#is_set","title":"is_set","text":"
def is_set(key: str) -> bool\n

Check value is set for key.

"},{"location":"aea-framework-documentation/api/protocols/base/#__eq__","title":"__eq__","text":"
def __eq__(other: Any) -> bool\n

Compare with another object.

"},{"location":"aea-framework-documentation/api/protocols/base/#__repr__","title":"__repr__","text":"
def __repr__() -> str\n

Get the representation of the message.

"},{"location":"aea-framework-documentation/api/protocols/base/#__str___1","title":"__str__","text":"
def __str__() -> str\n

Get the string representation of the message. Abbreviated to prevent spamming of logs.

"},{"location":"aea-framework-documentation/api/protocols/base/#encode","title":"encode","text":"
def encode() -> bytes\n

Encode the message.

"},{"location":"aea-framework-documentation/api/protocols/base/#decode","title":"decode","text":"
@classmethod\ndef decode(cls, data: bytes) -> \"Message\"\n

Decode the message.

"},{"location":"aea-framework-documentation/api/protocols/base/#has_dialogue_info","title":"has_dialogue_info","text":"
@property\ndef has_dialogue_info() -> bool\n

Check whether a message has the dialogue fields populated.

More precisely, it checks whether the fields 'message_id', 'target' and 'dialogue_reference' are set.

Returns:

True if the message has the dialogue fields set, False otherwise.

"},{"location":"aea-framework-documentation/api/protocols/base/#encoder-objects","title":"Encoder Objects","text":"
class Encoder(ABC)\n

Encoder interface.

"},{"location":"aea-framework-documentation/api/protocols/base/#encode_1","title":"encode","text":"
@staticmethod\n@abstractmethod\ndef encode(msg: Message) -> bytes\n

Encode a message.

Arguments:

  • msg: the message to be encoded.

Returns:

the encoded message.

"},{"location":"aea-framework-documentation/api/protocols/base/#decoder-objects","title":"Decoder Objects","text":"
class Decoder(ABC)\n

Decoder interface.

"},{"location":"aea-framework-documentation/api/protocols/base/#decode_1","title":"decode","text":"
@staticmethod\n@abstractmethod\ndef decode(obj: bytes) -> Message\n

Decode a message.

Arguments:

  • obj: the sequence of bytes to be decoded.

Returns:

the decoded message.

"},{"location":"aea-framework-documentation/api/protocols/base/#serializer-objects","title":"Serializer Objects","text":"
class Serializer(Encoder, Decoder, ABC)\n

The implementations of this class defines a serialization layer for a protocol.

"},{"location":"aea-framework-documentation/api/protocols/base/#protocol-objects","title":"Protocol Objects","text":"
class Protocol(Component)\n

This class implements a specifications for a protocol.

It includes a serializer to encode/decode a message.

"},{"location":"aea-framework-documentation/api/protocols/base/#__init___1","title":"__init__","text":"
def __init__(configuration: ProtocolConfig, message_class: Type[Message],\n**kwargs: Any) -> None\n

Initialize the protocol manager.

Arguments:

  • configuration: the protocol configurations.
  • message_class: the message class.
  • kwargs: the keyword arguments.

"},{"location":"aea-framework-documentation/api/protocols/base/#serializer","title":"serializer","text":"
@property\ndef serializer() -> Type[Serializer]\n

Get the serializer.

"},{"location":"aea-framework-documentation/api/protocols/base/#from_dir","title":"from_dir","text":"
@classmethod\ndef from_dir(cls, directory: str, **kwargs: Any) -> \"Protocol\"\n

Load the protocol from a directory.

Arguments:

  • directory: the directory to the skill package.
  • kwargs: the keyword arguments.

Returns:

the protocol object.

"},{"location":"aea-framework-documentation/api/protocols/base/#from_config","title":"from_config","text":"
@classmethod\ndef from_config(cls, configuration: ProtocolConfig,\n**kwargs: Any) -> \"Protocol\"\n

Load the protocol from configuration.

Arguments:

  • configuration: the protocol configuration.
  • kwargs: the keyword arguments.

Returns:

the protocol object.

"},{"location":"aea-framework-documentation/api/protocols/base/#protocol_id","title":"protocol_id","text":"
@property\ndef protocol_id() -> PublicId\n

Get protocol id.

"},{"location":"aea-framework-documentation/api/protocols/base/#protocol_specification_id","title":"protocol_specification_id","text":"
@property\ndef protocol_specification_id() -> PublicId\n

Get protocol specification id.

"},{"location":"aea-framework-documentation/api/protocols/base/#__repr___1","title":"__repr__","text":"
def __repr__() -> str\n

Get str representation of the protocol.

"},{"location":"aea-framework-documentation/api/protocols/default/custom_types/","title":"Custom Types","text":""},{"location":"aea-framework-documentation/api/protocols/default/custom_types/#packagesfetchaiprotocolsdefaultcustom_types","title":"packages.fetchai.protocols.default.custom_types","text":"

This module contains class representations corresponding to every custom type in the protocol specification.

"},{"location":"aea-framework-documentation/api/protocols/default/custom_types/#errorcode-objects","title":"ErrorCode Objects","text":"
class ErrorCode(Enum)\n

This class represents an instance of ErrorCode.

"},{"location":"aea-framework-documentation/api/protocols/default/custom_types/#encode","title":"encode","text":"
@staticmethod\ndef encode(error_code_protobuf_object: Any,\nerror_code_object: \"ErrorCode\") -> None\n

Encode an instance of this class into the protocol buffer object.

The protocol buffer object in the error_code_protobuf_object argument is matched with the instance of this class in the 'error_code_object' argument.

Arguments:

  • error_code_protobuf_object: the protocol buffer object whose type corresponds with this class.
  • error_code_object: an instance of this class to be encoded in the protocol buffer object.

"},{"location":"aea-framework-documentation/api/protocols/default/custom_types/#decode","title":"decode","text":"
@classmethod\ndef decode(cls, error_code_protobuf_object: Any) -> \"ErrorCode\"\n

Decode a protocol buffer object that corresponds with this class into an instance of this class.

A new instance of this class is created that matches the protocol buffer object in the 'error_code_protobuf_object' argument.

Arguments:

  • error_code_protobuf_object: the protocol buffer object whose type corresponds with this class.

Returns:

A new instance of this class that matches the protocol buffer object in the 'error_code_protobuf_object' argument.

"},{"location":"aea-framework-documentation/api/protocols/default/dialogues/","title":"Dialogues","text":""},{"location":"aea-framework-documentation/api/protocols/default/dialogues/#packagesfetchaiprotocolsdefaultdialogues","title":"packages.fetchai.protocols.default.dialogues","text":"

This module contains the classes required for default dialogue management.

  • DefaultDialogue: The dialogue class maintains state of a dialogue and manages it.
  • DefaultDialogues: The dialogues class keeps track of all dialogues.

"},{"location":"aea-framework-documentation/api/protocols/default/dialogues/#defaultdialogue-objects","title":"DefaultDialogue Objects","text":"
class DefaultDialogue(Dialogue)\n

The default dialogue class maintains state of a dialogue and manages it.

"},{"location":"aea-framework-documentation/api/protocols/default/dialogues/#role-objects","title":"Role Objects","text":"
class Role(Dialogue.Role)\n

This class defines the agent's role in a default dialogue.

"},{"location":"aea-framework-documentation/api/protocols/default/dialogues/#endstate-objects","title":"EndState Objects","text":"
class EndState(Dialogue.EndState)\n

This class defines the end states of a default dialogue.

"},{"location":"aea-framework-documentation/api/protocols/default/dialogues/#__init__","title":"__init__","text":"
def __init__(dialogue_label: DialogueLabel,\nself_address: Address,\nrole: Dialogue.Role,\nmessage_class: Type[DefaultMessage] = DefaultMessage) -> None\n

Initialize a dialogue.

Arguments:

  • dialogue_label: the identifier of the dialogue
  • self_address: the address of the entity for whom this dialogue is maintained
  • role: the role of the agent this dialogue is maintained for
  • message_class: the message class used

"},{"location":"aea-framework-documentation/api/protocols/default/dialogues/#defaultdialogues-objects","title":"DefaultDialogues Objects","text":"
class DefaultDialogues(Dialogues, ABC)\n

This class keeps track of all default dialogues.

"},{"location":"aea-framework-documentation/api/protocols/default/dialogues/#__init___1","title":"__init__","text":"
def __init__(self_address: Address,\nrole_from_first_message: Callable[[Message, Address],\nDialogue.Role],\ndialogue_class: Type[DefaultDialogue] = DefaultDialogue) -> None\n

Initialize dialogues.

Arguments:

  • self_address: the address of the entity for whom dialogues are maintained
  • dialogue_class: the dialogue class used
  • role_from_first_message: the callable determining role from first message
"},{"location":"aea-framework-documentation/api/protocols/default/message/","title":"Message","text":""},{"location":"aea-framework-documentation/api/protocols/default/message/#packagesfetchaiprotocolsdefaultmessage","title":"packages.fetchai.protocols.default.message","text":"

This module contains default's message definition.

"},{"location":"aea-framework-documentation/api/protocols/default/message/#defaultmessage-objects","title":"DefaultMessage Objects","text":"
class DefaultMessage(Message)\n

A protocol for exchanging any bytes message.

"},{"location":"aea-framework-documentation/api/protocols/default/message/#performative-objects","title":"Performative Objects","text":"
class Performative(Message.Performative)\n

Performatives for the default protocol.

"},{"location":"aea-framework-documentation/api/protocols/default/message/#__str__","title":"__str__","text":"
def __str__() -> str\n

Get the string representation.

"},{"location":"aea-framework-documentation/api/protocols/default/message/#__init__","title":"__init__","text":"
def __init__(performative: Performative,\ndialogue_reference: Tuple[str, str] = (\"\", \"\"),\nmessage_id: int = 1,\ntarget: int = 0,\n**kwargs: Any)\n

Initialise an instance of DefaultMessage.

Arguments:

  • message_id: the message id.
  • dialogue_reference: the dialogue reference.
  • target: the message target.
  • performative: the message performative.
  • **kwargs: extra options.

"},{"location":"aea-framework-documentation/api/protocols/default/message/#valid_performatives","title":"valid_performatives","text":"
@property\ndef valid_performatives() -> Set[str]\n

Get valid performatives.

"},{"location":"aea-framework-documentation/api/protocols/default/message/#dialogue_reference","title":"dialogue_reference","text":"
@property\ndef dialogue_reference() -> Tuple[str, str]\n

Get the dialogue_reference of the message.

"},{"location":"aea-framework-documentation/api/protocols/default/message/#message_id","title":"message_id","text":"
@property\ndef message_id() -> int\n

Get the message_id of the message.

"},{"location":"aea-framework-documentation/api/protocols/default/message/#performative","title":"performative","text":"
@property\ndef performative() -> Performative\n

Get the performative of the message.

"},{"location":"aea-framework-documentation/api/protocols/default/message/#target","title":"target","text":"
@property\ndef target() -> int\n

Get the target of the message.

"},{"location":"aea-framework-documentation/api/protocols/default/message/#content","title":"content","text":"
@property\ndef content() -> bytes\n

Get the 'content' content from the message.

"},{"location":"aea-framework-documentation/api/protocols/default/message/#error_code","title":"error_code","text":"
@property\ndef error_code() -> CustomErrorCode\n

Get the 'error_code' content from the message.

"},{"location":"aea-framework-documentation/api/protocols/default/message/#error_data","title":"error_data","text":"
@property\ndef error_data() -> Dict[str, bytes]\n

Get the 'error_data' content from the message.

"},{"location":"aea-framework-documentation/api/protocols/default/message/#error_msg","title":"error_msg","text":"
@property\ndef error_msg() -> str\n

Get the 'error_msg' content from the message.

"},{"location":"aea-framework-documentation/api/protocols/default/serialization/","title":"Serialization","text":""},{"location":"aea-framework-documentation/api/protocols/default/serialization/#packagesfetchaiprotocolsdefaultserialization","title":"packages.fetchai.protocols.default.serialization","text":"

Serialization module for default protocol.

"},{"location":"aea-framework-documentation/api/protocols/default/serialization/#defaultserializer-objects","title":"DefaultSerializer Objects","text":"
class DefaultSerializer(Serializer)\n

Serialization for the 'default' protocol.

"},{"location":"aea-framework-documentation/api/protocols/default/serialization/#encode","title":"encode","text":"
@staticmethod\ndef encode(msg: Message) -> bytes\n

Encode a 'Default' message into bytes.

Arguments:

  • msg: the message object.

Returns:

the bytes.

"},{"location":"aea-framework-documentation/api/protocols/default/serialization/#decode","title":"decode","text":"
@staticmethod\ndef decode(obj: bytes) -> Message\n

Decode bytes into a 'Default' message.

Arguments:

  • obj: the bytes object.

Returns:

the 'Default' message.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/","title":"Base","text":""},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#aeaprotocolsdialoguebase","title":"aea.protocols.dialogue.base","text":"

This module contains the classes required for dialogue management.

  • DialogueLabel: The dialogue label class acts as an identifier for dialogues.
  • Dialogue: The dialogue class maintains state of a dialogue and manages it.
  • Dialogues: The dialogues class keeps track of all dialogues.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#invaliddialoguemessage-objects","title":"InvalidDialogueMessage Objects","text":"
class InvalidDialogueMessage(Exception)\n

Exception for adding invalid message to a dialogue.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#dialoguelabel-objects","title":"DialogueLabel Objects","text":"
class DialogueLabel()\n

The dialogue label class acts as an identifier for dialogues.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#__init__","title":"__init__","text":"
def __init__(dialogue_reference: Tuple[str,\nstr], dialogue_opponent_addr: Address,\ndialogue_starter_addr: Address) -> None\n

Initialize a dialogue label.

Arguments:

  • dialogue_reference: the reference of the dialogue.
  • dialogue_opponent_addr: the addr of the agent with which the dialogue is kept.
  • dialogue_starter_addr: the addr of the agent which started the dialogue.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#dialogue_reference","title":"dialogue_reference","text":"
@property\ndef dialogue_reference() -> Tuple[str, str]\n

Get the dialogue reference.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#dialogue_starter_reference","title":"dialogue_starter_reference","text":"
@property\ndef dialogue_starter_reference() -> str\n

Get the dialogue starter reference.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#dialogue_responder_reference","title":"dialogue_responder_reference","text":"
@property\ndef dialogue_responder_reference() -> str\n

Get the dialogue responder reference.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#dialogue_opponent_addr","title":"dialogue_opponent_addr","text":"
@property\ndef dialogue_opponent_addr() -> str\n

Get the address of the dialogue opponent.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#dialogue_starter_addr","title":"dialogue_starter_addr","text":"
@property\ndef dialogue_starter_addr() -> str\n

Get the address of the dialogue starter.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#__eq__","title":"__eq__","text":"
def __eq__(other: Any) -> bool\n

Check for equality between two DialogueLabel objects.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#__hash__","title":"__hash__","text":"
def __hash__() -> int\n

Turn object into hash.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#json","title":"json","text":"
@property\ndef json() -> Dict\n

Return the JSON representation.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#from_json","title":"from_json","text":"
@classmethod\ndef from_json(cls, obj: Dict[str, str]) -> \"DialogueLabel\"\n

Get dialogue label from json.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#get_incomplete_version","title":"get_incomplete_version","text":"
def get_incomplete_version() -> \"DialogueLabel\"\n

Get the incomplete version of the label.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#__str__","title":"__str__","text":"
def __str__() -> str\n

Get the string representation.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#from_str","title":"from_str","text":"
@classmethod\ndef from_str(cls, obj: str) -> \"DialogueLabel\"\n

Get the dialogue label from string representation.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#_dialoguemeta-objects","title":"_DialogueMeta Objects","text":"
class _DialogueMeta(type)\n

Metaclass for Dialogue.

Creates class level Rules instance to share among instances

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#__new__","title":"__new__","text":"
def __new__(cls, name: str, bases: Tuple[Type], dct: Dict) -> \"_DialogueMeta\"\n

Construct a new type.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#dialogue-objects","title":"Dialogue Objects","text":"
class Dialogue(metaclass=_DialogueMeta)\n

The dialogue class maintains state of a dialogue and manages it.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#rules-objects","title":"Rules Objects","text":"
class Rules()\n

This class defines the rules for the dialogue.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#__init___1","title":"__init__","text":"
def __init__(\ninitial_performatives: FrozenSet[Message.Performative],\nterminal_performatives: FrozenSet[Message.Performative],\nvalid_replies: Dict[Message.Performative, FrozenSet[Message.Performative]]\n) -> None\n

Initialize a dialogue.

Arguments:

  • initial_performatives: the set of all initial performatives.
  • terminal_performatives: the set of all terminal performatives.
  • valid_replies: the reply structure of speech-acts.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#initial_performatives","title":"initial_performatives","text":"
@property\ndef initial_performatives() -> FrozenSet[Message.Performative]\n

Get the performatives one of which the terminal message in the dialogue must have.

Returns:

the valid performatives of an terminal message

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#terminal_performatives","title":"terminal_performatives","text":"
@property\ndef terminal_performatives() -> FrozenSet[Message.Performative]\n

Get the performatives one of which the terminal message in the dialogue must have.

Returns:

the valid performatives of an terminal message

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#valid_replies","title":"valid_replies","text":"
@property\ndef valid_replies(\n) -> Dict[Message.Performative, FrozenSet[Message.Performative]]\n

Get all the valid performatives which are a valid replies to performatives.

Returns:

the full valid reply structure.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#get_valid_replies","title":"get_valid_replies","text":"
def get_valid_replies(\nperformative: Message.Performative) -> FrozenSet[Message.Performative]\n

Given a performative, return the list of performatives which are its valid replies in a dialogue.

Arguments:

  • performative: the performative in a message

Returns:

list of valid performative replies

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#role-objects","title":"Role Objects","text":"
class Role(Enum)\n

This class defines the agent's role in a dialogue.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#__str___1","title":"__str__","text":"
def __str__() -> str\n

Get the string representation.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#endstate-objects","title":"EndState Objects","text":"
class EndState(Enum)\n

This class defines the end states of a dialogue.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#__str___2","title":"__str__","text":"
def __str__() -> str\n

Get the string representation.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#__init___2","title":"__init__","text":"
def __init__(dialogue_label: DialogueLabel, message_class: Type[Message],\nself_address: Address, role: Role) -> None\n

Initialize a dialogue.

Arguments:

  • dialogue_label: the identifier of the dialogue
  • message_class: the message class used
  • self_address: the address of the entity for whom this dialogue is maintained
  • role: the role of the agent this dialogue is maintained for

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#add_terminal_state_callback","title":"add_terminal_state_callback","text":"
def add_terminal_state_callback(fn: Callable[[\"Dialogue\"], None]) -> None\n

Add callback to be called on dialogue reach terminal state.

Arguments:

  • fn: callable to be called with one argument: Dialogue

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#__eq___1","title":"__eq__","text":"
def __eq__(other: Any) -> bool\n

Compare two dialogues.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#json_1","title":"json","text":"
def json() -> dict\n

Get json representation of the dialogue.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#from_json_1","title":"from_json","text":"
@classmethod\ndef from_json(cls, message_class: Type[Message], data: dict) -> \"Dialogue\"\n

Create a dialogue instance with all messages from json data.

Arguments:

  • message_class: type of message used with this dialogue
  • data: dict with data exported with Dialogue.to_json() method

Returns:

Dialogue instance

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#dialogue_label","title":"dialogue_label","text":"
@property\ndef dialogue_label() -> DialogueLabel\n

Get the dialogue label.

Returns:

The dialogue label

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#incomplete_dialogue_label","title":"incomplete_dialogue_label","text":"
@property\ndef incomplete_dialogue_label() -> DialogueLabel\n

Get the dialogue label.

Returns:

The incomplete dialogue label

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#dialogue_labels","title":"dialogue_labels","text":"
@property\ndef dialogue_labels() -> Set[DialogueLabel]\n

Get the dialogue labels (incomplete and complete, if it exists).

Returns:

the dialogue labels

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#self_address","title":"self_address","text":"
@property\ndef self_address() -> Address\n

Get the address of the entity for whom this dialogues is maintained.

Returns:

the address of this entity

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#role","title":"role","text":"
@property\ndef role() -> \"Role\"\n

Get the agent's role in the dialogue.

Returns:

the agent's role

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#rules","title":"rules","text":"
@property\ndef rules() -> \"Rules\"\n

Get the dialogue rules.

Returns:

the rules

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#message_class","title":"message_class","text":"
@property\ndef message_class() -> Type[Message]\n

Get the message class.

Returns:

the message class

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#is_self_initiated","title":"is_self_initiated","text":"
@property\ndef is_self_initiated() -> bool\n

Check whether the agent initiated the dialogue.

Returns:

True if the agent initiated the dialogue, False otherwise

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#last_incoming_message","title":"last_incoming_message","text":"
@property\ndef last_incoming_message() -> Optional[Message]\n

Get the last incoming message.

Returns:

the last incoming message if it exists, None otherwise

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#last_outgoing_message","title":"last_outgoing_message","text":"
@property\ndef last_outgoing_message() -> Optional[Message]\n

Get the last outgoing message.

Returns:

the last outgoing message if it exists, None otherwise

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#last_message","title":"last_message","text":"
@property\ndef last_message() -> Optional[Message]\n

Get the last message.

Returns:

the last message if it exists, None otherwise

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#is_empty","title":"is_empty","text":"
@property\ndef is_empty() -> bool\n

Check whether the dialogue is empty.

Returns:

True if empty, False otherwise

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#reply","title":"reply","text":"
def reply(performative: Message.Performative,\ntarget_message: Optional[Message] = None,\ntarget: Optional[int] = None,\n**kwargs: Any) -> Message\n

Reply to the 'target_message' in this dialogue with a message with 'performative', and contents from kwargs.

Note if no target_message is provided, the last message in the dialogue will be replied to.

Arguments:

  • target_message: the message to reply to.
  • target: the id of the message to reply to.
  • performative: the performative of the reply message.
  • kwargs: the content of the reply message.

Returns:

the reply message if it was successfully added as a reply, None otherwise.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#get_message_by_id","title":"get_message_by_id","text":"
def get_message_by_id(message_id: int) -> Optional[Message]\n

Get message by id, if not presents return None.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#get_outgoing_next_message_id","title":"get_outgoing_next_message_id","text":"
def get_outgoing_next_message_id() -> int\n

Get next outgoing message id.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#get_incoming_next_message_id","title":"get_incoming_next_message_id","text":"
def get_incoming_next_message_id() -> int\n

Get next incoming message id.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#__str___3","title":"__str__","text":"
def __str__() -> str\n

Get the string representation.

Returns:

The string representation of the dialogue

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#dialoguestats-objects","title":"DialogueStats Objects","text":"
class DialogueStats()\n

Class to handle statistics on default dialogues.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#__init___3","title":"__init__","text":"
def __init__(end_states: FrozenSet[Dialogue.EndState]) -> None\n

Initialize a StatsManager.

Arguments:

  • end_states: the list of dialogue endstates

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#self_initiated","title":"self_initiated","text":"
@property\ndef self_initiated() -> Dict[Dialogue.EndState, int]\n

Get the stats dictionary on self initiated dialogues.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#other_initiated","title":"other_initiated","text":"
@property\ndef other_initiated() -> Dict[Dialogue.EndState, int]\n

Get the stats dictionary on other initiated dialogues.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#add_dialogue_endstate","title":"add_dialogue_endstate","text":"
def add_dialogue_endstate(end_state: Dialogue.EndState,\nis_self_initiated: bool) -> None\n

Add dialogue endstate stats.

Arguments:

  • end_state: the end state of the dialogue
  • is_self_initiated: whether the dialogue is initiated by the agent or the opponent

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#find_caller_object","title":"find_caller_object","text":"
def find_caller_object(object_type: Type) -> Any\n

Find caller object of certain type in the call stack.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#basicdialoguesstorage-objects","title":"BasicDialoguesStorage Objects","text":"
class BasicDialoguesStorage()\n

Dialogues state storage.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#__init___4","title":"__init__","text":"
def __init__(dialogues: \"Dialogues\") -> None\n

Init dialogues storage.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#dialogues_in_terminal_state","title":"dialogues_in_terminal_state","text":"
@property\ndef dialogues_in_terminal_state() -> List[\"Dialogue\"]\n

Get all dialogues in terminal state.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#dialogues_in_active_state","title":"dialogues_in_active_state","text":"
@property\ndef dialogues_in_active_state() -> List[\"Dialogue\"]\n

Get all dialogues in active state.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#is_terminal_dialogues_kept","title":"is_terminal_dialogues_kept","text":"
@property\ndef is_terminal_dialogues_kept() -> bool\n

Return True if dialogues should stay after terminal state.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#dialogue_terminal_state_callback","title":"dialogue_terminal_state_callback","text":"
def dialogue_terminal_state_callback(dialogue: \"Dialogue\") -> None\n

Method to be called on dialogue terminal state reached.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#setup","title":"setup","text":"
def setup() -> None\n

Set up dialogue storage.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#teardown","title":"teardown","text":"
def teardown() -> None\n

Tear down dialogue storage.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#add","title":"add","text":"
def add(dialogue: Dialogue) -> None\n

Add dialogue to storage.

Arguments:

  • dialogue: dialogue to add.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#remove","title":"remove","text":"
def remove(dialogue_label: DialogueLabel) -> None\n

Remove dialogue from storage by it's label.

Arguments:

  • dialogue_label: label of the dialogue to remove

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#get","title":"get","text":"
def get(dialogue_label: DialogueLabel) -> Optional[Dialogue]\n

Get dialogue stored by it's label.

Arguments:

  • dialogue_label: label of the dialogue

Returns:

dialogue if presents or None

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#get_dialogues_with_counterparty","title":"get_dialogues_with_counterparty","text":"
def get_dialogues_with_counterparty(counterparty: Address) -> List[Dialogue]\n

Get the dialogues by address.

Arguments:

  • counterparty: the counterparty

Returns:

The dialogues with the counterparty.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#is_in_incomplete","title":"is_in_incomplete","text":"
def is_in_incomplete(dialogue_label: DialogueLabel) -> bool\n

Check dialogue label presents in list of incomplete.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#set_incomplete_dialogue","title":"set_incomplete_dialogue","text":"
def set_incomplete_dialogue(incomplete_dialogue_label: DialogueLabel,\ncomplete_dialogue_label: DialogueLabel) -> None\n

Set incomplete dialogue label.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#is_dialogue_present","title":"is_dialogue_present","text":"
def is_dialogue_present(dialogue_label: DialogueLabel) -> bool\n

Check dialogue with label specified presents in storage.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#get_latest_label","title":"get_latest_label","text":"
def get_latest_label(dialogue_label: DialogueLabel) -> DialogueLabel\n

Get latest label for dialogue.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#persistdialoguesstorage-objects","title":"PersistDialoguesStorage Objects","text":"
class PersistDialoguesStorage(BasicDialoguesStorage)\n

Persist dialogues storage.

Uses generic storage to load/save dialogues data on setup/teardown.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#__init___5","title":"__init__","text":"
def __init__(dialogues: \"Dialogues\") -> None\n

Init dialogues storage.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#get_skill_component","title":"get_skill_component","text":"
@staticmethod\ndef get_skill_component() -> Optional[SkillComponent]\n

Get skill component dialogues storage constructed for.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#setup_1","title":"setup","text":"
def setup() -> None\n

Set up dialogue storage.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#teardown_1","title":"teardown","text":"
def teardown() -> None\n

Tear down dialogue storage.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#remove_1","title":"remove","text":"
def remove(dialogue_label: DialogueLabel) -> None\n

Remove dialogue from memory and persistent storage.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#persistdialoguesstoragewithoffloading-objects","title":"PersistDialoguesStorageWithOffloading Objects","text":"
class PersistDialoguesStorageWithOffloading(PersistDialoguesStorage)\n

Dialogue Storage with dialogues offloading.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#dialogue_terminal_state_callback_1","title":"dialogue_terminal_state_callback","text":"
def dialogue_terminal_state_callback(dialogue: \"Dialogue\") -> None\n

Call on dialogue reaches terminal state.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#get_1","title":"get","text":"
def get(dialogue_label: DialogueLabel) -> Optional[Dialogue]\n

Try to get dialogue by label from memory or persists storage.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#get_dialogues_with_counterparty_1","title":"get_dialogues_with_counterparty","text":"
def get_dialogues_with_counterparty(counterparty: Address) -> List[Dialogue]\n

Get the dialogues by address.

Arguments:

  • counterparty: the counterparty

Returns:

The dialogues with the counterparty.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#dialogues_in_terminal_state_1","title":"dialogues_in_terminal_state","text":"
@property\ndef dialogues_in_terminal_state() -> List[\"Dialogue\"]\n

Get all dialogues in terminal state.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#dialogues-objects","title":"Dialogues Objects","text":"
class Dialogues()\n

The dialogues class keeps track of all dialogues for an agent.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#__init___6","title":"__init__","text":"
def __init__(self_address: Address,\nend_states: FrozenSet[Dialogue.EndState],\nmessage_class: Type[Message],\ndialogue_class: Type[Dialogue],\nrole_from_first_message: Callable[[Message, Address],\nDialogue.Role],\nkeep_terminal_state_dialogues: Optional[bool] = None) -> None\n

Initialize dialogues.

Arguments:

  • self_address: the address of the entity for whom dialogues are maintained
  • end_states: the list of dialogue endstates
  • message_class: the message class used
  • dialogue_class: the dialogue class used
  • role_from_first_message: the callable determining role from first message
  • keep_terminal_state_dialogues: specify do dialogues in terminal state should stay or not

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#is_keep_dialogues_in_terminal_state","title":"is_keep_dialogues_in_terminal_state","text":"
@property\ndef is_keep_dialogues_in_terminal_state() -> bool\n

Is required to keep dialogues in terminal state.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#self_address_1","title":"self_address","text":"
@property\ndef self_address() -> Address\n

Get the address of the agent for whom dialogues are maintained.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#dialogue_stats","title":"dialogue_stats","text":"
@property\ndef dialogue_stats() -> DialogueStats\n

Get the dialogue statistics.

Returns:

dialogue stats object

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#message_class_1","title":"message_class","text":"
@property\ndef message_class() -> Type[Message]\n

Get the message class.

Returns:

the message class

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#dialogue_class","title":"dialogue_class","text":"
@property\ndef dialogue_class() -> Type[Dialogue]\n

Get the dialogue class.

Returns:

the dialogue class

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#get_dialogues_with_counterparty_2","title":"get_dialogues_with_counterparty","text":"
def get_dialogues_with_counterparty(counterparty: Address) -> List[Dialogue]\n

Get the dialogues by address.

Arguments:

  • counterparty: the counterparty

Returns:

The dialogues with the counterparty.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#new_self_initiated_dialogue_reference","title":"new_self_initiated_dialogue_reference","text":"
@classmethod\ndef new_self_initiated_dialogue_reference(cls) -> Tuple[str, str]\n

Return a dialogue label for a new self initiated dialogue.

Returns:

the next nonce

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#create","title":"create","text":"
def create(counterparty: Address, performative: Message.Performative,\n**kwargs: Any) -> Tuple[Message, Dialogue]\n

Create a dialogue with 'counterparty', with an initial message whose performative is 'performative' and contents are from 'kwargs'.

Arguments:

  • counterparty: the counterparty of the dialogue.
  • performative: the performative of the initial message.
  • kwargs: the content of the initial message.

Returns:

the initial message and the dialogue.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#create_with_message","title":"create_with_message","text":"
def create_with_message(counterparty: Address,\ninitial_message: Message) -> Dialogue\n

Create a dialogue with 'counterparty', with an initial message provided.

Arguments:

  • counterparty: the counterparty of the dialogue.
  • initial_message: the initial_message.

Returns:

the initial message and the dialogue.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#update","title":"update","text":"
def update(message: Message) -> Optional[Dialogue]\n

Update the state of dialogues with a new incoming message.

If the message is for a new dialogue, a new dialogue is created with 'message' as its first message, and returned. If the message is addressed to an existing dialogue, the dialogue is retrieved, extended with this message and returned. If there are any errors, e.g. the message dialogue reference does not exists or the message is invalid w.r.t. the dialogue, return None.

Arguments:

  • message: a new incoming message

Returns:

the new or existing dialogue the message is intended for, or None in case of any errors.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#get_dialogue","title":"get_dialogue","text":"
def get_dialogue(message: Message) -> Optional[Dialogue]\n

Retrieve the dialogue 'message' belongs to.

Arguments:

  • message: a message

Returns:

the dialogue, or None in case such a dialogue does not exist

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#get_dialogue_from_label","title":"get_dialogue_from_label","text":"
def get_dialogue_from_label(\ndialogue_label: DialogueLabel) -> Optional[Dialogue]\n

Retrieve a dialogue based on its label.

Arguments:

  • dialogue_label: the dialogue label

Returns:

the dialogue if present

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#setup_2","title":"setup","text":"
def setup() -> None\n

Set up.

"},{"location":"aea-framework-documentation/api/protocols/dialogue/base/#teardown_2","title":"teardown","text":"
def teardown() -> None\n

Tear down.

"},{"location":"aea-framework-documentation/api/protocols/generator/common/","title":"Common","text":""},{"location":"aea-framework-documentation/api/protocols/generator/common/#aeaprotocolsgeneratorcommon","title":"aea.protocols.generator.common","text":"

This module contains utility code for generator modules.

"},{"location":"aea-framework-documentation/api/protocols/generator/common/#is_installed","title":"is_installed","text":"
def is_installed(programme: str) -> bool\n

Check whether a programme is installed on the system.

Arguments:

  • programme: the name of the programme.

Returns:

True if installed, False otherwise

"},{"location":"aea-framework-documentation/api/protocols/generator/common/#base_protolint_command","title":"base_protolint_command","text":"
def base_protolint_command() -> str\n

Return the base protolint command.

Returns:

The base protolint command

"},{"location":"aea-framework-documentation/api/protocols/generator/common/#check_prerequisites","title":"check_prerequisites","text":"
def check_prerequisites() -> None\n

Check whether a programme is installed on the system.

"},{"location":"aea-framework-documentation/api/protocols/generator/common/#get_protoc_version","title":"get_protoc_version","text":"
def get_protoc_version() -> str\n

Get the protoc version used.

"},{"location":"aea-framework-documentation/api/protocols/generator/common/#load_protocol_specification","title":"load_protocol_specification","text":"
def load_protocol_specification(\nspecification_path: str) -> ProtocolSpecification\n

Load a protocol specification.

Arguments:

  • specification_path: path to the protocol specification yaml file.

Returns:

A ProtocolSpecification object

"},{"location":"aea-framework-documentation/api/protocols/generator/common/#try_run_black_formatting","title":"try_run_black_formatting","text":"
def try_run_black_formatting(path_to_protocol_package: str) -> None\n

Run Black code formatting via subprocess.

Arguments:

  • path_to_protocol_package: a path where formatting should be applied.

"},{"location":"aea-framework-documentation/api/protocols/generator/common/#try_run_isort_formatting","title":"try_run_isort_formatting","text":"
def try_run_isort_formatting(path_to_protocol_package: str) -> None\n

Run Isort code formatting via subprocess.

Arguments:

  • path_to_protocol_package: a path where formatting should be applied.

"},{"location":"aea-framework-documentation/api/protocols/generator/common/#try_run_protoc","title":"try_run_protoc","text":"
def try_run_protoc(path_to_generated_protocol_package: str,\nname: str,\nlanguage: str = PROTOCOL_LANGUAGE_PYTHON) -> None\n

Run 'protoc' protocol buffer compiler via subprocess.

Arguments:

  • path_to_generated_protocol_package: path to the protocol buffer schema file.
  • name: name of the protocol buffer schema file.
  • language: the target language in which to compile the protobuf schema file

"},{"location":"aea-framework-documentation/api/protocols/generator/common/#try_run_protolint","title":"try_run_protolint","text":"
def try_run_protolint(path_to_generated_protocol_package: str,\nname: str) -> None\n

Run 'protolint' linter via subprocess.

Arguments:

  • path_to_generated_protocol_package: path to the protocol buffer schema file.
  • name: name of the protocol buffer schema file.

"},{"location":"aea-framework-documentation/api/protocols/generator/common/#check_protobuf_using_protoc","title":"check_protobuf_using_protoc","text":"
def check_protobuf_using_protoc(path_to_generated_protocol_package: str,\nname: str) -> Tuple[bool, str]\n

Check whether a protocol buffer schema file is valid.

Validation is via trying to compile the schema file. If successfully compiled it is valid, otherwise invalid. If valid, return True and a 'protobuf file is valid' message, otherwise return False and the error thrown by the compiler.

Arguments:

  • path_to_generated_protocol_package: path to the protocol buffer schema file.
  • name: name of the protocol buffer schema file.

Returns:

Boolean result and an accompanying message

"},{"location":"aea-framework-documentation/api/protocols/generator/common/#compile_protobuf_using_protoc","title":"compile_protobuf_using_protoc","text":"
def compile_protobuf_using_protoc(path_to_generated_protocol_package: str,\nname: str,\nlanguage: str) -> Tuple[bool, str]\n

Compile a protocol buffer schema file using protoc.

If successfully compiled, return True and a success message, otherwise return False and the error thrown by the compiler.

Arguments:

  • path_to_generated_protocol_package: path to the protocol buffer schema file.
  • name: name of the protocol buffer schema file.
  • language: the target language in which to compile the protobuf schema file

Returns:

Boolean result and an accompanying message

"},{"location":"aea-framework-documentation/api/protocols/generator/common/#apply_protolint","title":"apply_protolint","text":"
def apply_protolint(path_to_proto_file: str, name: str) -> Tuple[bool, str]\n

Apply protolint linter to a protocol buffer schema file.

If no output, return True and a success message, otherwise return False and the output shown by the linter (minus the indentation suggestions which are automatically fixed by protolint).

Arguments:

  • path_to_proto_file: path to the protocol buffer schema file.
  • name: name of the protocol buffer schema file.

Returns:

Boolean result and an accompanying message

"},{"location":"aea-framework-documentation/api/protocols/generator/extract_specification/","title":"Extract Specification","text":""},{"location":"aea-framework-documentation/api/protocols/generator/extract_specification/#aeaprotocolsgeneratorextract_specification","title":"aea.protocols.generator.extract_specification","text":"

This module extracts a valid protocol specification into pythonic objects.

"},{"location":"aea-framework-documentation/api/protocols/generator/extract_specification/#pythonicprotocolspecification-objects","title":"PythonicProtocolSpecification Objects","text":"
class PythonicProtocolSpecification()\n

This class represents a protocol specification in python.

"},{"location":"aea-framework-documentation/api/protocols/generator/extract_specification/#__init__","title":"__init__","text":"
def __init__() -> None\n

Instantiate a Pythonic protocol specification.

"},{"location":"aea-framework-documentation/api/protocols/generator/extract_specification/#extract","title":"extract","text":"
def extract(\nprotocol_specification: ProtocolSpecification\n) -> PythonicProtocolSpecification\n

Converts a protocol specification into a Pythonic protocol specification.

Arguments:

  • protocol_specification: a protocol specification

Returns:

a Pythonic protocol specification

"},{"location":"aea-framework-documentation/api/protocols/generator/validate/","title":"Validate","text":""},{"location":"aea-framework-documentation/api/protocols/generator/validate/#aeaprotocolsgeneratorvalidate","title":"aea.protocols.generator.validate","text":"

This module validates a protocol specification.

"},{"location":"aea-framework-documentation/api/protocols/generator/validate/#validate","title":"validate","text":"
def validate(\nprotocol_specification: ProtocolSpecification) -> Tuple[bool, str]\n

Evaluate whether a protocol specification is valid.

Arguments:

  • protocol_specification: a protocol specification.

Returns:

Boolean result, and associated message.

"},{"location":"aea-framework-documentation/api/protocols/signing/custom_types/","title":"Custom Types","text":""},{"location":"aea-framework-documentation/api/protocols/signing/custom_types/#packagesfetchaiprotocolssigningcustom_types","title":"packages.fetchai.protocols.signing.custom_types","text":"

This module contains class representations corresponding to every custom type in the protocol specification.

"},{"location":"aea-framework-documentation/api/protocols/signing/custom_types/#errorcode-objects","title":"ErrorCode Objects","text":"
class ErrorCode(Enum)\n

This class represents an instance of ErrorCode.

"},{"location":"aea-framework-documentation/api/protocols/signing/custom_types/#encode","title":"encode","text":"
@staticmethod\ndef encode(error_code_protobuf_object: Any,\nerror_code_object: \"ErrorCode\") -> None\n

Encode an instance of this class into the protocol buffer object.

The protocol buffer object in the error_code_protobuf_object argument is matched with the instance of this class in the 'error_code_object' argument.

Arguments:

  • error_code_protobuf_object: the protocol buffer object whose type corresponds with this class.
  • error_code_object: an instance of this class to be encoded in the protocol buffer object.

"},{"location":"aea-framework-documentation/api/protocols/signing/custom_types/#decode","title":"decode","text":"
@classmethod\ndef decode(cls, error_code_protobuf_object: Any) -> \"ErrorCode\"\n

Decode a protocol buffer object that corresponds with this class into an instance of this class.

A new instance of this class is created that matches the protocol buffer object in the 'error_code_protobuf_object' argument.

Arguments:

  • error_code_protobuf_object: the protocol buffer object whose type corresponds with this class.

Returns:

A new instance of this class that matches the protocol buffer object in the 'error_code_protobuf_object' argument.

"},{"location":"aea-framework-documentation/api/protocols/signing/dialogues/","title":"Dialogues","text":""},{"location":"aea-framework-documentation/api/protocols/signing/dialogues/#packagesfetchaiprotocolssigningdialogues","title":"packages.fetchai.protocols.signing.dialogues","text":"

This module contains the classes required for signing dialogue management.

  • SigningDialogue: The dialogue class maintains state of a dialogue and manages it.
  • SigningDialogues: The dialogues class keeps track of all dialogues.

"},{"location":"aea-framework-documentation/api/protocols/signing/dialogues/#signingdialogue-objects","title":"SigningDialogue Objects","text":"
class SigningDialogue(Dialogue)\n

The signing dialogue class maintains state of a dialogue and manages it.

"},{"location":"aea-framework-documentation/api/protocols/signing/dialogues/#role-objects","title":"Role Objects","text":"
class Role(Dialogue.Role)\n

This class defines the agent's role in a signing dialogue.

"},{"location":"aea-framework-documentation/api/protocols/signing/dialogues/#endstate-objects","title":"EndState Objects","text":"
class EndState(Dialogue.EndState)\n

This class defines the end states of a signing dialogue.

"},{"location":"aea-framework-documentation/api/protocols/signing/dialogues/#__init__","title":"__init__","text":"
def __init__(dialogue_label: DialogueLabel,\nself_address: Address,\nrole: Dialogue.Role,\nmessage_class: Type[SigningMessage] = SigningMessage) -> None\n

Initialize a dialogue.

Arguments:

  • dialogue_label: the identifier of the dialogue
  • self_address: the address of the entity for whom this dialogue is maintained
  • role: the role of the agent this dialogue is maintained for
  • message_class: the message class used

"},{"location":"aea-framework-documentation/api/protocols/signing/dialogues/#signingdialogues-objects","title":"SigningDialogues Objects","text":"
class SigningDialogues(Dialogues, ABC)\n

This class keeps track of all signing dialogues.

"},{"location":"aea-framework-documentation/api/protocols/signing/dialogues/#__init___1","title":"__init__","text":"
def __init__(self_address: Address,\nrole_from_first_message: Callable[[Message, Address],\nDialogue.Role],\ndialogue_class: Type[SigningDialogue] = SigningDialogue) -> None\n

Initialize dialogues.

Arguments:

  • self_address: the address of the entity for whom dialogues are maintained
  • dialogue_class: the dialogue class used
  • role_from_first_message: the callable determining role from first message
"},{"location":"aea-framework-documentation/api/protocols/signing/message/","title":"Message","text":""},{"location":"aea-framework-documentation/api/protocols/signing/message/#packagesfetchaiprotocolssigningmessage","title":"packages.fetchai.protocols.signing.message","text":"

This module contains signing's message definition.

"},{"location":"aea-framework-documentation/api/protocols/signing/message/#signingmessage-objects","title":"SigningMessage Objects","text":"
class SigningMessage(Message)\n

A protocol for communication between skills and decision maker.

"},{"location":"aea-framework-documentation/api/protocols/signing/message/#performative-objects","title":"Performative Objects","text":"
class Performative(Message.Performative)\n

Performatives for the signing protocol.

"},{"location":"aea-framework-documentation/api/protocols/signing/message/#__str__","title":"__str__","text":"
def __str__() -> str\n

Get the string representation.

"},{"location":"aea-framework-documentation/api/protocols/signing/message/#__init__","title":"__init__","text":"
def __init__(performative: Performative,\ndialogue_reference: Tuple[str, str] = (\"\", \"\"),\nmessage_id: int = 1,\ntarget: int = 0,\n**kwargs: Any)\n

Initialise an instance of SigningMessage.

Arguments:

  • message_id: the message id.
  • dialogue_reference: the dialogue reference.
  • target: the message target.
  • performative: the message performative.
  • **kwargs: extra options.

"},{"location":"aea-framework-documentation/api/protocols/signing/message/#valid_performatives","title":"valid_performatives","text":"
@property\ndef valid_performatives() -> Set[str]\n

Get valid performatives.

"},{"location":"aea-framework-documentation/api/protocols/signing/message/#dialogue_reference","title":"dialogue_reference","text":"
@property\ndef dialogue_reference() -> Tuple[str, str]\n

Get the dialogue_reference of the message.

"},{"location":"aea-framework-documentation/api/protocols/signing/message/#message_id","title":"message_id","text":"
@property\ndef message_id() -> int\n

Get the message_id of the message.

"},{"location":"aea-framework-documentation/api/protocols/signing/message/#performative","title":"performative","text":"
@property\ndef performative() -> Performative\n

Get the performative of the message.

"},{"location":"aea-framework-documentation/api/protocols/signing/message/#target","title":"target","text":"
@property\ndef target() -> int\n

Get the target of the message.

"},{"location":"aea-framework-documentation/api/protocols/signing/message/#error_code","title":"error_code","text":"
@property\ndef error_code() -> CustomErrorCode\n

Get the 'error_code' content from the message.

"},{"location":"aea-framework-documentation/api/protocols/signing/message/#raw_message","title":"raw_message","text":"
@property\ndef raw_message() -> CustomRawMessage\n

Get the 'raw_message' content from the message.

"},{"location":"aea-framework-documentation/api/protocols/signing/message/#raw_transaction","title":"raw_transaction","text":"
@property\ndef raw_transaction() -> CustomRawTransaction\n

Get the 'raw_transaction' content from the message.

"},{"location":"aea-framework-documentation/api/protocols/signing/message/#signed_message","title":"signed_message","text":"
@property\ndef signed_message() -> CustomSignedMessage\n

Get the 'signed_message' content from the message.

"},{"location":"aea-framework-documentation/api/protocols/signing/message/#signed_transaction","title":"signed_transaction","text":"
@property\ndef signed_transaction() -> CustomSignedTransaction\n

Get the 'signed_transaction' content from the message.

"},{"location":"aea-framework-documentation/api/protocols/signing/message/#terms","title":"terms","text":"
@property\ndef terms() -> CustomTerms\n

Get the 'terms' content from the message.

"},{"location":"aea-framework-documentation/api/protocols/signing/serialization/","title":"Serialization","text":""},{"location":"aea-framework-documentation/api/protocols/signing/serialization/#packagesfetchaiprotocolssigningserialization","title":"packages.fetchai.protocols.signing.serialization","text":"

Serialization module for signing protocol.

"},{"location":"aea-framework-documentation/api/protocols/signing/serialization/#signingserializer-objects","title":"SigningSerializer Objects","text":"
class SigningSerializer(Serializer)\n

Serialization for the 'signing' protocol.

"},{"location":"aea-framework-documentation/api/protocols/signing/serialization/#encode","title":"encode","text":"
@staticmethod\ndef encode(msg: Message) -> bytes\n

Encode a 'Signing' message into bytes.

Arguments:

  • msg: the message object.

Returns:

the bytes.

"},{"location":"aea-framework-documentation/api/protocols/signing/serialization/#decode","title":"decode","text":"
@staticmethod\ndef decode(obj: bytes) -> Message\n

Decode bytes into a 'Signing' message.

Arguments:

  • obj: the bytes object.

Returns:

the 'Signing' message.

"},{"location":"aea-framework-documentation/api/protocols/state_update/dialogues/","title":"Dialogues","text":""},{"location":"aea-framework-documentation/api/protocols/state_update/dialogues/#packagesfetchaiprotocolsstate_updatedialogues","title":"packages.fetchai.protocols.state_update.dialogues","text":"

This module contains the classes required for state_update dialogue management.

  • StateUpdateDialogue: The dialogue class maintains state of a dialogue and manages it.
  • StateUpdateDialogues: The dialogues class keeps track of all dialogues.

"},{"location":"aea-framework-documentation/api/protocols/state_update/dialogues/#stateupdatedialogue-objects","title":"StateUpdateDialogue Objects","text":"
class StateUpdateDialogue(Dialogue)\n

The state_update dialogue class maintains state of a dialogue and manages it.

"},{"location":"aea-framework-documentation/api/protocols/state_update/dialogues/#role-objects","title":"Role Objects","text":"
class Role(Dialogue.Role)\n

This class defines the agent's role in a state_update dialogue.

"},{"location":"aea-framework-documentation/api/protocols/state_update/dialogues/#endstate-objects","title":"EndState Objects","text":"
class EndState(Dialogue.EndState)\n

This class defines the end states of a state_update dialogue.

"},{"location":"aea-framework-documentation/api/protocols/state_update/dialogues/#__init__","title":"__init__","text":"
def __init__(\ndialogue_label: DialogueLabel,\nself_address: Address,\nrole: Dialogue.Role,\nmessage_class: Type[StateUpdateMessage] = StateUpdateMessage) -> None\n

Initialize a dialogue.

Arguments:

  • dialogue_label: the identifier of the dialogue
  • self_address: the address of the entity for whom this dialogue is maintained
  • role: the role of the agent this dialogue is maintained for
  • message_class: the message class used

"},{"location":"aea-framework-documentation/api/protocols/state_update/dialogues/#stateupdatedialogues-objects","title":"StateUpdateDialogues Objects","text":"
class StateUpdateDialogues(Dialogues, ABC)\n

This class keeps track of all state_update dialogues.

"},{"location":"aea-framework-documentation/api/protocols/state_update/dialogues/#__init___1","title":"__init__","text":"
def __init__(\nself_address: Address,\nrole_from_first_message: Callable[[Message, Address], Dialogue.Role],\ndialogue_class: Type[StateUpdateDialogue] = StateUpdateDialogue\n) -> None\n

Initialize dialogues.

Arguments:

  • self_address: the address of the entity for whom dialogues are maintained
  • dialogue_class: the dialogue class used
  • role_from_first_message: the callable determining role from first message
"},{"location":"aea-framework-documentation/api/protocols/state_update/message/","title":"Message","text":""},{"location":"aea-framework-documentation/api/protocols/state_update/message/#packagesfetchaiprotocolsstate_updatemessage","title":"packages.fetchai.protocols.state_update.message","text":"

This module contains state_update's message definition.

"},{"location":"aea-framework-documentation/api/protocols/state_update/message/#stateupdatemessage-objects","title":"StateUpdateMessage Objects","text":"
class StateUpdateMessage(Message)\n

A protocol for state updates to the decision maker state.

"},{"location":"aea-framework-documentation/api/protocols/state_update/message/#performative-objects","title":"Performative Objects","text":"
class Performative(Message.Performative)\n

Performatives for the state_update protocol.

"},{"location":"aea-framework-documentation/api/protocols/state_update/message/#__str__","title":"__str__","text":"
def __str__() -> str\n

Get the string representation.

"},{"location":"aea-framework-documentation/api/protocols/state_update/message/#__init__","title":"__init__","text":"
def __init__(performative: Performative,\ndialogue_reference: Tuple[str, str] = (\"\", \"\"),\nmessage_id: int = 1,\ntarget: int = 0,\n**kwargs: Any)\n

Initialise an instance of StateUpdateMessage.

Arguments:

  • message_id: the message id.
  • dialogue_reference: the dialogue reference.
  • target: the message target.
  • performative: the message performative.
  • **kwargs: extra options.

"},{"location":"aea-framework-documentation/api/protocols/state_update/message/#valid_performatives","title":"valid_performatives","text":"
@property\ndef valid_performatives() -> Set[str]\n

Get valid performatives.

"},{"location":"aea-framework-documentation/api/protocols/state_update/message/#dialogue_reference","title":"dialogue_reference","text":"
@property\ndef dialogue_reference() -> Tuple[str, str]\n

Get the dialogue_reference of the message.

"},{"location":"aea-framework-documentation/api/protocols/state_update/message/#message_id","title":"message_id","text":"
@property\ndef message_id() -> int\n

Get the message_id of the message.

"},{"location":"aea-framework-documentation/api/protocols/state_update/message/#performative","title":"performative","text":"
@property\ndef performative() -> Performative\n

Get the performative of the message.

"},{"location":"aea-framework-documentation/api/protocols/state_update/message/#target","title":"target","text":"
@property\ndef target() -> int\n

Get the target of the message.

"},{"location":"aea-framework-documentation/api/protocols/state_update/message/#amount_by_currency_id","title":"amount_by_currency_id","text":"
@property\ndef amount_by_currency_id() -> Dict[str, int]\n

Get the 'amount_by_currency_id' content from the message.

"},{"location":"aea-framework-documentation/api/protocols/state_update/message/#exchange_params_by_currency_id","title":"exchange_params_by_currency_id","text":"
@property\ndef exchange_params_by_currency_id() -> Dict[str, float]\n

Get the 'exchange_params_by_currency_id' content from the message.

"},{"location":"aea-framework-documentation/api/protocols/state_update/message/#quantities_by_good_id","title":"quantities_by_good_id","text":"
@property\ndef quantities_by_good_id() -> Dict[str, int]\n

Get the 'quantities_by_good_id' content from the message.

"},{"location":"aea-framework-documentation/api/protocols/state_update/message/#utility_params_by_good_id","title":"utility_params_by_good_id","text":"
@property\ndef utility_params_by_good_id() -> Dict[str, float]\n

Get the 'utility_params_by_good_id' content from the message.

"},{"location":"aea-framework-documentation/api/protocols/state_update/serialization/","title":"Serialization","text":""},{"location":"aea-framework-documentation/api/protocols/state_update/serialization/#packagesfetchaiprotocolsstate_updateserialization","title":"packages.fetchai.protocols.state_update.serialization","text":"

Serialization module for state_update protocol.

"},{"location":"aea-framework-documentation/api/protocols/state_update/serialization/#stateupdateserializer-objects","title":"StateUpdateSerializer Objects","text":"
class StateUpdateSerializer(Serializer)\n

Serialization for the 'state_update' protocol.

"},{"location":"aea-framework-documentation/api/protocols/state_update/serialization/#encode","title":"encode","text":"
@staticmethod\ndef encode(msg: Message) -> bytes\n

Encode a 'StateUpdate' message into bytes.

Arguments:

  • msg: the message object.

Returns:

the bytes.

"},{"location":"aea-framework-documentation/api/protocols/state_update/serialization/#decode","title":"decode","text":"
@staticmethod\ndef decode(obj: bytes) -> Message\n

Decode bytes into a 'StateUpdate' message.

Arguments:

  • obj: the bytes object.

Returns:

the 'StateUpdate' message.

"},{"location":"aea-framework-documentation/api/registries/base/","title":"Base","text":""},{"location":"aea-framework-documentation/api/registries/base/#aearegistriesbase","title":"aea.registries.base","text":"

This module contains registries.

"},{"location":"aea-framework-documentation/api/registries/base/#registry-objects","title":"Registry Objects","text":"
class Registry(Generic[ItemId, Item], WithLogger, ABC)\n

This class implements an abstract registry.

"},{"location":"aea-framework-documentation/api/registries/base/#__init__","title":"__init__","text":"
def __init__(agent_name: str = \"standalone\") -> None\n

Initialize the registry.

Arguments:

  • agent_name: the name of the agent

"},{"location":"aea-framework-documentation/api/registries/base/#register","title":"register","text":"
@abstractmethod\ndef register(item_id: ItemId,\nitem: Item,\nis_dynamically_added: bool = False) -> None\n

Register an item.

Arguments:

  • item_id: the public id of the item.
  • item: the item.
  • is_dynamically_added: whether or not the item is dynamically added.

Raises:

  • None: ValueError if an item is already registered with that item id.

Returns:

None

"},{"location":"aea-framework-documentation/api/registries/base/#unregister","title":"unregister","text":"
@abstractmethod\ndef unregister(item_id: ItemId) -> Optional[Item]\n

Unregister an item.

Arguments:

  • item_id: the public id of the item.

Raises:

  • None: ValueError if no item registered with that item id.

Returns:

the item

"},{"location":"aea-framework-documentation/api/registries/base/#fetch","title":"fetch","text":"
@abstractmethod\ndef fetch(item_id: ItemId) -> Optional[Item]\n

Fetch an item.

Arguments:

  • item_id: the public id of the item.

Returns:

the Item

"},{"location":"aea-framework-documentation/api/registries/base/#fetch_all","title":"fetch_all","text":"
@abstractmethod\ndef fetch_all() -> List[Item]\n

Fetch all the items.

Returns:

the list of items.

"},{"location":"aea-framework-documentation/api/registries/base/#ids","title":"ids","text":"
@abstractmethod\ndef ids() -> Set[ItemId]\n

Return the set of all the used item ids.

Returns:

the set of item ids.

"},{"location":"aea-framework-documentation/api/registries/base/#setup","title":"setup","text":"
@abstractmethod\ndef setup() -> None\n

Set up registry.

Returns:

None

"},{"location":"aea-framework-documentation/api/registries/base/#teardown","title":"teardown","text":"
@abstractmethod\ndef teardown() -> None\n

Teardown the registry.

Returns:

None

"},{"location":"aea-framework-documentation/api/registries/base/#publicidregistry-objects","title":"PublicIdRegistry Objects","text":"
class PublicIdRegistry(Generic[Item], Registry[PublicId, Item])\n

This class implement a registry whose keys are public ids.

In particular, it is able to handle the case when the public id points to the 'latest' version of a package.

"},{"location":"aea-framework-documentation/api/registries/base/#__init___1","title":"__init__","text":"
def __init__() -> None\n

Initialize the registry.

"},{"location":"aea-framework-documentation/api/registries/base/#register_1","title":"register","text":"
def register(public_id: PublicId,\nitem: Item,\nis_dynamically_added: bool = False) -> None\n

Register an item.

"},{"location":"aea-framework-documentation/api/registries/base/#unregister_1","title":"unregister","text":"
def unregister(public_id: PublicId) -> Item\n

Unregister an item.

"},{"location":"aea-framework-documentation/api/registries/base/#fetch_1","title":"fetch","text":"
def fetch(public_id: PublicId) -> Optional[Item]\n

Fetch an item associated with a public id.

Arguments:

  • public_id: the public id.

Returns:

an item, or None if the key is not present.

"},{"location":"aea-framework-documentation/api/registries/base/#fetch_all_1","title":"fetch_all","text":"
def fetch_all() -> List[Item]\n

Fetch all the items.

"},{"location":"aea-framework-documentation/api/registries/base/#ids_1","title":"ids","text":"
def ids() -> Set[PublicId]\n

Get all the item ids.

"},{"location":"aea-framework-documentation/api/registries/base/#setup_1","title":"setup","text":"
def setup() -> None\n

Set up the items.

"},{"location":"aea-framework-documentation/api/registries/base/#teardown_1","title":"teardown","text":"
def teardown() -> None\n

Tear down the items.

"},{"location":"aea-framework-documentation/api/registries/base/#agentcomponentregistry-objects","title":"AgentComponentRegistry Objects","text":"
class AgentComponentRegistry(Registry[ComponentId, Component])\n

This class implements a simple dictionary-based registry for agent components.

"},{"location":"aea-framework-documentation/api/registries/base/#__init___2","title":"__init__","text":"
def __init__(**kwargs: Any) -> None\n

Instantiate the registry.

Arguments:

  • kwargs: kwargs

"},{"location":"aea-framework-documentation/api/registries/base/#register_2","title":"register","text":"
def register(component_id: ComponentId,\ncomponent: Component,\nis_dynamically_added: bool = False) -> None\n

Register a component.

Arguments:

  • component_id: the id of the component.
  • component: the component object.
  • is_dynamically_added: whether or not the item is dynamically added.

"},{"location":"aea-framework-documentation/api/registries/base/#unregister_2","title":"unregister","text":"
def unregister(component_id: ComponentId) -> Optional[Component]\n

Unregister a component.

Arguments:

  • component_id: the ComponentId

Returns:

the item

"},{"location":"aea-framework-documentation/api/registries/base/#fetch_2","title":"fetch","text":"
def fetch(component_id: ComponentId) -> Optional[Component]\n

Fetch the component by id.

Arguments:

  • component_id: the contract id

Returns:

the component or None if the component is not registered

"},{"location":"aea-framework-documentation/api/registries/base/#fetch_all_2","title":"fetch_all","text":"
def fetch_all() -> List[Component]\n

Fetch all the components.

Returns:

the list of registered components.

"},{"location":"aea-framework-documentation/api/registries/base/#fetch_by_type","title":"fetch_by_type","text":"
def fetch_by_type(component_type: ComponentType) -> List[Component]\n

Fetch all the components by a given type..

Arguments:

  • component_type: a component type

Returns:

the list of registered components of a given type.

"},{"location":"aea-framework-documentation/api/registries/base/#ids_2","title":"ids","text":"
def ids() -> Set[ComponentId]\n

Get the item ids.

"},{"location":"aea-framework-documentation/api/registries/base/#setup_2","title":"setup","text":"
def setup() -> None\n

Set up the registry.

"},{"location":"aea-framework-documentation/api/registries/base/#teardown_2","title":"teardown","text":"
def teardown() -> None\n

Teardown the registry.

"},{"location":"aea-framework-documentation/api/registries/base/#componentregistry-objects","title":"ComponentRegistry Objects","text":"
class ComponentRegistry(Registry[Tuple[PublicId, str], SkillComponentType],\nGeneric[SkillComponentType])\n

This class implements a generic registry for skill components.

"},{"location":"aea-framework-documentation/api/registries/base/#__init___3","title":"__init__","text":"
def __init__(**kwargs: Any) -> None\n

Instantiate the registry.

Arguments:

  • kwargs: kwargs

"},{"location":"aea-framework-documentation/api/registries/base/#register_3","title":"register","text":"
def register(item_id: Tuple[PublicId, str],\nitem: SkillComponentType,\nis_dynamically_added: bool = False) -> None\n

Register a item.

Arguments:

  • item_id: a pair (skill id, item name).
  • item: the item to register.
  • is_dynamically_added: whether or not the item is dynamically added.

Raises:

  • None: ValueError if an item is already registered with that item id.

"},{"location":"aea-framework-documentation/api/registries/base/#unregister_3","title":"unregister","text":"
def unregister(item_id: Tuple[PublicId, str]) -> Optional[SkillComponentType]\n

Unregister a item.

Arguments:

  • item_id: a pair (skill id, item name).

Raises:

  • None: ValueError if no item registered with that item id.

Returns:

skill component

"},{"location":"aea-framework-documentation/api/registries/base/#fetch_3","title":"fetch","text":"
def fetch(item_id: Tuple[PublicId, str]) -> Optional[SkillComponentType]\n

Fetch an item.

Arguments:

  • item_id: the public id of the item.

Returns:

the Item

"},{"location":"aea-framework-documentation/api/registries/base/#fetch_by_skill","title":"fetch_by_skill","text":"
def fetch_by_skill(skill_id: PublicId) -> List[SkillComponentType]\n

Fetch all the items of a given skill.

"},{"location":"aea-framework-documentation/api/registries/base/#fetch_all_3","title":"fetch_all","text":"
def fetch_all() -> List[SkillComponentType]\n

Fetch all the items.

"},{"location":"aea-framework-documentation/api/registries/base/#unregister_by_skill","title":"unregister_by_skill","text":"
def unregister_by_skill(skill_id: PublicId) -> None\n

Unregister all the components by skill.

"},{"location":"aea-framework-documentation/api/registries/base/#ids_3","title":"ids","text":"
def ids() -> Set[Tuple[PublicId, str]]\n

Get the item ids.

"},{"location":"aea-framework-documentation/api/registries/base/#setup_3","title":"setup","text":"
def setup() -> None\n

Set up the items in the registry.

"},{"location":"aea-framework-documentation/api/registries/base/#teardown_3","title":"teardown","text":"
def teardown() -> None\n

Teardown the registry.

"},{"location":"aea-framework-documentation/api/registries/base/#handlerregistry-objects","title":"HandlerRegistry Objects","text":"
class HandlerRegistry(ComponentRegistry[Handler])\n

This class implements the handlers registry.

"},{"location":"aea-framework-documentation/api/registries/base/#__init___4","title":"__init__","text":"
def __init__(**kwargs: Any) -> None\n

Instantiate the registry.

Arguments:

  • kwargs: kwargs

"},{"location":"aea-framework-documentation/api/registries/base/#register_4","title":"register","text":"
def register(item_id: Tuple[PublicId, str],\nitem: Handler,\nis_dynamically_added: bool = False) -> None\n

Register a handler.

Arguments:

  • item_id: the item id.
  • item: the handler.
  • is_dynamically_added: whether or not the item is dynamically added.

Raises:

  • ValueError: if the protocol is None, or an item with pair (skill_id, protocol_id_ already exists.

"},{"location":"aea-framework-documentation/api/registries/base/#unregister_4","title":"unregister","text":"
def unregister(item_id: Tuple[PublicId, str]) -> Handler\n

Unregister a item.

Arguments:

  • item_id: a pair (skill id, item name).

Raises:

  • None: ValueError if no item is registered with that item id.

Returns:

the unregistered handler

"},{"location":"aea-framework-documentation/api/registries/base/#unregister_by_skill_1","title":"unregister_by_skill","text":"
def unregister_by_skill(skill_id: PublicId) -> None\n

Unregister all the components by skill.

"},{"location":"aea-framework-documentation/api/registries/base/#fetch_by_protocol","title":"fetch_by_protocol","text":"
def fetch_by_protocol(protocol_id: PublicId) -> List[Handler]\n

Fetch the handler by the pair protocol id and skill id.

Arguments:

  • protocol_id: the protocol id

Returns:

the handlers registered for the protocol_id and skill_id

"},{"location":"aea-framework-documentation/api/registries/base/#fetch_by_protocol_and_skill","title":"fetch_by_protocol_and_skill","text":"
def fetch_by_protocol_and_skill(protocol_id: PublicId,\nskill_id: PublicId) -> Optional[Handler]\n

Fetch the handler by the pair protocol id and skill id.

Arguments:

  • protocol_id: the protocol id
  • skill_id: the skill id.

Returns:

the handlers registered for the protocol_id and skill_id

"},{"location":"aea-framework-documentation/api/registries/filter/","title":"Filter","text":""},{"location":"aea-framework-documentation/api/registries/filter/#aearegistriesfilter","title":"aea.registries.filter","text":"

This module contains registries.

"},{"location":"aea-framework-documentation/api/registries/filter/#filter-objects","title":"Filter Objects","text":"
class Filter(WithLogger)\n

This class implements the filter of an AEA.

"},{"location":"aea-framework-documentation/api/registries/filter/#__init__","title":"__init__","text":"
def __init__(resources: Resources,\ndecision_maker_out_queue: AsyncFriendlyQueue) -> None\n

Instantiate the filter.

Arguments:

  • resources: the resources
  • decision_maker_out_queue: the decision maker queue

"},{"location":"aea-framework-documentation/api/registries/filter/#resources","title":"resources","text":"
@property\ndef resources() -> Resources\n

Get resources.

"},{"location":"aea-framework-documentation/api/registries/filter/#decision_maker_out_queue","title":"decision_maker_out_queue","text":"
@property\ndef decision_maker_out_queue() -> AsyncFriendlyQueue\n

Get decision maker (out) queue.

"},{"location":"aea-framework-documentation/api/registries/filter/#get_active_handlers","title":"get_active_handlers","text":"
def get_active_handlers(protocol_id: PublicId,\nskill_id: Optional[PublicId] = None) -> List[Handler]\n

Get active handlers based on protocol id and optional skill id.

Arguments:

  • protocol_id: the protocol id
  • skill_id: the skill id

Returns:

the list of handlers currently active

"},{"location":"aea-framework-documentation/api/registries/filter/#get_active_behaviours","title":"get_active_behaviours","text":"
def get_active_behaviours() -> List[Behaviour]\n

Get the active behaviours.

Returns:

the list of behaviours currently active

"},{"location":"aea-framework-documentation/api/registries/filter/#handle_new_handlers_and_behaviours","title":"handle_new_handlers_and_behaviours","text":"
def handle_new_handlers_and_behaviours() -> None\n

Handle the messages from the decision maker.

"},{"location":"aea-framework-documentation/api/registries/filter/#get_internal_message","title":"get_internal_message","text":"
async def get_internal_message() -> Optional[Message]\n

Get a message from decision_maker_out_queue.

"},{"location":"aea-framework-documentation/api/registries/filter/#handle_internal_message","title":"handle_internal_message","text":"
def handle_internal_message(internal_message: Optional[Message]) -> None\n

Handle internal message.

"},{"location":"aea-framework-documentation/api/registries/resources/","title":"Resources","text":""},{"location":"aea-framework-documentation/api/registries/resources/#aearegistriesresources","title":"aea.registries.resources","text":"

This module contains the resources class.

"},{"location":"aea-framework-documentation/api/registries/resources/#resources-objects","title":"Resources Objects","text":"
class Resources()\n

This class implements the object that holds the resources of an AEA.

"},{"location":"aea-framework-documentation/api/registries/resources/#__init__","title":"__init__","text":"
def __init__(agent_name: str = \"standalone\") -> None\n

Instantiate the resources.

Arguments:

  • agent_name: the name of the agent

"},{"location":"aea-framework-documentation/api/registries/resources/#agent_name","title":"agent_name","text":"
@property\ndef agent_name() -> str\n

Get the agent name.

"},{"location":"aea-framework-documentation/api/registries/resources/#component_registry","title":"component_registry","text":"
@property\ndef component_registry() -> AgentComponentRegistry\n

Get the agent component registry.

"},{"location":"aea-framework-documentation/api/registries/resources/#behaviour_registry","title":"behaviour_registry","text":"
@property\ndef behaviour_registry() -> ComponentRegistry[Behaviour]\n

Get the behaviour registry.

"},{"location":"aea-framework-documentation/api/registries/resources/#handler_registry","title":"handler_registry","text":"
@property\ndef handler_registry() -> HandlerRegistry\n

Get the handler registry.

"},{"location":"aea-framework-documentation/api/registries/resources/#model_registry","title":"model_registry","text":"
@property\ndef model_registry() -> ComponentRegistry[Model]\n

Get the model registry.

"},{"location":"aea-framework-documentation/api/registries/resources/#add_component","title":"add_component","text":"
def add_component(component: Component) -> None\n

Add a component to resources.

"},{"location":"aea-framework-documentation/api/registries/resources/#add_protocol","title":"add_protocol","text":"
def add_protocol(protocol: Protocol) -> None\n

Add a protocol to the set of resources.

Arguments:

  • protocol: a protocol

"},{"location":"aea-framework-documentation/api/registries/resources/#get_protocol","title":"get_protocol","text":"
def get_protocol(protocol_id: PublicId) -> Optional[Protocol]\n

Get protocol for given protocol id.

Arguments:

  • protocol_id: the protocol id

Returns:

a matching protocol, if present, else None

"},{"location":"aea-framework-documentation/api/registries/resources/#get_protocol_by_specification_id","title":"get_protocol_by_specification_id","text":"
def get_protocol_by_specification_id(\nprotocol_specification_id: PublicId) -> Optional[Protocol]\n

Get protocol for given protocol_specification_id.

Arguments:

  • protocol_specification_id: the protocol id

Returns:

a matching protocol, if present, else None

"},{"location":"aea-framework-documentation/api/registries/resources/#get_all_protocols","title":"get_all_protocols","text":"
def get_all_protocols() -> List[Protocol]\n

Get the list of all the protocols.

Returns:

the list of protocols.

"},{"location":"aea-framework-documentation/api/registries/resources/#remove_protocol","title":"remove_protocol","text":"
def remove_protocol(protocol_id: PublicId) -> None\n

Remove a protocol from the set of resources.

Arguments:

  • protocol_id: the protocol id for the protocol to be removed.

"},{"location":"aea-framework-documentation/api/registries/resources/#add_contract","title":"add_contract","text":"
def add_contract(contract: Contract) -> None\n

Add a contract to the set of resources.

Arguments:

  • contract: a contract

"},{"location":"aea-framework-documentation/api/registries/resources/#get_contract","title":"get_contract","text":"
def get_contract(contract_id: PublicId) -> Optional[Contract]\n

Get contract for given contract id.

Arguments:

  • contract_id: the contract id

Returns:

a matching contract, if present, else None

"},{"location":"aea-framework-documentation/api/registries/resources/#get_all_contracts","title":"get_all_contracts","text":"
def get_all_contracts() -> List[Contract]\n

Get the list of all the contracts.

Returns:

the list of contracts.

"},{"location":"aea-framework-documentation/api/registries/resources/#remove_contract","title":"remove_contract","text":"
def remove_contract(contract_id: PublicId) -> None\n

Remove a contract from the set of resources.

Arguments:

  • contract_id: the contract id for the contract to be removed.

"},{"location":"aea-framework-documentation/api/registries/resources/#add_connection","title":"add_connection","text":"
def add_connection(connection: Connection) -> None\n

Add a connection to the set of resources.

Arguments:

  • connection: a connection

"},{"location":"aea-framework-documentation/api/registries/resources/#get_connection","title":"get_connection","text":"
def get_connection(connection_id: PublicId) -> Optional[Connection]\n

Get connection for given connection id.

Arguments:

  • connection_id: the connection id

Returns:

a matching connection, if present, else None

"},{"location":"aea-framework-documentation/api/registries/resources/#get_all_connections","title":"get_all_connections","text":"
def get_all_connections() -> List[Connection]\n

Get the list of all the connections.

Returns:

the list of connections.

"},{"location":"aea-framework-documentation/api/registries/resources/#remove_connection","title":"remove_connection","text":"
def remove_connection(connection_id: PublicId) -> None\n

Remove a connection from the set of resources.

Arguments:

  • connection_id: the connection id for the connection to be removed.

"},{"location":"aea-framework-documentation/api/registries/resources/#add_skill","title":"add_skill","text":"
def add_skill(skill: Skill) -> None\n

Add a skill to the set of resources.

Arguments:

  • skill: a skill

"},{"location":"aea-framework-documentation/api/registries/resources/#get_skill","title":"get_skill","text":"
def get_skill(skill_id: PublicId) -> Optional[Skill]\n

Get the skill for a given skill id.

Arguments:

  • skill_id: the skill id

Returns:

a matching skill, if present, else None

"},{"location":"aea-framework-documentation/api/registries/resources/#get_all_skills","title":"get_all_skills","text":"
def get_all_skills() -> List[Skill]\n

Get the list of all the skills.

Returns:

the list of skills.

"},{"location":"aea-framework-documentation/api/registries/resources/#remove_skill","title":"remove_skill","text":"
def remove_skill(skill_id: PublicId) -> None\n

Remove a skill from the set of resources.

Arguments:

  • skill_id: the skill id for the skill to be removed.

"},{"location":"aea-framework-documentation/api/registries/resources/#get_handler","title":"get_handler","text":"
def get_handler(protocol_id: PublicId,\nskill_id: PublicId) -> Optional[Handler]\n

Get a specific handler.

Arguments:

  • protocol_id: the protocol id the handler is handling
  • skill_id: the skill id of the handler's skill

Returns:

the handler

"},{"location":"aea-framework-documentation/api/registries/resources/#get_handlers","title":"get_handlers","text":"
def get_handlers(protocol_id: PublicId) -> List[Handler]\n

Get all handlers for a given protocol.

Arguments:

  • protocol_id: the protocol id the handler is handling

Returns:

the list of handlers matching the protocol

"},{"location":"aea-framework-documentation/api/registries/resources/#get_all_handlers","title":"get_all_handlers","text":"
def get_all_handlers() -> List[Handler]\n

Get all handlers from all skills.

Returns:

the list of handlers

"},{"location":"aea-framework-documentation/api/registries/resources/#get_behaviour","title":"get_behaviour","text":"
def get_behaviour(skill_id: PublicId,\nbehaviour_name: str) -> Optional[Behaviour]\n

Get a specific behaviours for a given skill.

Arguments:

  • skill_id: the skill id
  • behaviour_name: the behaviour name

Returns:

the behaviour, if it is present, else None

"},{"location":"aea-framework-documentation/api/registries/resources/#get_behaviours","title":"get_behaviours","text":"
def get_behaviours(skill_id: PublicId) -> List[Behaviour]\n

Get all behaviours for a given skill.

Arguments:

  • skill_id: the skill id

Returns:

the list of behaviours of the skill

"},{"location":"aea-framework-documentation/api/registries/resources/#get_all_behaviours","title":"get_all_behaviours","text":"
def get_all_behaviours() -> List[Behaviour]\n

Get all behaviours from all skills.

Returns:

the list of all behaviours

"},{"location":"aea-framework-documentation/api/registries/resources/#setup","title":"setup","text":"
def setup() -> None\n

Set up the resources.

Calls setup on all resources.

"},{"location":"aea-framework-documentation/api/registries/resources/#teardown","title":"teardown","text":"
def teardown() -> None\n

Teardown the resources.

Calls teardown on all resources.

"},{"location":"aea-framework-documentation/api/skills/base/","title":"Base","text":""},{"location":"aea-framework-documentation/api/skills/base/#aeaskillsbase","title":"aea.skills.base","text":"

This module contains the base classes for the skills.

"},{"location":"aea-framework-documentation/api/skills/base/#skillcontext-objects","title":"SkillContext Objects","text":"
class SkillContext()\n

This class implements the context of a skill.

"},{"location":"aea-framework-documentation/api/skills/base/#__init__","title":"__init__","text":"
def __init__(agent_context: Optional[AgentContext] = None,\nskill: Optional[\"Skill\"] = None) -> None\n

Initialize a skill context.

Arguments:

  • agent_context: the agent context.
  • skill: the skill.

"},{"location":"aea-framework-documentation/api/skills/base/#logger","title":"logger","text":"
@property\ndef logger() -> Logger\n

Get the logger.

"},{"location":"aea-framework-documentation/api/skills/base/#logger_1","title":"logger","text":"
@logger.setter\ndef logger(logger_: Logger) -> None\n

Set the logger.

"},{"location":"aea-framework-documentation/api/skills/base/#set_agent_context","title":"set_agent_context","text":"
def set_agent_context(agent_context: AgentContext) -> None\n

Set the agent context.

"},{"location":"aea-framework-documentation/api/skills/base/#shared_state","title":"shared_state","text":"
@property\ndef shared_state() -> Dict[str, Any]\n

Get the shared state dictionary.

"},{"location":"aea-framework-documentation/api/skills/base/#agent_name","title":"agent_name","text":"
@property\ndef agent_name() -> str\n

Get agent name.

"},{"location":"aea-framework-documentation/api/skills/base/#skill_id","title":"skill_id","text":"
@property\ndef skill_id() -> PublicId\n

Get the skill id of the skill context.

"},{"location":"aea-framework-documentation/api/skills/base/#is_active","title":"is_active","text":"
@property\ndef is_active() -> bool\n

Get the status of the skill (active/not active).

"},{"location":"aea-framework-documentation/api/skills/base/#is_active_1","title":"is_active","text":"
@is_active.setter\ndef is_active(value: bool) -> None\n

Set the status of the skill (active/not active).

"},{"location":"aea-framework-documentation/api/skills/base/#new_behaviours","title":"new_behaviours","text":"
@property\ndef new_behaviours() -> \"Queue[Behaviour]\"\n

Queue for the new behaviours.

This queue can be used to send messages to the framework to request the registration of a behaviour.

Returns:

the queue of new behaviours.

"},{"location":"aea-framework-documentation/api/skills/base/#new_handlers","title":"new_handlers","text":"
@property\ndef new_handlers() -> \"Queue[Handler]\"\n

Queue for the new handlers.

This queue can be used to send messages to the framework to request the registration of a handler.

Returns:

the queue of new handlers.

"},{"location":"aea-framework-documentation/api/skills/base/#agent_addresses","title":"agent_addresses","text":"
@property\ndef agent_addresses() -> Dict[str, str]\n

Get addresses.

"},{"location":"aea-framework-documentation/api/skills/base/#agent_address","title":"agent_address","text":"
@property\ndef agent_address() -> str\n

Get address.

"},{"location":"aea-framework-documentation/api/skills/base/#public_key","title":"public_key","text":"
@property\ndef public_key() -> str\n

Get public key.

"},{"location":"aea-framework-documentation/api/skills/base/#public_keys","title":"public_keys","text":"
@property\ndef public_keys() -> Dict[str, str]\n

Get public keys.

"},{"location":"aea-framework-documentation/api/skills/base/#connection_status","title":"connection_status","text":"
@property\ndef connection_status() -> MultiplexerStatus\n

Get connection status.

"},{"location":"aea-framework-documentation/api/skills/base/#outbox","title":"outbox","text":"
@property\ndef outbox() -> OutBox\n

Get outbox.

"},{"location":"aea-framework-documentation/api/skills/base/#storage","title":"storage","text":"
@property\ndef storage() -> Optional[Storage]\n

Get optional storage for agent.

"},{"location":"aea-framework-documentation/api/skills/base/#message_in_queue","title":"message_in_queue","text":"
@property\ndef message_in_queue() -> Queue\n

Get message in queue.

"},{"location":"aea-framework-documentation/api/skills/base/#decision_maker_message_queue","title":"decision_maker_message_queue","text":"
@property\ndef decision_maker_message_queue() -> Queue\n

Get message queue of decision maker.

"},{"location":"aea-framework-documentation/api/skills/base/#decision_maker_handler_context","title":"decision_maker_handler_context","text":"
@property\ndef decision_maker_handler_context() -> SimpleNamespace\n

Get decision maker handler context.

"},{"location":"aea-framework-documentation/api/skills/base/#task_manager","title":"task_manager","text":"
@property\ndef task_manager() -> TaskManager\n

Get behaviours of the skill.

"},{"location":"aea-framework-documentation/api/skills/base/#default_ledger_id","title":"default_ledger_id","text":"
@property\ndef default_ledger_id() -> str\n

Get the default ledger id.

"},{"location":"aea-framework-documentation/api/skills/base/#currency_denominations","title":"currency_denominations","text":"
@property\ndef currency_denominations() -> Dict[str, str]\n

Get a dictionary mapping ledger ids to currency denominations.

"},{"location":"aea-framework-documentation/api/skills/base/#search_service_address","title":"search_service_address","text":"
@property\ndef search_service_address() -> Address\n

Get the address of the search service.

"},{"location":"aea-framework-documentation/api/skills/base/#decision_maker_address","title":"decision_maker_address","text":"
@property\ndef decision_maker_address() -> Address\n

Get the address of the decision maker.

"},{"location":"aea-framework-documentation/api/skills/base/#handlers","title":"handlers","text":"
@property\ndef handlers() -> SimpleNamespace\n

Get handlers of the skill.

"},{"location":"aea-framework-documentation/api/skills/base/#behaviours","title":"behaviours","text":"
@property\ndef behaviours() -> SimpleNamespace\n

Get behaviours of the skill.

"},{"location":"aea-framework-documentation/api/skills/base/#namespace","title":"namespace","text":"
@property\ndef namespace() -> SimpleNamespace\n

Get the agent context namespace.

"},{"location":"aea-framework-documentation/api/skills/base/#__getattr__","title":"__getattr__","text":"
def __getattr__(item: Any) -> Any\n

Get attribute.

"},{"location":"aea-framework-documentation/api/skills/base/#send_to_skill","title":"send_to_skill","text":"
def send_to_skill(message_or_envelope: Union[Message, Envelope],\ncontext: Optional[EnvelopeContext] = None) -> None\n

Send message or envelope to another skill.

If message passed it will be wrapped into envelope with optional envelope context.

Arguments:

  • message_or_envelope: envelope to send to another skill.
  • context: the optional envelope context

"},{"location":"aea-framework-documentation/api/skills/base/#skillcomponent-objects","title":"SkillComponent Objects","text":"
class SkillComponent(ABC)\n

This class defines an abstract interface for skill component classes.

"},{"location":"aea-framework-documentation/api/skills/base/#__init___1","title":"__init__","text":"
def __init__(name: str,\nskill_context: SkillContext,\nconfiguration: Optional[SkillComponentConfiguration] = None,\n**kwargs: Any) -> None\n

Initialize a skill component.

Arguments:

  • name: the name of the component.
  • configuration: the configuration for the component.
  • skill_context: the skill context.
  • kwargs: the keyword arguments.

"},{"location":"aea-framework-documentation/api/skills/base/#name","title":"name","text":"
@property\ndef name() -> str\n

Get the name of the skill component.

"},{"location":"aea-framework-documentation/api/skills/base/#context","title":"context","text":"
@property\ndef context() -> SkillContext\n

Get the context of the skill component.

"},{"location":"aea-framework-documentation/api/skills/base/#skill_id_1","title":"skill_id","text":"
@property\ndef skill_id() -> PublicId\n

Get the skill id of the skill component.

"},{"location":"aea-framework-documentation/api/skills/base/#configuration","title":"configuration","text":"
@property\ndef configuration() -> SkillComponentConfiguration\n

Get the skill component configuration.

"},{"location":"aea-framework-documentation/api/skills/base/#config","title":"config","text":"
@property\ndef config() -> Dict[Any, Any]\n

Get the config of the skill component.

"},{"location":"aea-framework-documentation/api/skills/base/#setup","title":"setup","text":"
@abstractmethod\ndef setup() -> None\n

Implement the setup.

"},{"location":"aea-framework-documentation/api/skills/base/#teardown","title":"teardown","text":"
@abstractmethod\ndef teardown() -> None\n

Implement the teardown.

"},{"location":"aea-framework-documentation/api/skills/base/#parse_module","title":"parse_module","text":"
@classmethod\n@abstractmethod\ndef parse_module(cls, path: str, configs: Dict[str,\nSkillComponentConfiguration],\nskill_context: SkillContext) -> dict\n

Parse the component module.

"},{"location":"aea-framework-documentation/api/skills/base/#abstractbehaviour-objects","title":"AbstractBehaviour Objects","text":"
class AbstractBehaviour(SkillComponent, ABC)\n

Abstract behaviour for periodical calls.

tick_interval: float, interval to call behaviour's act. start_at: optional datetime, when to start periodical calls.

"},{"location":"aea-framework-documentation/api/skills/base/#tick_interval","title":"tick_interval","text":"
@property\ndef tick_interval() -> float\n

Get the tick_interval in seconds.

"},{"location":"aea-framework-documentation/api/skills/base/#start_at","title":"start_at","text":"
@property\ndef start_at() -> Optional[datetime.datetime]\n

Get the start time of the behaviour.

"},{"location":"aea-framework-documentation/api/skills/base/#behaviour-objects","title":"Behaviour Objects","text":"
class Behaviour(AbstractBehaviour, ABC)\n

This class implements an abstract behaviour.

In a subclass of Behaviour, the flag 'is_programmatically_defined' can be used by the developer to signal to the framework that the class is meant to be used programmatically; hence, in case the class is not declared in the configuration file but it is present in a skill module, the framework will just ignore this class instead of printing a warning message.

"},{"location":"aea-framework-documentation/api/skills/base/#act","title":"act","text":"
@abstractmethod\ndef act() -> None\n

Implement the behaviour.

Returns:

None

"},{"location":"aea-framework-documentation/api/skills/base/#is_done","title":"is_done","text":"
def is_done() -> bool\n

Return True if the behaviour is terminated, False otherwise.

"},{"location":"aea-framework-documentation/api/skills/base/#act_wrapper","title":"act_wrapper","text":"
def act_wrapper() -> None\n

Wrap the call of the action. This method must be called only by the framework.

"},{"location":"aea-framework-documentation/api/skills/base/#parse_module_1","title":"parse_module","text":"
@classmethod\ndef parse_module(cls, path: str,\nbehaviour_configs: Dict[str, SkillComponentConfiguration],\nskill_context: SkillContext) -> Dict[str, \"Behaviour\"]\n

Parse the behaviours module.

Arguments:

  • path: path to the Python module containing the Behaviour classes.
  • behaviour_configs: a list of behaviour configurations.
  • skill_context: the skill context

Returns:

a list of Behaviour.

"},{"location":"aea-framework-documentation/api/skills/base/#handler-objects","title":"Handler Objects","text":"
class Handler(SkillComponent, ABC)\n

This class implements an abstract behaviour.

In a subclass of Handler, the flag 'is_programmatically_defined' can be used by the developer to signal to the framework that the component is meant to be used programmatically; hence, in case the class is not declared in the configuration file but it is present in a skill module, the framework will just ignore this class instead of printing a warning message.

SUPPORTED_PROTOCOL is read by the framework when the handlers are loaded to register them as 'listeners' to the protocol identified by the specified public id. Whenever a message of protocol 'SUPPORTED_PROTOCOL' is sent to the agent, the framework will call the 'handle' method.

"},{"location":"aea-framework-documentation/api/skills/base/#handle","title":"handle","text":"
@abstractmethod\ndef handle(message: Message) -> None\n

Implement the reaction to a message.

Arguments:

  • message: the message

Returns:

None

"},{"location":"aea-framework-documentation/api/skills/base/#handle_wrapper","title":"handle_wrapper","text":"
def handle_wrapper(message: Message) -> None\n

Wrap the call of the handler. This method must be called only by the framework.

"},{"location":"aea-framework-documentation/api/skills/base/#parse_module_2","title":"parse_module","text":"
@classmethod\ndef parse_module(cls, path: str,\nhandler_configs: Dict[str, SkillComponentConfiguration],\nskill_context: SkillContext) -> Dict[str, \"Handler\"]\n

Parse the handler module.

Arguments:

  • path: path to the Python module containing the Handler class.
  • handler_configs: the list of handler configurations.
  • skill_context: the skill context

Returns:

an handler, or None if the parsing fails.

"},{"location":"aea-framework-documentation/api/skills/base/#model-objects","title":"Model Objects","text":"
class Model(SkillComponent, ABC)\n

This class implements an abstract model.

"},{"location":"aea-framework-documentation/api/skills/base/#__init___2","title":"__init__","text":"
def __init__(name: str,\nskill_context: SkillContext,\nconfiguration: Optional[SkillComponentConfiguration] = None,\nkeep_terminal_state_dialogues: Optional[bool] = None,\n**kwargs: Any) -> None\n

Initialize a model.

Arguments:

  • name: the name of the component.
  • configuration: the configuration for the component.
  • skill_context: the skill context.
  • keep_terminal_state_dialogues: specify do dialogues in terminal state should stay or not
  • kwargs: the keyword arguments.

"},{"location":"aea-framework-documentation/api/skills/base/#setup_1","title":"setup","text":"
def setup() -> None\n

Set the class up.

"},{"location":"aea-framework-documentation/api/skills/base/#teardown_1","title":"teardown","text":"
def teardown() -> None\n

Tear the class down.

"},{"location":"aea-framework-documentation/api/skills/base/#parse_module_3","title":"parse_module","text":"
@classmethod\ndef parse_module(cls, path: str,\nmodel_configs: Dict[str, SkillComponentConfiguration],\nskill_context: SkillContext) -> Dict[str, \"Model\"]\n

Parse the model module.

Arguments:

  • path: path to the Python skill module.
  • model_configs: a list of model configurations.
  • skill_context: the skill context

Returns:

a list of Model.

"},{"location":"aea-framework-documentation/api/skills/base/#skill-objects","title":"Skill Objects","text":"
class Skill(Component)\n

This class implements a skill.

"},{"location":"aea-framework-documentation/api/skills/base/#__init___3","title":"__init__","text":"
def __init__(configuration: SkillConfig,\nskill_context: Optional[SkillContext] = None,\nhandlers: Optional[Dict[str, Handler]] = None,\nbehaviours: Optional[Dict[str, Behaviour]] = None,\nmodels: Optional[Dict[str, Model]] = None,\n**kwargs: Any)\n

Initialize a skill.

Arguments:

  • configuration: the skill configuration.
  • skill_context: the skill context.
  • handlers: dictionary of handlers.
  • behaviours: dictionary of behaviours.
  • models: dictionary of models.
  • kwargs: the keyword arguments.

"},{"location":"aea-framework-documentation/api/skills/base/#skill_context","title":"skill_context","text":"
@property\ndef skill_context() -> SkillContext\n

Get the skill context.

"},{"location":"aea-framework-documentation/api/skills/base/#handlers_1","title":"handlers","text":"
@property\ndef handlers() -> Dict[str, Handler]\n

Get the handlers.

"},{"location":"aea-framework-documentation/api/skills/base/#behaviours_1","title":"behaviours","text":"
@property\ndef behaviours() -> Dict[str, Behaviour]\n

Get the handlers.

"},{"location":"aea-framework-documentation/api/skills/base/#models","title":"models","text":"
@property\ndef models() -> Dict[str, Model]\n

Get the handlers.

"},{"location":"aea-framework-documentation/api/skills/base/#from_dir","title":"from_dir","text":"
@classmethod\ndef from_dir(cls, directory: str, agent_context: AgentContext,\n**kwargs: Any) -> \"Skill\"\n

Load the skill from a directory.

Arguments:

  • directory: the directory to the skill package.
  • agent_context: the skill context.
  • kwargs: the keyword arguments.

Returns:

the skill object.

"},{"location":"aea-framework-documentation/api/skills/base/#logger_2","title":"logger","text":"
@property\ndef logger() -> Logger\n

Get the logger.

In the case of a skill, return the logger provided by the skill context.

Returns:

the logger

"},{"location":"aea-framework-documentation/api/skills/base/#logger_3","title":"logger","text":"
@logger.setter\ndef logger(*args: str) -> None\n

Set the logger.

"},{"location":"aea-framework-documentation/api/skills/base/#from_config","title":"from_config","text":"
@classmethod\ndef from_config(cls, configuration: SkillConfig, agent_context: AgentContext,\n**kwargs: Any) -> \"Skill\"\n

Load the skill from configuration.

Arguments:

  • configuration: a skill configuration. Must be associated with a directory.
  • agent_context: the agent context.
  • kwargs: the keyword arguments.

Returns:

the skill.

"},{"location":"aea-framework-documentation/api/skills/base/#_skillcomponentloadingitem-objects","title":"_SkillComponentLoadingItem Objects","text":"
class _SkillComponentLoadingItem()\n

Class to represent a triple (component name, component configuration, component class).

"},{"location":"aea-framework-documentation/api/skills/base/#__init___4","title":"__init__","text":"
def __init__(name: str, config: SkillComponentConfiguration,\nclass_: Type[SkillComponent], type_: _SKILL_COMPONENT_TYPES)\n

Initialize the item.

"},{"location":"aea-framework-documentation/api/skills/base/#_skillcomponentloader-objects","title":"_SkillComponentLoader Objects","text":"
class _SkillComponentLoader()\n

This class implements the loading policy for skill components.

"},{"location":"aea-framework-documentation/api/skills/base/#__init___5","title":"__init__","text":"
def __init__(configuration: SkillConfig, skill_context: SkillContext,\n**kwargs: Any)\n

Initialize the helper class.

"},{"location":"aea-framework-documentation/api/skills/base/#load_skill","title":"load_skill","text":"
def load_skill() -> Skill\n

Load the skill.

"},{"location":"aea-framework-documentation/api/skills/behaviours/","title":"Behaviors","text":""},{"location":"aea-framework-documentation/api/skills/behaviours/#aeaskillsbehaviours","title":"aea.skills.behaviours","text":"

This module contains the classes for specific behaviours.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#simplebehaviour-objects","title":"SimpleBehaviour Objects","text":"
class SimpleBehaviour(Behaviour, ABC)\n

This class implements a simple behaviour.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#__init__","title":"__init__","text":"
def __init__(act: Optional[Callable[[], None]] = None, **kwargs: Any) -> None\n

Initialize a simple behaviour.

Arguments:

  • act: the act callable.
  • kwargs: the keyword arguments to be passed to the parent class.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#setup","title":"setup","text":"
def setup() -> None\n

Set the behaviour up.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#act","title":"act","text":"
def act() -> None\n

Do the action.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#teardown","title":"teardown","text":"
def teardown() -> None\n

Tear the behaviour down.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#compositebehaviour-objects","title":"CompositeBehaviour Objects","text":"
class CompositeBehaviour(Behaviour, ABC)\n

This class implements a composite behaviour.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#cyclicbehaviour-objects","title":"CyclicBehaviour Objects","text":"
class CyclicBehaviour(SimpleBehaviour, ABC)\n

This behaviour is executed until the agent is stopped.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#__init___1","title":"__init__","text":"
def __init__(**kwargs: Any) -> None\n

Initialize the cyclic behaviour.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#number_of_executions","title":"number_of_executions","text":"
@property\ndef number_of_executions() -> int\n

Get the number of executions.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#act_wrapper","title":"act_wrapper","text":"
def act_wrapper() -> None\n

Wrap the call of the action. This method must be called only by the framework.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#is_done","title":"is_done","text":"
def is_done() -> bool\n

Return True if the behaviour is terminated, False otherwise.

The user should implement it properly to determine the stopping condition.

Returns:

bool indicating status

"},{"location":"aea-framework-documentation/api/skills/behaviours/#oneshotbehaviour-objects","title":"OneShotBehaviour Objects","text":"
class OneShotBehaviour(SimpleBehaviour, ABC)\n

This behaviour is executed only once.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#__init___2","title":"__init__","text":"
def __init__(**kwargs: Any) -> None\n

Initialize the cyclic behaviour.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#is_done_1","title":"is_done","text":"
def is_done() -> bool\n

Return True if the behaviour is terminated, False otherwise.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#act_wrapper_1","title":"act_wrapper","text":"
def act_wrapper() -> None\n

Wrap the call of the action. This method must be called only by the framework.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#tickerbehaviour-objects","title":"TickerBehaviour Objects","text":"
class TickerBehaviour(SimpleBehaviour, ABC)\n

This behaviour is executed periodically with an interval.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#__init___3","title":"__init__","text":"
def __init__(tick_interval: float = 1.0,\nstart_at: Optional[datetime.datetime] = None,\n**kwargs: Any) -> None\n

Initialize the ticker behaviour.

Arguments:

  • tick_interval: interval of the behaviour in seconds.
  • start_at: whether to start the behaviour with an offset.
  • kwargs: the keyword arguments.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#tick_interval","title":"tick_interval","text":"
@property\ndef tick_interval() -> float\n

Get the tick_interval in seconds.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#start_at","title":"start_at","text":"
@property\ndef start_at() -> datetime.datetime\n

Get the start time.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#last_act_time","title":"last_act_time","text":"
@property\ndef last_act_time() -> datetime.datetime\n

Get the last time the act method has been called.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#act_wrapper_2","title":"act_wrapper","text":"
def act_wrapper() -> None\n

Wrap the call of the action. This method must be called only by the framework.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#is_time_to_act","title":"is_time_to_act","text":"
def is_time_to_act() -> bool\n

Check whether it is time to act, according to the tick_interval constraint and the 'start at' constraint.

Returns:

True if it is time to act, false otherwise.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#sequencebehaviour-objects","title":"SequenceBehaviour Objects","text":"
class SequenceBehaviour(CompositeBehaviour, ABC)\n

This behaviour executes sub-behaviour serially.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#__init___4","title":"__init__","text":"
def __init__(behaviour_sequence: List[Behaviour], **kwargs: Any) -> None\n

Initialize the sequence behaviour.

Arguments:

  • behaviour_sequence: the sequence of behaviour.
  • kwargs: the keyword arguments

"},{"location":"aea-framework-documentation/api/skills/behaviours/#current_behaviour","title":"current_behaviour","text":"
@property\ndef current_behaviour() -> Optional[Behaviour]\n

Get the current behaviour.

If None, the sequence behaviour can be considered done.

Returns:

current behaviour or None

"},{"location":"aea-framework-documentation/api/skills/behaviours/#act_1","title":"act","text":"
def act() -> None\n

Implement the behaviour.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#is_done_2","title":"is_done","text":"
def is_done() -> bool\n

Return True if the behaviour is terminated, False otherwise.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#state-objects","title":"State Objects","text":"
class State(SimpleBehaviour, ABC)\n

A state of a FSMBehaviour.

A State behaviour is a simple behaviour with a special property 'event' that is opportunely set by the implementer. The event is read by the framework when the behaviour is done in order to pick the transition to trigger.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#__init___5","title":"__init__","text":"
def __init__(**kwargs: Any) -> None\n

Initialize a state of the state machine.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#event","title":"event","text":"
@property\ndef event() -> Optional[str]\n

Get the event to be triggered at the end of the behaviour.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#is_done_3","title":"is_done","text":"
@abstractmethod\ndef is_done() -> bool\n

Return True if the behaviour is terminated, False otherwise.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#reset","title":"reset","text":"
def reset() -> None\n

Reset initial conditions.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#fsmbehaviour-objects","title":"FSMBehaviour Objects","text":"
class FSMBehaviour(CompositeBehaviour, ABC)\n

This class implements a finite-state machine behaviour.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#__init___6","title":"__init__","text":"
def __init__(**kwargs: Any) -> None\n

Initialize the finite-state machine behaviour.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#is_started","title":"is_started","text":"
@property\ndef is_started() -> bool\n

Check if the behaviour is started.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#register_state","title":"register_state","text":"
def register_state(name: str, state: State, initial: bool = False) -> None\n

Register a state.

Arguments:

  • name: the name of the state.
  • state: the behaviour in that state.
  • initial: whether the state is an initial state.

Raises:

  • ValueError: if a state with the provided name already exists.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#register_final_state","title":"register_final_state","text":"
def register_final_state(name: str, state: State) -> None\n

Register a final state.

Arguments:

  • name: the name of the state.
  • state: the state.

Raises:

  • ValueError: if a state with the provided name already exists.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#unregister_state","title":"unregister_state","text":"
def unregister_state(name: str) -> None\n

Unregister a state.

Arguments:

  • name: the state name to unregister.

Raises:

  • ValueError: if the state is not registered.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#states","title":"states","text":"
@property\ndef states() -> Set[str]\n

Get all the state names.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#initial_state","title":"initial_state","text":"
@property\ndef initial_state() -> Optional[str]\n

Get the initial state name.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#initial_state_1","title":"initial_state","text":"
@initial_state.setter\ndef initial_state(name: str) -> None\n

Set the initial state.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#final_states","title":"final_states","text":"
@property\ndef final_states() -> Set[str]\n

Get the final state names.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#get_state","title":"get_state","text":"
def get_state(name: str) -> Optional[State]\n

Get a state from its name.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#act_2","title":"act","text":"
def act() -> None\n

Implement the behaviour.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#is_done_4","title":"is_done","text":"
def is_done() -> bool\n

Return True if the behaviour is terminated, False otherwise.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#register_transition","title":"register_transition","text":"
def register_transition(source: str,\ndestination: str,\nevent: Optional[str] = None) -> None\n

Register a transition.

No sanity check is done.

Arguments:

  • source: the source state name.
  • destination: the destination state name.
  • event: the event.

Raises:

  • ValueError: if a transition from source with event is already present.

"},{"location":"aea-framework-documentation/api/skills/behaviours/#unregister_transition","title":"unregister_transition","text":"
def unregister_transition(source: str,\ndestination: str,\nevent: Optional[str] = None) -> None\n

Unregister a transition.

Arguments:

  • source: the source state name.
  • destination: the destination state name.
  • event: the event.

Raises:

  • ValueError: if a transition from source with event is not present.
"},{"location":"aea-framework-documentation/api/skills/tasks/","title":"Task","text":""},{"location":"aea-framework-documentation/api/skills/tasks/#aeaskillstasks","title":"aea.skills.tasks","text":"

This module contains the classes for tasks.

"},{"location":"aea-framework-documentation/api/skills/tasks/#task-objects","title":"Task Objects","text":"
class Task(WithLogger)\n

This class implements an abstract task.

"},{"location":"aea-framework-documentation/api/skills/tasks/#__init__","title":"__init__","text":"
def __init__(**kwargs: Any) -> None\n

Initialize a task.

"},{"location":"aea-framework-documentation/api/skills/tasks/#__call__","title":"__call__","text":"
def __call__(*args: Any, **kwargs: Any) -> Any\n

Execute the task.

Arguments:

  • args: positional arguments forwarded to the 'execute' method.
  • kwargs: keyword arguments forwarded to the 'execute' method.

Raises:

  • ValueError: if the task has already been executed.

Returns:

the task instance

"},{"location":"aea-framework-documentation/api/skills/tasks/#is_executed","title":"is_executed","text":"
@property\ndef is_executed() -> bool\n

Check if the task has already been executed.

"},{"location":"aea-framework-documentation/api/skills/tasks/#result","title":"result","text":"
@property\ndef result() -> Any\n

Get the result.

Raises:

  • ValueError: if the task has not been executed yet.

Returns:

the result from the execute method.

"},{"location":"aea-framework-documentation/api/skills/tasks/#setup","title":"setup","text":"
def setup() -> None\n

Implement the task setup.

"},{"location":"aea-framework-documentation/api/skills/tasks/#execute","title":"execute","text":"
@abstractmethod\ndef execute(*args: Any, **kwargs: Any) -> Any\n

Run the task logic.

Arguments:

  • args: the positional arguments
  • kwargs: the keyword arguments

Returns:

any

"},{"location":"aea-framework-documentation/api/skills/tasks/#teardown","title":"teardown","text":"
def teardown() -> None\n

Implement the task teardown.

"},{"location":"aea-framework-documentation/api/skills/tasks/#init_worker","title":"init_worker","text":"
def init_worker() -> None\n

Initialize a worker.

Disable the SIGINT handler of process pool is using. Related to a well-known bug: https://bugs.python.org/issue8296

"},{"location":"aea-framework-documentation/api/skills/tasks/#taskmanager-objects","title":"TaskManager Objects","text":"
class TaskManager(WithLogger)\n

A Task manager.

"},{"location":"aea-framework-documentation/api/skills/tasks/#__init___1","title":"__init__","text":"
def __init__(nb_workers: int = DEFAULT_WORKERS_AMOUNT,\nis_lazy_pool_start: bool = True,\nlogger: Optional[logging.Logger] = None,\npool_mode: str = THREAD_POOL_MODE) -> None\n

Initialize the task manager.

Arguments:

  • nb_workers: the number of worker processes.
  • is_lazy_pool_start: option to postpone pool creation till the first enqueue_task called.
  • logger: the logger.
  • pool_mode: str. multithread or multiprocess

"},{"location":"aea-framework-documentation/api/skills/tasks/#is_started","title":"is_started","text":"
@property\ndef is_started() -> bool\n

Get started status of TaskManager.

Returns:

bool

"},{"location":"aea-framework-documentation/api/skills/tasks/#nb_workers","title":"nb_workers","text":"
@property\ndef nb_workers() -> int\n

Get the number of workers.

Returns:

int

"},{"location":"aea-framework-documentation/api/skills/tasks/#enqueue_task","title":"enqueue_task","text":"
def enqueue_task(func: Callable,\nargs: Sequence = (),\nkwargs: Optional[Dict[str, Any]] = None) -> int\n

Enqueue a task with the executor.

Arguments:

  • func: the callable instance to be enqueued
  • args: the positional arguments to be passed to the function.
  • kwargs: the keyword arguments to be passed to the function.

Raises:

  • ValueError: if the task manager is not running.

Returns:

the task id to get the the result.

"},{"location":"aea-framework-documentation/api/skills/tasks/#get_task_result","title":"get_task_result","text":"
def get_task_result(task_id: int) -> AsyncResult\n

Get the result from a task.

Arguments:

  • task_id: the task id

Returns:

async result for task_id

"},{"location":"aea-framework-documentation/api/skills/tasks/#start","title":"start","text":"
def start() -> None\n

Start the task manager.

"},{"location":"aea-framework-documentation/api/skills/tasks/#stop","title":"stop","text":"
def stop() -> None\n

Stop the task manager.

"},{"location":"aea-framework-documentation/api/skills/tasks/#threadedtaskmanager-objects","title":"ThreadedTaskManager Objects","text":"
class ThreadedTaskManager(TaskManager)\n

A threaded task manager.

"},{"location":"aea-framework-documentation/api/skills/tasks/#__init___2","title":"__init__","text":"
def __init__(nb_workers: int = DEFAULT_WORKERS_AMOUNT,\nis_lazy_pool_start: bool = True,\nlogger: Optional[logging.Logger] = None) -> None\n

Initialize the task manager.

Arguments:

  • nb_workers: the number of worker processes.
  • is_lazy_pool_start: option to postpone pool creation till the first enqueue_task called.
  • logger: the logger.

"},{"location":"aea-framework-documentation/api/skills/tasks/#processtaskmanager-objects","title":"ProcessTaskManager Objects","text":"
class ProcessTaskManager(TaskManager)\n

A multiprocess task manager.

"},{"location":"aea-framework-documentation/api/skills/tasks/#__init___3","title":"__init__","text":"
def __init__(nb_workers: int = DEFAULT_WORKERS_AMOUNT,\nis_lazy_pool_start: bool = True,\nlogger: Optional[logging.Logger] = None) -> None\n

Initialize the task manager.

Arguments:

  • nb_workers: the number of worker processes.
  • is_lazy_pool_start: option to postpone pool creation till the first enqueue_task called.
  • logger: the logger.
"},{"location":"aea-framework-documentation/api/test_tools/constants/","title":"Constants","text":""},{"location":"aea-framework-documentation/api/test_tools/constants/#aeatest_toolsconstants","title":"aea.test_tools.constants","text":"

This is a module with constants for test tools.

"},{"location":"aea-framework-documentation/api/test_tools/exceptions/","title":"Exceptions","text":""},{"location":"aea-framework-documentation/api/test_tools/exceptions/#aeatest_toolsexceptions","title":"aea.test_tools.exceptions","text":"

Module with AEA testing exceptions.

"},{"location":"aea-framework-documentation/api/test_tools/exceptions/#aeatestingexception-objects","title":"AEATestingException Objects","text":"
class AEATestingException(Exception)\n

An exception to be raised on incorrect testing tools usage.

"},{"location":"aea-framework-documentation/api/test_tools/generic/","title":"Generic","text":""},{"location":"aea-framework-documentation/api/test_tools/generic/#aeatest_toolsgeneric","title":"aea.test_tools.generic","text":"

This module contains generic tools for AEA end-to-end testing.

"},{"location":"aea-framework-documentation/api/test_tools/generic/#write_envelope_to_file","title":"write_envelope_to_file","text":"
def write_envelope_to_file(envelope: Envelope, file_path: str) -> None\n

Write an envelope to a file.

Arguments:

  • envelope: Envelope.
  • file_path: the file path

"},{"location":"aea-framework-documentation/api/test_tools/generic/#read_envelope_from_file","title":"read_envelope_from_file","text":"
def read_envelope_from_file(file_path: str) -> Envelope\n

Read an envelope from a file.

Arguments:

  • file_path: the file path.

Returns:

envelope

"},{"location":"aea-framework-documentation/api/test_tools/generic/#nested_set_config","title":"nested_set_config","text":"
def nested_set_config(dotted_path: str,\nvalue: Any,\nauthor: str = DEFAULT_AUTHOR) -> None\n

Set an AEA config with nested values.

Run from agent's directory.

Allowed dotted_path: 'agent.an_attribute_name' 'protocols.my_protocol.an_attribute_name' 'connections.my_connection.an_attribute_name' 'contracts.my_contract.an_attribute_name' 'skills.my_skill.an_attribute_name' 'vendor.author.[protocols|connections|skills].package_name.attribute_name

Arguments:

  • dotted_path: dotted path to a setting.
  • value: a value to assign. Must be of yaml serializable type.
  • author: the author name, used to parse the dotted path.
"},{"location":"aea-framework-documentation/api/test_tools/test_cases/","title":"Test Cases","text":""},{"location":"aea-framework-documentation/api/test_tools/test_cases/#aeatest_toolstest_cases","title":"aea.test_tools.test_cases","text":"

This module contains test case classes based on pytest for AEA end-to-end testing.

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#baseaeatestcase-objects","title":"BaseAEATestCase Objects","text":"
class BaseAEATestCase(ABC)\n

Base class for AEA test cases.

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#set_agent_context","title":"set_agent_context","text":"
@classmethod\ndef set_agent_context(cls, agent_name: str) -> None\n

Set the current agent context.

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#unset_agent_context","title":"unset_agent_context","text":"
@classmethod\ndef unset_agent_context(cls) -> None\n

Unset the current agent context.

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#set_config","title":"set_config","text":"
@classmethod\ndef set_config(cls,\ndotted_path: str,\nvalue: Any,\ntype_: Optional[str] = None) -> Result\n

Set a config.

Run from agent's directory.

Arguments:

  • dotted_path: str dotted path to config param.
  • value: a new value to set.
  • type_: the type

Returns:

Result

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#nested_set_config","title":"nested_set_config","text":"
@classmethod\ndef nested_set_config(cls, dotted_path: str, value: Any) -> None\n

Force set config.

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#disable_aea_logging","title":"disable_aea_logging","text":"
@classmethod\ndef disable_aea_logging(cls) -> None\n

Disable AEA logging of specific agent.

Run from agent's directory.

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#run_cli_command","title":"run_cli_command","text":"
@classmethod\ndef run_cli_command(cls, *args: str, cwd: str = \".\", **kwargs: str) -> Result\n

Run AEA CLI command.

Arguments:

  • args: CLI args
  • cwd: the working directory from where to run the command.
  • kwargs: other keyword arguments to click.CliRunner.invoke.

Raises:

  • AEATestingException: if command fails.

Returns:

Result

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#start_subprocess","title":"start_subprocess","text":"
@classmethod\ndef start_subprocess(cls, *args: str, cwd: str = \".\") -> subprocess.Popen\n

Run python with args as subprocess.

Arguments:

  • args: CLI args
  • cwd: the current working directory

Returns:

subprocess object.

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#start_thread","title":"start_thread","text":"
@classmethod\ndef start_thread(cls, target: Callable, **kwargs: subprocess.Popen) -> Thread\n

Start python Thread.

Arguments:

  • target: target method.
  • kwargs: thread keyword arguments

Returns:

thread

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#create_agents","title":"create_agents","text":"
@classmethod\ndef create_agents(cls,\n*agents_names: str,\nis_local: bool = True,\nis_empty: bool = False) -> None\n

Create agents in current working directory.

Arguments:

  • agents_names: str agent names.
  • is_local: a flag for local folder add True by default.
  • is_empty: optional boolean flag for skip adding default dependencies.

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#fetch_agent","title":"fetch_agent","text":"
@classmethod\ndef fetch_agent(cls,\npublic_id: str,\nagent_name: str,\nis_local: bool = True) -> None\n

Create agents in current working directory.

Arguments:

  • public_id: str public id
  • agent_name: str agent name.
  • is_local: a flag for local folder add True by default.

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#difference_to_fetched_agent","title":"difference_to_fetched_agent","text":"
@classmethod\ndef difference_to_fetched_agent(cls, public_id: str,\nagent_name: str) -> List[str]\n

Compare agent against the one fetched from public id.

Arguments:

  • public_id: str public id
  • agent_name: str agent name.

Returns:

list of files differing in the projects

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#delete_agents","title":"delete_agents","text":"
@classmethod\ndef delete_agents(cls, *agents_names: str) -> None\n

Delete agents in current working directory.

Arguments:

  • agents_names: str agent names.

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#run_agent","title":"run_agent","text":"
@classmethod\ndef run_agent(cls, *args: str) -> subprocess.Popen\n

Run agent as subprocess.

Run from agent's directory.

Arguments:

  • args: CLI args

Returns:

subprocess object.

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#run_interaction","title":"run_interaction","text":"
@classmethod\ndef run_interaction(cls) -> subprocess.Popen\n

Run interaction as subprocess.

Run from agent's directory.

Returns:

subprocess object.

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#terminate_agents","title":"terminate_agents","text":"
@classmethod\ndef terminate_agents(cls,\n*subprocesses: subprocess.Popen,\ntimeout: int = 20) -> None\n

Terminate agent subprocesses.

Run from agent's directory.

Arguments:

  • subprocesses: the subprocesses running the agents
  • timeout: the timeout for interruption

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#is_successfully_terminated","title":"is_successfully_terminated","text":"
@classmethod\ndef is_successfully_terminated(cls, *subprocesses: subprocess.Popen) -> bool\n

Check if all subprocesses terminated successfully.

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#initialize_aea","title":"initialize_aea","text":"
@classmethod\ndef initialize_aea(cls, author: str) -> None\n

Initialize AEA locally with author name.

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#add_item","title":"add_item","text":"
@classmethod\ndef add_item(cls,\nitem_type: str,\npublic_id: str,\nlocal: bool = True) -> Result\n

Add an item to the agent.

Run from agent's directory.

Arguments:

  • item_type: str item type.
  • public_id: public id of the item.
  • local: a flag for local folder add True by default.

Returns:

Result

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#remove_item","title":"remove_item","text":"
@classmethod\ndef remove_item(cls, item_type: str, public_id: str) -> Result\n

Remove an item from the agent.

Run from agent's directory.

Arguments:

  • item_type: str item type.
  • public_id: public id of the item.

Returns:

Result

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#scaffold_item","title":"scaffold_item","text":"
@classmethod\ndef scaffold_item(cls,\nitem_type: str,\nname: str,\nskip_consistency_check: bool = False) -> Result\n

Scaffold an item for the agent.

Run from agent's directory.

Arguments:

  • item_type: str item type.
  • name: name of the item.
  • skip_consistency_check: if True, skip consistency check.

Returns:

Result

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#fingerprint_item","title":"fingerprint_item","text":"
@classmethod\ndef fingerprint_item(cls, item_type: str, public_id: str) -> Result\n

Fingerprint an item for the agent.

Run from agent's directory.

Arguments:

  • item_type: str item type.
  • public_id: public id of the item.

Returns:

Result

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#eject_item","title":"eject_item","text":"
@classmethod\ndef eject_item(cls, item_type: str, public_id: str) -> Result\n

Eject an item in the agent in quiet mode (i.e. no interaction).

Run from agent's directory.

Arguments:

  • item_type: str item type.
  • public_id: public id of the item.

Returns:

None

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#run_install","title":"run_install","text":"
@classmethod\ndef run_install(cls) -> Result\n

Execute AEA CLI install command.

Run from agent's directory.

Returns:

Result

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#generate_private_key","title":"generate_private_key","text":"
@classmethod\ndef generate_private_key(cls,\nledger_api_id: str = DEFAULT_LEDGER,\nprivate_key_file: Optional[str] = None,\npassword: Optional[str] = None) -> Result\n

Generate AEA private key with CLI command.

Run from agent's directory.

Arguments:

  • ledger_api_id: ledger API ID.
  • private_key_file: the private key file.
  • password: the password.

Returns:

Result

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#add_private_key","title":"add_private_key","text":"
@classmethod\ndef add_private_key(cls,\nledger_api_id: str = DEFAULT_LEDGER,\nprivate_key_filepath: str = DEFAULT_PRIVATE_KEY_FILE,\nconnection: bool = False,\npassword: Optional[str] = None) -> Result\n

Add private key with CLI command.

Run from agent's directory.

Arguments:

  • ledger_api_id: ledger API ID.
  • private_key_filepath: private key filepath.
  • connection: whether or not the private key filepath is for a connection.
  • password: the password to encrypt private keys.

Returns:

Result

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#remove_private_key","title":"remove_private_key","text":"
@classmethod\ndef remove_private_key(cls,\nledger_api_id: str = DEFAULT_LEDGER,\nconnection: bool = False) -> Result\n

Remove private key with CLI command.

Run from agent's directory.

Arguments:

  • ledger_api_id: ledger API ID.
  • connection: whether or not the private key filepath is for a connection.

Returns:

Result

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#replace_private_key_in_file","title":"replace_private_key_in_file","text":"
@classmethod\ndef replace_private_key_in_file(\ncls,\nprivate_key: str,\nprivate_key_filepath: str = DEFAULT_PRIVATE_KEY_FILE) -> None\n

Replace the private key in the provided file with the provided key.

Arguments:

  • private_key: the private key
  • private_key_filepath: the filepath to the private key file

Raises:

  • None: exception if file does not exist

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#generate_wealth","title":"generate_wealth","text":"
@classmethod\ndef generate_wealth(cls,\nledger_api_id: str = DEFAULT_LEDGER,\npassword: Optional[str] = None) -> Result\n

Generate wealth with CLI command.

Run from agent's directory.

Arguments:

  • ledger_api_id: ledger API ID.
  • password: the password.

Returns:

Result

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#get_wealth","title":"get_wealth","text":"
@classmethod\ndef get_wealth(cls,\nledger_api_id: str = DEFAULT_LEDGER,\npassword: Optional[str] = None) -> str\n

Get wealth with CLI command.

Run from agent's directory.

Arguments:

  • ledger_api_id: ledger API ID.
  • password: the password to encrypt/decrypt private keys.

Returns:

command line output

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#get_address","title":"get_address","text":"
@classmethod\ndef get_address(cls,\nledger_api_id: str = DEFAULT_LEDGER,\npassword: Optional[str] = None) -> str\n

Get address with CLI command.

Run from agent's directory.

Arguments:

  • ledger_api_id: ledger API ID.
  • password: the password to encrypt/decrypt private keys.

Returns:

command line output

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#replace_file_content","title":"replace_file_content","text":"
@classmethod\ndef replace_file_content(cls, src: Path, dest: Path) -> None\n

Replace the content of the source file to the destination file.

Arguments:

  • src: the source file.
  • dest: the destination file.

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#change_directory","title":"change_directory","text":"
@classmethod\ndef change_directory(cls, path: Path) -> None\n

Change current working directory.

Arguments:

  • path: path to the new working directory.

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#send_envelope_to_agent","title":"send_envelope_to_agent","text":"
@classmethod\ndef send_envelope_to_agent(cls, envelope: Envelope, agent: str) -> None\n

Send an envelope to an agent, using the stub connection.

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#read_envelope_from_agent","title":"read_envelope_from_agent","text":"
@classmethod\ndef read_envelope_from_agent(cls, agent: str) -> Envelope\n

Read an envelope from an agent, using the stub connection.

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#missing_from_output","title":"missing_from_output","text":"
@classmethod\ndef missing_from_output(cls,\nprocess: subprocess.Popen,\nstrings: Sequence[str],\ntimeout: int = DEFAULT_PROCESS_TIMEOUT,\nperiod: int = 1,\nis_terminating: bool = True) -> List[str]\n

Check if strings are present in process output.

Read process stdout in thread and terminate when all strings are present or timeout expired.

Arguments:

  • process: agent subprocess.
  • strings: tuple of strings expected to appear in output.
  • timeout: int amount of seconds before stopping check.
  • period: int period of checking.
  • is_terminating: whether or not the agents are terminated

Returns:

list of missed strings.

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#is_running","title":"is_running","text":"
@classmethod\ndef is_running(cls,\nprocess: subprocess.Popen,\ntimeout: int = DEFAULT_LAUNCH_TIMEOUT) -> bool\n

Check if the AEA is launched and running (ready to process messages).

Arguments:

  • process: agent subprocess.
  • timeout: the timeout to wait for launch to complete

Returns:

bool indicating status

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#invoke","title":"invoke","text":"
@classmethod\ndef invoke(cls, *args: str) -> Result\n

Call the cli command.

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#load_agent_config","title":"load_agent_config","text":"
@classmethod\ndef load_agent_config(cls, agent_name: str) -> AgentConfig\n

Load agent configuration.

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#setup_class","title":"setup_class","text":"
@classmethod\ndef setup_class(cls) -> None\n

Set up the test class.

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#teardown_class","title":"teardown_class","text":"
@classmethod\ndef teardown_class(cls) -> None\n

Teardown the test.

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#aeatestcaseempty-objects","title":"AEATestCaseEmpty Objects","text":"
class AEATestCaseEmpty(BaseAEATestCase)\n

Test case for a default AEA project.

This test case will create a default AEA project.

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#setup_class_1","title":"setup_class","text":"
@classmethod\ndef setup_class(cls) -> None\n

Set up the test class.

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#teardown_class_1","title":"teardown_class","text":"
@classmethod\ndef teardown_class(cls) -> None\n

Teardown the test class.

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#aeatestcaseemptyflaky-objects","title":"AEATestCaseEmptyFlaky Objects","text":"
class AEATestCaseEmptyFlaky(AEATestCaseEmpty)\n

Test case for a default AEA project.

This test case will create a default AEA project.

Use for flaky tests with the flaky decorator.

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#setup_class_2","title":"setup_class","text":"
@classmethod\ndef setup_class(cls) -> None\n

Set up the test class.

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#teardown_class_2","title":"teardown_class","text":"
@classmethod\ndef teardown_class(cls) -> None\n

Teardown the test class.

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#aeatestcasemany-objects","title":"AEATestCaseMany Objects","text":"
class AEATestCaseMany(BaseAEATestCase)\n

Test case for many AEA projects.

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#setup_class_3","title":"setup_class","text":"
@classmethod\ndef setup_class(cls) -> None\n

Set up the test class.

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#teardown_class_3","title":"teardown_class","text":"
@classmethod\ndef teardown_class(cls) -> None\n

Teardown the test class.

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#aeatestcasemanyflaky-objects","title":"AEATestCaseManyFlaky Objects","text":"
class AEATestCaseManyFlaky(AEATestCaseMany)\n

Test case for many AEA projects which are flaky.

Use for flaky tests with the flaky decorator.

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#setup_class_4","title":"setup_class","text":"
@classmethod\ndef setup_class(cls) -> None\n

Set up the test class.

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#teardown_class_4","title":"teardown_class","text":"
@classmethod\ndef teardown_class(cls) -> None\n

Teardown the test class.

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#aeatestcase-objects","title":"AEATestCase Objects","text":"
class AEATestCase(BaseAEATestCase)\n

Test case from an existing AEA project.

Subclass this class and set path_to_aea properly. By default, it is assumed the project is inside the current working directory.

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#setup_class_5","title":"setup_class","text":"
@classmethod\ndef setup_class(cls) -> None\n

Set up the test class.

"},{"location":"aea-framework-documentation/api/test_tools/test_cases/#teardown_class_5","title":"teardown_class","text":"
@classmethod\ndef teardown_class(cls) -> None\n

Teardown the test class.

"},{"location":"aea-framework-documentation/api/test_tools/test_contract/","title":"Test Contract","text":""},{"location":"aea-framework-documentation/api/test_tools/test_contract/#aeatest_toolstest_contract","title":"aea.test_tools.test_contract","text":"

This module contains test case classes based on pytest for AEA contract testing.

"},{"location":"aea-framework-documentation/api/test_tools/test_contract/#basecontracttestcase-objects","title":"BaseContractTestCase Objects","text":"
class BaseContractTestCase(ABC)\n

A class to test a contract.

"},{"location":"aea-framework-documentation/api/test_tools/test_contract/#contract","title":"contract","text":"
@property\ndef contract() -> Contract\n

Get the contract.

"},{"location":"aea-framework-documentation/api/test_tools/test_contract/#setup","title":"setup","text":"
@classmethod\ndef setup(cls, **kwargs: Any) -> None\n

Set up the contract test case.

"},{"location":"aea-framework-documentation/api/test_tools/test_contract/#finish_contract_deployment","title":"finish_contract_deployment","text":"
@classmethod\n@abstractmethod\ndef finish_contract_deployment(cls) -> str\n

Finish deploying contract.

Returns:

contract address

"},{"location":"aea-framework-documentation/api/test_tools/test_contract/#refill_from_faucet","title":"refill_from_faucet","text":"
@staticmethod\ndef refill_from_faucet(ledger_api: LedgerApi, faucet_api: FaucetApi,\naddress: str) -> None\n

Refill from faucet.

"},{"location":"aea-framework-documentation/api/test_tools/test_contract/#sign_send_confirm_receipt_multisig_transaction","title":"sign_send_confirm_receipt_multisig_transaction","text":"
@staticmethod\ndef sign_send_confirm_receipt_multisig_transaction(\ntx: JSONLike,\nledger_api: LedgerApi,\ncryptos: List[Crypto],\nsleep_time: float = 2.0) -> JSONLike\n

Sign, send and confirm settlement of a transaction with multiple signatures.

Arguments:

  • tx: the transaction
  • ledger_api: the ledger api
  • cryptos: Cryptos to sign transaction with
  • sleep_time: the time to sleep between transaction submission and receipt request

Returns:

The transaction receipt

"},{"location":"aea-framework-documentation/api/test_tools/test_contract/#sign_send_confirm_receipt_transaction","title":"sign_send_confirm_receipt_transaction","text":"
@classmethod\ndef sign_send_confirm_receipt_transaction(cls,\ntx: JSONLike,\nledger_api: LedgerApi,\ncrypto: Crypto,\nsleep_time: float = 2.0) -> JSONLike\n

Sign, send and confirm settlement of a transaction with multiple signatures.

Arguments:

  • tx: the transaction
  • ledger_api: the ledger api
  • crypto: Crypto to sign transaction with
  • sleep_time: the time to sleep between transaction submission and receipt request

Returns:

The transaction receipt

"},{"location":"aea-framework-documentation/api/test_tools/test_skill/","title":"Test Skill","text":""},{"location":"aea-framework-documentation/api/test_tools/test_skill/#aeatest_toolstest_skill","title":"aea.test_tools.test_skill","text":"

This module contains test case classes based on pytest for AEA skill testing.

"},{"location":"aea-framework-documentation/api/test_tools/test_skill/#baseskilltestcase-objects","title":"BaseSkillTestCase Objects","text":"
class BaseSkillTestCase()\n

A class to test a skill.

"},{"location":"aea-framework-documentation/api/test_tools/test_skill/#skill","title":"skill","text":"
@property\ndef skill() -> Skill\n

Get the skill.

"},{"location":"aea-framework-documentation/api/test_tools/test_skill/#get_quantity_in_outbox","title":"get_quantity_in_outbox","text":"
def get_quantity_in_outbox() -> int\n

Get the quantity of envelopes in the outbox.

"},{"location":"aea-framework-documentation/api/test_tools/test_skill/#get_message_from_outbox","title":"get_message_from_outbox","text":"
def get_message_from_outbox() -> Optional[Message]\n

Get message from outbox.

"},{"location":"aea-framework-documentation/api/test_tools/test_skill/#drop_messages_from_outbox","title":"drop_messages_from_outbox","text":"
def drop_messages_from_outbox(number: int = 1) -> None\n

Dismiss the first 'number' number of message from outbox.

"},{"location":"aea-framework-documentation/api/test_tools/test_skill/#get_quantity_in_decision_maker_inbox","title":"get_quantity_in_decision_maker_inbox","text":"
def get_quantity_in_decision_maker_inbox() -> int\n

Get the quantity of messages in the decision maker inbox.

"},{"location":"aea-framework-documentation/api/test_tools/test_skill/#get_message_from_decision_maker_inbox","title":"get_message_from_decision_maker_inbox","text":"
def get_message_from_decision_maker_inbox() -> Optional[Message]\n

Get message from decision maker inbox.

"},{"location":"aea-framework-documentation/api/test_tools/test_skill/#drop_messages_from_decision_maker_inbox","title":"drop_messages_from_decision_maker_inbox","text":"
def drop_messages_from_decision_maker_inbox(number: int = 1) -> None\n

Dismiss the first 'number' number of message from decision maker inbox.

"},{"location":"aea-framework-documentation/api/test_tools/test_skill/#assert_quantity_in_outbox","title":"assert_quantity_in_outbox","text":"
def assert_quantity_in_outbox(expected_quantity: int) -> None\n

Assert the quantity of messages in the outbox.

"},{"location":"aea-framework-documentation/api/test_tools/test_skill/#assert_quantity_in_decision_making_queue","title":"assert_quantity_in_decision_making_queue","text":"
def assert_quantity_in_decision_making_queue(expected_quantity: int) -> None\n

Assert the quantity of messages in the decision maker queue.

"},{"location":"aea-framework-documentation/api/test_tools/test_skill/#message_has_attributes","title":"message_has_attributes","text":"
@staticmethod\ndef message_has_attributes(actual_message: Message,\nmessage_type: Type[Message],\n**kwargs: Any) -> Tuple[bool, str]\n

Evaluates whether a message's attributes match the expected attributes provided.

Arguments:

  • actual_message: the actual message
  • message_type: the expected message type
  • kwargs: other expected message attributes

Returns:

boolean result of the evaluation and accompanied message

"},{"location":"aea-framework-documentation/api/test_tools/test_skill/#build_incoming_message","title":"build_incoming_message","text":"
def build_incoming_message(message_type: Type[Message],\nperformative: Message.Performative,\ndialogue_reference: Optional[Tuple[str,\nstr]] = None,\nmessage_id: Optional[int] = None,\ntarget: Optional[int] = None,\nto: Optional[Address] = None,\nsender: Optional[Address] = None,\nis_agent_to_agent_messages: Optional[bool] = None,\n**kwargs: Any) -> Message\n

Quickly create an incoming message with the provided attributes.

For any attribute not provided, the corresponding default value in message is used.

Arguments:

  • message_type: the type of the message
  • dialogue_reference: the dialogue_reference
  • message_id: the message_id
  • target: the target
  • performative: the performative
  • to: the 'to' address
  • sender: the 'sender' address
  • is_agent_to_agent_messages: whether the dialogue is between agents or components
  • kwargs: other attributes

Returns:

the created incoming message

"},{"location":"aea-framework-documentation/api/test_tools/test_skill/#build_incoming_message_for_skill_dialogue","title":"build_incoming_message_for_skill_dialogue","text":"
def build_incoming_message_for_skill_dialogue(\ndialogue: Dialogue,\nperformative: Message.Performative,\nmessage_type: Optional[Type[Message]] = None,\ndialogue_reference: Optional[Tuple[str, str]] = None,\nmessage_id: Optional[int] = None,\ntarget: Optional[int] = None,\nto: Optional[Address] = None,\nsender: Optional[Address] = None,\n**kwargs: Any) -> Message\n

Quickly create an incoming message with the provided attributes for a dialogue.

For any attribute not provided, a value based on the dialogue is used. These values are shown in parentheses in the list of parameters below.

NOTE: This method must be used with care. The dialogue provided is part of the skill which is being tested. Because for any unspecified attribute, a \"correct\" value is used, the test will be, by design, insured to pass on these values.

Arguments:

  • dialogue: the dialogue to which the incoming message is intended
  • performative: the performative of the message
  • message_type: (the message_class of the provided dialogue) the type of the message
  • dialogue_reference: (the dialogue_reference of the provided dialogue) the dialogue reference of the message
  • message_id: (the id of the last message in the provided dialogue + 1) the id of the message
  • target: (the id of the last message in the provided dialogue) the target of the message
  • to: (the agent address associated with this skill) the receiver of the message
  • sender: (the counterparty in the provided dialogue) the sender of the message
  • kwargs: other attributes

Returns:

the created incoming message

"},{"location":"aea-framework-documentation/api/test_tools/test_skill/#prepare_skill_dialogue","title":"prepare_skill_dialogue","text":"
def prepare_skill_dialogue(\ndialogues: Dialogues,\nmessages: Tuple[DialogueMessage, ...],\ncounterparty: Optional[Address] = None,\nis_agent_to_agent_messages: Optional[bool] = None) -> Dialogue\n

Quickly create a dialogue.

The 'messages' argument is a tuple of DialogueMessages. For every DialogueMessage (performative, contents, is_incoming, target): - if 'is_incoming' is not provided: for the first message it is assumed False (outgoing), for any other message, it is the opposite of the one preceding it. - if 'target' is not provided: for the first message it is assumed 0, for any other message, it is the index of the message before it in the tuple of messages + 1.

Arguments:

  • dialogues: a dialogues class
  • counterparty: the message_id
  • messages: the dialogue_reference
  • is_agent_to_agent_messages: whether the dialogue is between agents or components

Returns:

the created incoming message

"},{"location":"aea-framework-documentation/api/test_tools/test_skill/#setup","title":"setup","text":"
@classmethod\ndef setup(cls, **kwargs: Any) -> None\n

Set up the skill test case.

"},{"location":"archives/redelegate-for-decentralization/","title":"How to Redelegate for Decentralization","text":"

Introduction

Decentralization is a key problem that we as a community need to tackle in order to provide more security to the network.

As part of this, we have requested that everyone currently staking on the Fetch.ai Mainnet check to see if they are only staking with a single validator or if they are staking within the top 10 validators.

You can tell if your validator is in the top 10 validators by going to the Validator Voting Power Distribution List.

If this is the case, then we would greatly appreciate it if you followed this guide on the importance of redelegating part of your stake outside the top 10 validators and how to do it.

Disclaimer - please note the following:

  1. We are asking you to redelegate your staked $FET, do not undelegate your $FET to transfer from one validator to another, otherwise this process will cause you to lose out on staking rewards for 21 days while you wait for the unbonding period to end.
  2. Redelegating, unlike undelegating, is an instant way for you to move part (or all) of your delegated $FET to different validators. Doing this is a very easy process and is what we will be outlining in this article.
"},{"location":"archives/redelegate-for-decentralization/#why-redelegating-is-important-right-now","title":"Why redelegating is important right now","text":"

The top 10 validators on the Fetch.ai network currently control roughly 50% of the total voting power on chain through delegations.

For the Fetch.ai network to be as secure as possible, we shouldn't have more than 33% of delegated FET staked within the top 10 validators. It should be made clear that lists of validators on block explorers are not the highest to lowest for reputation or benefits, it's simply outlining who has the most FET staked to them.

When it comes to reputation or how the validator has performed, it's up to you as a delegator to find that out. Delegating is meant to be an active role between you and a Validator (although commonly perceived incorrectly as \"passive income\").

When you select a validator to delegate with, that is essentially your vote to the network saying they are trustworthy, and you are given staking rewards for that.

So please ensure that you do your due diligence in ensuring you know as much about a validator as possible.

"},{"location":"archives/redelegate-for-decentralization/#to-redelegate-using-the-cosmostation-app","title":"To redelegate using the Cosmostation App","text":"
  1. On the main screen, click Delegate.
  2. Select the validator you're staking with that you wish to move your stake from.
  3. Select Redelegate.
  4. Enter the amount of FET you wish to redelegate and click Next.
  5. Choose a validator outside of the top 10 that you wish to redelegate to (see here for the voting power of the validators), and click Next.
  6. You'll be prompted to enter an optional memo, but you can leave it blank. Click Next.
  7. Choose the transaction fee for redelegating (low is fine). Click Next.
  8. Review the details of your redelegation. Click Confirm.
  9. Click Yes on the popup. Enter your PIN.
"},{"location":"archives/redelegate-for-decentralization/#to-redelegate-using-keystationcosmostation-web-wallet","title":"To redelegate using Keystation/Cosmostation Web Wallet","text":"
  1. Connect your Fetch.ai wallet to the Cosmostation web wallet.
  2. Select Reward from the options on the left side panel.
  3. Scroll to My Validators and select Redelegate.
  4. Pick the validator outside of the top 10 that you would like to Redelegate to.
  5. Enter the amount of FET you'd like to redelegate to them. Leave the memo blank.
  6. Click Generate & Sign Transaction. On the pop-up select Allow.
  7. Enter your PIN.
"},{"location":"archives/redelegate-for-decentralization/#to-redelegate-using-the-fetch-wallet","title":"To redelegate using the Fetch Wallet","text":"

First you must ensure that you have the Fetch.ai wallet that you're staking with connected to the Fetch Wallet.

  1. Connect to the Fetch Wallet.
  2. In the Fetch.ai Validator Explorer page., click Connect in the top right of the web page. Once you're connected, click on your wallet address that is now listed in the top right on the web page.
  3. From there, scroll down to the Delegations section. Find the validator you'd like to redelegate from.
  4. Click Transfer Stake. In the pop-up that appears, select a validator below the top 10 that you'd like to redelegate to.

    Please keep in mind they are not in order on the drop-down, so you will need to have the Validator Voting Power Distribution List open in another tab to see the order.

  5. Enter in the amount of FET you wish to redelegate to this new validator.

  6. Select Transfer Stake. You will then be prompted by a transaction request.

    Choose the gas fee (Low is fine)

  7. Click Approve.

    And then you're done.

"},{"location":"archives/redelegate-for-decentralization/#what-should-you-look-for-in-a-validator","title":"What should you look for in a validator?","text":"

People always ask what they should be looking for in a validator. So here is a short list of things you, as a delegator, can look for online or ask validators themselves.

  • Do they have a website?
  • Do they have social media? (Twitter, Reddit, Instagram)
  • Are they easy to contact? (Discord, Telegram)
  • Do they have FET self-bonded to their node?
  • Are they active in the community?
  • Do they have a good uptime?
  • Do they offer slashing & double sign protection?
  • Do they vote on proposals?
  • Do they have a mission and principles that align with your own?

Some of these questions can be answered through one of the Fetch.ai block explorers, such as our Native Block Explorer or Mintscan. But other things on this list will require you to reach out to validators for answers, which is a good opportunity to see if they are active frequently or not. It should also be noted that not every single one of these questions needs to be answered \"yes\" but if they are, then it's extremely helpful.

"},{"location":"basics/staking/different_ways_to_stake_fet/","title":"Different ways to stake FET","text":"

Below you can find different ways one can stake FET.

"},{"location":"basics/staking/different_ways_to_stake_fet/#stakingunstaking-on-fetch-mainnet-for-binance-users","title":"STAKING/UNSTAKING ON FETCH MAINNET FOR BINANCE USERS","text":"

Binance has integrated native FET which means you can send your tokens directly to your cosmostation address and start staking. Above is the user journey detailing the steps.

If you need further clarification, please feel free to refer to the section Applicable for users holding FET on Binance below.

"},{"location":"basics/staking/different_ways_to_stake_fet/#how-to-stake-fet-using-ledger-on-cosmostation-web-wallet-applicable-for-users-holding-fet-on-binance","title":"How to stake FET using Ledger on Cosmostation Web Wallet: applicable for users holding FET on Binance","text":"

Binance has completed the Fetch.ai (FET) mainnet integration. This means Binance has integrated Native FET tokens which can be staked easily on Cosmostation without relying on token bridge. Please follow the below steps:

  • Step 1: Please head over to Cosmostation Web Wallet (Fetch.ai chain) and create an address for Fetch.ai chain. There is a detailed pdf from the Cosmostation team explaining how to create an account using ledger and how to use the web wallet.

  • Step 2: Once done, send a small amount of FET as test from your Binance account to your generated Cosmostation address. Make sure the addresses are correct and above all the chain chosen for your generated address is Fetch.ai chain on Cosmostation web wallet.

  • Step 3: If the tokens arrive, send the rest.

  • Step 4: Delegate to your preferred validators and start staking.

"},{"location":"basics/staking/different_ways_to_stake_fet/#how-to-stake-on-fetch-mainnet-using-cosmostation-mobile-wallets-applicable-for-users-holding-fet-on-binance","title":"How to stake on Fetch Mainnet using Cosmostation Mobile Wallets: applicable for users holding FET on Binance","text":"

Binance has completed the Fetch.ai (FET) mainnet integration. This means Binance has integrated Native FET tokens which can be staked easily on Cosmostation without relying on token bridge. Please follow the below steps:

  • Step 1: Please consider if you wish to stake FET using Cosmostation iOS or Android mobile apps

For Mobile users: iOS and Android.

  • Step 2: Create Cosmostation address for Fetch.ai chain. There is a detailed pdf from the Cosmostation team explaining how to create an account.

  • Step 3: Send a small amount of $FET from your Binance account to your generated cosmostation address. Make sure the addresses are correct and above all the chain chosen for your generated address is Fetch.ai chain on Cosmostation.

  • Step 4: If the tokens arrive, send the rest.

  • Step 5: Delegate to your preferred validators and start staking.

"},{"location":"basics/staking/different_ways_to_stake_fet/#stakingunstaking-on-fetch-mainnet-for-users-with-access-to-migrated-tokens","title":"STAKING/UNSTAKING ON FETCH MAINNET FOR USERS WITH ACCESS TO MIGRATED TOKENS","text":"

Our staking program has moved from Ethereum and we have successfully migrated all the tokens on September 15 \u2014 which you can access on our Fetch.ai browser wallet. If you want to access your migrated tokens, here is how you do it.

For those who staked on [staking.fetch.ai] using only Metamask \u2014 Here is the guide

For those who staked on [staking.fetch.ai] using ledger \u2014 Here is the guide (refer to Key Migration Desktop)

IMPORTANT: BE SURE TO CREATE YOUR COSMOSTATION ADDRESS ONLY FOR FETCH.AI CHAIN.

Official guide from Cosmostation team on how to generate address on Cosmostation Mobile \u2014 pdf

Official guide from Cosmostation team on how to generate address on Cosmostation Web Wallet \u2014 pdf

Here is the user journey below detailing how to stake on our Mainnet if you have your tokens on the Fetch browser wallet.

To unstake, use this guide which explains how to send tokens back to your metamask using the Fetch browser wallet.

If you need further clarification, please feel free to refer to the section Applicable for users holding FET on Binance below.

"},{"location":"basics/staking/different_ways_to_stake_fet/#stakingunstaking-on-fetch-mainnet-if-you-have-erc20-tokens-on-other-exchanges","title":"STAKING/UNSTAKING ON FETCH MAINNET IF YOU HAVE ERC20 TOKENS ON OTHER EXCHANGES","text":"

With the exception of Binance and HitBTC all other exchanges including Coinbase currently hold ERC20 FET. To stake on Mainnet, you must use our token bridge and metamask.

Please feel free to refer to the section Applicable for users on exchanges with ERC20 FET below.

To unstake, use this guide which explains how to send tokens back to your metamask using the Fetch browser wallet.

For Ledger users: Staking on Fetch Mainnet using Ledger and Cosmostation Web Wallet.

"},{"location":"basics/staking/different_ways_to_stake_fet/#how-to-stake-fet-using-ledger-on-cosmostation-web-wallet-applicable-for-users-on-exchanges-with-erc20-fet","title":"How to stake FET using Ledger on Cosmostation Web Wallet: applicable for users on exchanges with ERC20 FET","text":"

Disclaimer: The guide is a courtesy of Cros-Net who is a validator on Fetch.ai Mainnet.

What do I need:

  • Ledger Nano and a Desktop PC, as mobile devices are not yet supported.
  • Ledger Live software installed on your PC and an empty MetaMask wallet on your Brave/Chrome Browser. FET ERC20 tokens (except HitBTC all other exchanges
  • Head over here \u2014 Cosmostation web application (Cosmos Web Wallet) to stake the ERC20 Fetch coins.

This guide also assumes you have Fetch.AI tokens stored in your Ledger Nano Wallet ready to be staked.

STEP 1 (Install Ethereum and Cosmos Ledger apps)

After setting up your device with a PIN and passphrase, you should install both the Ethereum and Cosmos wallets through the Ledger Live app.

STEP 2 (Set specific Ethereum app settings correctly)

Open the Ethereum app on your Ledger wallet. Within the app, go to settings; make sure \"Contract Data\" is set to \"Allow contract data in transactions\". By default this is turned off, it must be turned on so signing the transaction later on won\u2019t fail. Now exit the app.

STEP 3 (Obtaining Fetch.ai address)

  1. Open Cosmos app on your Ledger.
  2. Via Brave/Chrome, go to Cosmos Web Wallet, Cosmostation: https://wallet.cosmostation.io/
  3. On the top right corner, drop down menu, make sure it is set to \u201cfetch.ai\u201d:
  4. Click \"Connect Wallet\". Click \"Connect to Ledger\"
  5. Your fetch.ai address will appear in the middle of the screen
  6. Make a note of this address as it will be needed later when staking
  7. Logout of Cosmostation website
  8. Close down the Cosmos App on Ledger Wallet and open up Ethereum App

STEP 4 (Set MetaMask bridge with Ledger Live Wallet)

  1. On your Brave/Chrome browser, open the MetaMask wallet. You will now link this wallet with your Ledger via a bridge. Make sure that MetaMask is set to Ethereum Mainnet.
  2. Open the MetaMask wallet and click on top right corner where it has your profile icon:
  3. Choose \"Connect Hardware Wallet\", a dialog will appear asking you to \"Open Ledger Live\"
  4. Click \"Open Ledger Live\". Ledger Live will then ask you to login (if not done so already). Ledger Live will then display \"Expose your device accounts through websocket\"
  5. Click Open to expose the device via web socket. When doing this for the first time, you will receive a message asking if you want to allow Ledger to make outside connections. Click OK on this.
  6. Once the connection is established and the bridge is set, Ledger Live will display \u201cEthereum bridge opened\u201d. Now we can communicate between the two wallets in this process.
  7. You will also notice that your MetaMask will now have two accounts on Ethereum Mainnet, one which is your original account that was setup with it. The second account is a hardware account linked to Ledger Wallet with \"Hardware\" displayed next to it.

STEP 5 (Visit the bridge to move coins onto Cosmostation)

To complete this phase, visit the bridge at https://token-bridge.fetch.ai/.

  • Make sure MetaMask is unlocked so it can be connected onto this bridge.
  • Make sure MetaMask is set to the HARDWARE wallet and that you have enough Ethereum in there to sign the transaction and pay the fees for transfer.

STEP 6 (Open Wallet and check bridge details)

The source address (Ethereum address of HARDWARE wallet) will be already filled in, and you will see a blank field for the Native address (this is your Fetch native destination address from STEP 3).

STEP 7 (Enter Fetch address details and make transfer)

Paste your address starting with \"fetch1\", that you identified in STEP 2 into the \"Native Address\" field, enter the amount, and then click the \"Transfer\" button. The Ethereum address is connected to the site through Metamask or other browser extension, and needs to be the source from which your tokens will move to Mainnet. The Fetch (Native) address is your destination address, to which they will move on main-net.

STEP 8 (Sign the transaction \u2014 first one)

This will trigger a transaction that will ask you to \"approve\" the bridge contract for holding your tokens. After signing the transaction in your Brave/Chrome browser, be sure to also sign it on your physical device (Ledger Nano), and it will be submitted to the Ethereum blockchain. Once this step is done, you can check your Ledger Live and see that the Fetch Tokens have transferred.

As the first transaction is signed, make sure the bridge between Ledger Nano and MetaMask is still running. This is not needed for the second signing below.

STEP 9 (Sign the transaction \u2014 second one)

To complete the tokens transfer, you will be asked to sign a second transaction. In case this step fails, retry the process again from Step 8 above and you will only need to sign once (since Fetch tokens would have been transferred onto Mainnet).

STEP 10 (Check tokens transferred to validator area)

After completing these steps, your Fetch address will be credited with tokens that you can delegate to a validator of your choice to start earning staking rewards. Login to Cosmostation as you did in Step 3. Your coins should be present at the centre of the dashboard.

STEP 11 (Delegating stake)

After completing the transfer onto Fetch AI Mainnet and confirming that the coins are now visible on your dashboard, it is time to stake the coins.

  1. Make sure Ledger is unlocked and Cosmos App is running.
  2. Click on Wallet and select \"Reward\" (see left hand side of screen)
  3. This will show a list of validators to delegate to. (In case you get an error when trying to open the Reward section, make sure the Cosmos App is unlocked and running on your Ledger).
  4. Validators are listed at the bottom of the screen. Choose a validator to delegate to by clicking on the \u201cDelegate\u201d button. A dialog will appear.
  5. Enter the amount of Fetch tokens you want to delegate and then click on the \u201cGenerate & Sign Transaction\u201d button.
  6. Confirm this on your Ledger wallet and your coins will be sent to the validator for staking.
  7. Repeat the process if you wish to delegate to other validators.
  8. As each validator is added, this will be displayed in the UI of the page.
"},{"location":"basics/staking/different_ways_to_stake_fet/#how-to-stake-on-fetch-mainnet-using-cosmostation-mobile-wallets-applicable-for-users-on-exchanges-with-erc20-fet","title":"How to stake on Fetch Mainnet using Cosmostation Mobile Wallets: applicable for users on exchanges with ERC20 FET","text":"

How do I stake on Mainnet 2.0 (ELI5 version) What you will need:

  • A Metamask extension for your browser
  • A Cosmostation Wallet (iOS and Android)

Steps:

  1. If you do not have the metamask extension installed on your browser. Download it and create an account. Never give out your metamask private key, never give out your mnemonics, and store your password safe.
  2. Set your metamask to receive FET. Click on add token and click on \"custom\" and add the ERC20 FET address 0xaea46A60368A7bD060eec7DF8CBa43b7EF41Ad85.
  3. To send your tokens from your exchange to Metamask \u2014 We will take Binance as an example here.

    • Go to Wallet/Overview FIAT and SPOT.
    • Withdraw your FET and ETH to your metamask account. You will need some ETH to pay for withdrawal. You can also buy ETH on Metamask directly. To withdraw your FET, copy and paste your Metamask address into the field. Always use the ERC20 network to send your FET.

    If you\u2019re on any other exchange, please send your tokens directly to Metamask.

  4. Then you wait for Metamask to receive your FET and ETH which may take a few minutes. If you are stuck, please refresh your page but be patient. If you start to get worried, head over to etherscan.io and check your tx hash. If it is successful, it is down to ETH network congestion but your tokens are on their way.

  5. Download the Cosmostation Wallet for your phone. Create an account on it. Remember to choose Fetch Mainnet when you\u2019re asked to choose a Cosmo network. Once again you will be asked to store your mnemonics so write them down and keep them somewhere safe.

  6. After you have successfully created your wallet on the Cosmostation wallet app, you can go to https://token-bridge.fetch.ai/. Ensure your metamask is connected to the token bridge. Once you have your tokens, connect your metamask with https://token-bridge.fetch.ai/. A new window will pop up and you will be prompted to approve the request to connect.

  7. Enter your metamask address in the Ethereum Address, your Cosmostation Wallet address in the Native Address field. Enter the amount of FET you wish to transfer, note that you must send a minimum of 100 FET across the bridge.

    Enter the amount \u2014 click transfer \u2014 pay for the first little transaction with ETH to approve \u2014 once the transaction is approved, a metamask pop-up should come asking to pay for the swap. Please pay the second transaction with ETH.

    If the first transaction succeeds but nothing comes on the page to pay the second transaction : just refresh the page, copy-paste your addresses again, enter again the amount, and you should see the \"swap\" button now.

  8. Once you have transferred and swapped on step \"g\", check your Cosmostation wallet and wait for a few minutes before the transferred tokens show up.

  9. Once they have arrived you can delegate them to a validator of your choice. Congratulations you have successfully staked on Fetch.ai Mainnet 2.0.

"},{"location":"basics/staking/how_to_stake/","title":"How to Stake","text":"

On this page, you can find instructions on how to stake FET, remove your stake, and claim your staking rewards.

"},{"location":"basics/staking/how_to_stake/#to-stake","title":"To Stake","text":"
  1. Ensure you are logged into your Fetch wallet.
  2. On the wallet dashboard, select Stake. You should be redirected to the ledger browser. Here you will find a list of every active validator with whom you can stake your FETs. You can also see the amount of FET staked to each validator and their commission rates.

    Info

    The validators on this page are ordered according to the number of FETs delegated to them and not their reputation or benefits. See choosing a validator for more details.

  3. Connect your wallet, if it is not yet connected, by pressing Connect Wallet at the top right.

  4. Choose a validator to stake your FETs with and hit Stake.

    Tip

    To see details of any validator, such as their voting power, self-bonded rate, uptime, active/inactive status at any given time, and contact information, head over to this page. For a visualization of the validators' voting power (more is NOT better) check out this page.

  5. In the pop-up, select the amount of FETs you want to delegate to this validator and press Stake.

    Tip

    Don't forget to leave some FETs undelegated, as some amount is necessary to pay for transaction fees when submitting any transaction to the main network. The fee is very minimal, but it is still important to make sure you have some FETs to pay for it.

  6. Your Fetch Wallet shows you a summary of the transaction. Review it, select a transaction fee, and if you are happy, hit Approve to complete the operation.

"},{"location":"basics/staking/how_to_stake/#to-claim-your-rewards","title":"To Claim your Rewards","text":""},{"location":"basics/staking/how_to_stake/#using-the-fetch-wallet","title":"Using the Fetch Wallet","text":"
  1. Ensure you are logged into your Fetch wallet.
  2. From the wallet dashboard select Claim.

    Info

    This will claim the total rewards accrued for your stakes across every validator.

  3. The wallet shows you a summary of the transaction. Review it, select a transaction fee, and if you are happy, hit Approve to complete the operation.

You should now see the rewards added to your Total Balance.

"},{"location":"basics/staking/how_to_stake/#using-the-staking-dashboard","title":"Using the Staking Dashboard","text":"
  1. Go to the ledger browser page.
  2. Connect your wallet, if it is not yet connected, by pressing Connect Wallet at the top right.
  3. Click on your wallet address at the top right of the page to go to your staking dashboard.
  4. In the Rewards section, click Claim Rewards for any validator to withdraw the rewards from your stakes with this particular validator.
  5. The wallet shows you a summary of the transaction. Review it, select a transaction fee, and if you are happy, hit Approve to complete the operation.

You should now see the rewards added to your Total Balance.

Info

Rewards are paid on a per-block basis and added to the existing pending rewards.

"},{"location":"basics/staking/how_to_stake/#to-remove-your-stake","title":"To Remove your Stake","text":"
  1. Go to the ledger browser page.
  2. Connect your wallet, if it is not yet connected, by pressing Connect Wallet at the top right.
  3. Click on your wallet address at the top right of the page to go to your staking dashboard.
  4. In the Delegations section, click Remove Stake for the validator you wish to remove your stakes from.
  5. In the pop-up, enter the amount of FETs you wish to remove from your stakes with this validator and click Remove Stake.
  6. The wallet shows you a summary of the transaction. Review it, select a transaction fee, and if you are happy, hit Approve to complete the operation.

Info

When you remove your stake, there is an unbonding (also known as, cooldown) period of 21 days. In your staking dashboard, the Unbonding Delegations section shows you the stakes you have removed which are now in the unbonding period. You can also see the amount of FET unbonded and the number of days remaining from the unbonding period. This is how long you need to wait before being able to withdraw the funds to your wallet.

"},{"location":"basics/staking/redelegation/","title":"Re-Delegation","text":"

Re-delegation means moving some or all of the stakes you delegated with one validator to another.

Note

To re-delegate, do not manually remove your stake from one validator and stake with another. This will trigger the unbonding period, causing you to miss out on staking rewards for 21 days while you wait for the unbonding period to end. Re-delegation, unlike manually removing and adding stake, is an instant process for moving some or all of your staked FETs from one validator to another.

"},{"location":"basics/staking/redelegation/#why-re-delegate","title":"Why Re-Delegate","text":"

There may be different reasons why you might choose to re-delegate and redistribute your stakes. Some examples are:

  • To increase the decentralization and therefore security of the network: For the Fetch network to be as secure as possible, there should not be a large concentration of stakes (e.g. more than 33% of the delegated FETs) staked within only a small number of (e.g. 10) validators. If you see this is currently the case and that you have also contributed to it by delegating your stakes with one of those validators, you may want to consider redistributing your stakes to some of the other validators.
  • To reduce your staking risks: Remember that when you delegate your tokens with a validator, just as you share the rewards for their contribution to the network's consensus protocol, you also share the punishment they would receive if they misbehave and act against the network's protocol. If this happens, your stake with them will be slashed. To reduce this risk, you may choose to re-delegate parts of your stakes to other validators to have a wider stake distribution.
"},{"location":"basics/staking/redelegation/#to-re-delegate-your-stake","title":"To Re-Delegate your Stake","text":"
  1. Go to the ledger browser page.
  2. Connect your wallet, if it is not yet connected, by pressing Connect Wallet at the top right.
  3. Click on your wallet address at the top right of the page to go to your staking dashboard.
  4. In the Delegations section, click Transfer Stake for the validator you wish to re-delegate some or all of your stakes from.
  5. In the pop-up, select a validator that you'd like to re-delegate your stakes to.

    Info

    The validators are not in any order on the drop-down list. To see the validators' details see validator voting power distribution and validator details.

  6. Enter the amount of FETs you wish to re-delegate to the new validator and click Transfer Stake.

  7. The wallet shows you a summary of the transaction. Review it, select a transaction fee, and if you are happy, hit Approve to complete the operation.
"},{"location":"basics/staking/redelegation/#choosing-a-validator","title":"Choosing a Validator","text":"

Choosing a validator to delegate your stake with is an important decision which ultimately impacts the network's security and performance. When you choose a validator, you are essentially casting a vote in the network indicating their trustworthiness, and that it is beneficial to have them participate in the maintenance of the network's operation.

When it comes to choosing a validator, it is up to you as the delegator to do your own research and due diligence to find out about their reputation and how well they have performed so far. Delegation is meant to be an active role between you and a validator.

To help you choose a suitable validator, here is a list of criteria to look for:

  • Do they have a website?
  • Do they have an active presence on social media (e.g. Twitter, Reddit, Instagram, ...)?
  • Are they easy to contact (e.g. on Discord, Telegram, ...)?
  • Do they have FET self-bonded to their node?
  • Are they active in the community?
  • Do they have a high uptime?
  • Do they offer slashing & double sign protection?
  • Do they participate and vote on proposals?
  • Do they have a mission or set of principles that align with yours?

Some of these questions can be answered via our Native Block Explorer or Mintscan. Some of the others on this list will require you to reach out to validators for answers, which is a good opportunity to see if they are active or not.

"},{"location":"basics/staking/what_is_staking/","title":"What is Staking?","text":"

Staking means locking up some of your tokens in order to participate in a blockchain network's operation, increase its security, and earn some passive rewards.

Fetch is a public, decentralized blockchain network which uses proof-of-stake (PoS) as its consensus protocol. The operators of PoS-based networks, known as validators, must lock up some tokens in exchange for the right to earn rewards from their work.

There are certain conditions for becoming a validator, such as staking a minimum token amount. Users with lower amounts of tokens could still participate by delegating stake to a particular validator to earn a share of the rewards that they receive. Note that the more tokens locked up, the more decision power a validator has and thus the higher the chances of them earning rewards.

"},{"location":"basics/staking/what_is_staking/#why-stake","title":"Why Stake?","text":""},{"location":"basics/staking/what_is_staking/#benefits-to-the-user","title":"Benefits to the User","text":"
  • Earn Rewards: Validators in the Fetch network participate in the maintenance of network's operation and earn rewards for their work. As someone who staked tokens with validators, you are eligible to earn a share of their rewards. Note that your stake could also be slashed in case a validator misbehaves and acts against the network's protocol. Therefore, you must do your own research when choosing validators to stake your tokens with. It is therefore often a good practice to distribute your tokens and stake with multiple validators.
  • Voting Rights: As a stakeholder, you will be eligible to vote on proposals for the future improvement of the network. The more you stake, the greater your influence and voting power.
"},{"location":"basics/staking/what_is_staking/#benefits-to-the-network","title":"Benefits to the Network","text":"
  • Decentralization and Security: The level of decentralization in any highly secure blockchain network is dependent on the number of validators involved in the consensus or block production. The larger the number of validators and the more distributed the total stakes, the more decentralized or safe the network is. As a result, it's always a good idea to distribute your stakes across several validators, not only to prevent staking risks but also to keep the network decentralized and secure.
  • Network improvements, DAO and decentralized decision-making: Having a DAO (Decentralized Autonomous Organization) make network enhancement decisions is an important aspect of any decentralized network. As a stakeholder, you have the right to vote on proposals that could be crucial for the network's improvement and use-cases in general. The greater the number of stakeholders, the more individuals vote on network upgrade proposals, resulting in highly decentralized decisions.
"},{"location":"basics/wallet/getting_started/","title":"Getting Started","text":"

The Fetch Wallet allows you to interact with the Fetch network via your browser.

"},{"location":"basics/wallet/getting_started/#compatibility","title":"Compatibility","text":"

The Fetch Wallet works on all Chromium-based web browsers, including Chrome, Brave, Edge and Decentr.

"},{"location":"basics/wallet/getting_started/#get-the-wallet","title":"Get the Wallet","text":"

Install the Fetch wallet from the Chrome web store.

Info

At this time, you cannot run the Keplr and Fetch wallets together because they interfere. Please disable the Keplr wallet before using the Fetch wallet.

"},{"location":"basics/wallet/getting_started/#set-up","title":"Set up","text":"

After opening the wallet for the first time, you will see the option to:

  • Create a new account
  • Import an existing account
  • Connect your ledger hardware wallet
"},{"location":"basics/wallet/getting_started/#to-create-a-new-account","title":"To Create a New Account","text":"
  • Select Create new account
  • Backup your mnemonic seed securely.

    Warning

    KEEP IT SAFE! Anyone with your mnemonic seed can access your wallet and take your assets.

    Danger

    DON'T LOSE IT! Lost mnemonic seed cannot be recovered! If you lose your mnemonic seed you will lose access to your wallet.

  • Give your account a name and set a password. This password will be used the next time you want to use the wallet or make important changes to your account.

  • Rearrange the mnemonic phrases by clicking on them in the correct order to confirm your mnemonic seed.
"},{"location":"basics/wallet/getting_started/#to-import-an-existing-account","title":"To Import an Existing Account","text":"

If you have an account on the Fetch network, for example having had one already on the Fetch wallet and want to access it again, have an account on another wallet (e.g. Cosmostation, Keplr, ...) and wish to bring it to the Fetch wallet, or having created an address using one of our tools (e.g. the AEA framework), you can import it into the Fetch wallet:

  • Select Import existing account
  • Enter your mnemonic seed (set of words) or private key (hexadecimal)

    Warning

    KEEP IT SAFE! Anyone with your mnemonic seed or private key can access your wallet and take your assets.

  • Give your account a name and set a password. This password will be used the next time you want to use the wallet or make important changes to your account.

"},{"location":"basics/wallet/getting_started/#to-use-ledger-hardware-wallet","title":"To Use Ledger Hardware Wallet","text":"

If you have a Ledger hardware wallet and wish to keep your key and mnemonics on that device while using the Fetch wallet:

Info

Currently only ledger hardware wallets are supported.

  • Select Import ledger
  • Give your account a name and set a password. This password will be used the next time you want to use the wallet or make important changes to your account.
  • Follow the instructions on the popup to connect your device.

Warning

Please ensure you keep your mnemonic seed somewhere safe where others cannot access it. If you lose it, your wallet will be inaccessible once you log out. The password for your account should also be kept safe but is not necessary for recovery if you have your mnemonic seed.

Info

If you lose your password, you need to uninstall and re-install the Fetch wallet and select Import existing account. Then use the mnemonic seed for your account and choose a new password.

"},{"location":"basics/wallet/how_to_use_wallet/","title":"How to Use the Wallet","text":""},{"location":"basics/wallet/how_to_use_wallet/#deposit-tokens","title":"Deposit Tokens","text":"

To transfer funds to your account on the Fetch wallet:

In the wallet or application you are using to send the funds, use your account's address as the destination account to which the funds must go.

"},{"location":"basics/wallet/how_to_use_wallet/#to-copy-your-accounts-address","title":"To Copy Your Account's Address","text":"
  1. Either click on the account address at the top of the dashboard (under the account name):
  2. Or select Deposit and scan the QR code.

Once you send the tokens, the balance should be updated.

Failure

If your origin wallet says that the address (which should start with \"fetch\") is invalid, it is probably expecting an Ethereum address (beginning with \"0x\") and is most likely trying to send ERC20 FET. In this case, you need to use the token bridge to swap your ERC20 FET for native FET.

Warning

You should not send ERC20 FET to this wallet. If you do, you will lose your tokens. The Fetch wallet can only hold native FET tokens and not ERC20 FET tokens.

"},{"location":"basics/wallet/how_to_use_wallet/#send-tokens","title":"Send Tokens","text":"

To send tokens from your account:

  1. Select Send.
  2. Fill in the details of your transaction:

    • Recipient: the address you want to send the tokens to
    • Token: the token denomination or type
    • Amount: the number of tokens you want to send with this transaction (you can see your current balance above the Amount)
    • Memo (Optional): some transactions (e.g. to/from some exchanges) require a specific memo. If not needed, you can leave it blank.
    • Fee: the transaction fee. Choose from Low, Average and High

    Tip

    Usually, the lower the transaction fee, the longer you need to wait for your transaction to be settled on the network.

  3. Press Send.

  4. In the summary screen, review the details and if everything is correct, select Approve.

Tip

You can check the status of your transaction via the explorer.

"},{"location":"colearn/","title":"Welcome to the Fetch.ai Collective Learning Library","text":"

Colearn is a library that enables privacy-preserving decentralized machine learning tasks on the FET network.

This blockchain-mediated collective learning system enables multiple stakeholders to build a shared machine learning model without needing to rely on a central authority, and without revealing their dataset to the other stakeholders. This library is currently in development.

"},{"location":"colearn/#how-collective-learning-works","title":"How collective learning works","text":"

A group of learners comes together, each of whom have their own datasets and want to collaborate on training a machine learning model over a set number of rounds. We refer to this as an 'experiment'. In each round of collective learning:

  1. One learner is selected to train the model and propose a new set of model weights.
  2. The other learners vote on whether the weights are an improvement.
  3. If the majority vote that the new weights are better than the old ones then the new weights are accepted by all the learners. Otherwise the new weights are discarded.
  4. The next round begins. For more information on the Collective Learning Protocol see here.
"},{"location":"colearn/#current-version","title":"Current Version","text":"

We have released v.0.2.8 of the Colearn Machine Learning Interface, the first version of an interface that allows developers to define their own model architectures that can then be used in collective learning. Together with the interface we provide a simple backend for local experiments. This is a prototype backend with upcoming blockchain ledger based backends to follow. Future releases will use similar interfaces so that learners built with the current system will work on a different backend that integrates a distributed ledger and provides other improvements. The current framework will then be used mainly for model development and debugging. We invite all users to experiment with the framework, develop their own models, and provide feedback!

"},{"location":"colearn/#getting-started","title":"Getting Started","text":"

To use the latest stable release we recommend installing the package from PyPi

To install with support for Keras and Pytorch:

pip install colearn[all]\n

To install with just support for Keras or Pytorch:

pip install colearn[keras]\npip install colearn[pytorch]\n

For more installation options or get the latest (development) version see Installation

Then run the standalone demo:

python -m colearn_examples.ml_interface.run_demo\n

For plenty of other examples see the Examples.

"},{"location":"colearn/#writing-your-own-models","title":"Writing your own models","text":"

We encourage users to try out the system by writing their own models. Models need to implement the collective learning interface, which provides functions for training and voting on updates. More instructions can be found in the Getting Started section.

"},{"location":"colearn/about/","title":"How collective learning works","text":"

A Colearn experiment begins when a group of entities, referred to as learners, decide on a model architecture and begin learning. Together they will train a single global model. The goal is to train a model that performs better than any of the learners can produce by training on their private data set.

"},{"location":"colearn/about/#how-training-works","title":"How Training Works","text":"

Training occurs in rounds; during each round the learners attempt to improve the performance of the global shared model. To do so each round an update of the global model (for example new set of weights in a neural network) is proposed. The learners then validate the update and decide if the new model is better than the current global model. If enough learners approve the update then the global model is updated. After an update is approved or rejected a new round begins.

The detailed steps of a round updating a global model M are as follows:

  1. One of the learners is selected and proposes a new updated model M'
  2. The rest of the learners validate M'
  3. If M' has better performance than M against their private data set then the learner votes to approve
  4. If not, the learner votes to reject
  5. The total votes are tallied
  6. If more than some threshold (typically 50%) of learners approve then M' becomes the new global model. If not, M continues to be the global model
  7. A new round begins.

By using a decentralized ledger (a blockchain) this learning process can be run in a completely decentralized, secure and auditable way. Further security can be provided by using differential privacy to avoid exposing your private data set when generating an update.

"},{"location":"colearn/about/#learning-algorithms-that-work-for-collective-learning","title":"Learning algorithms that work for collective learning","text":"

Collective learning is not just for neural networks; any learning algorithm that can be trained on subsets of the data and which can use the results of previous training rounds as the basis for subsequent rounds can be used. Neural networks fit both these constraints: training can be done on mini-batches of data and each training step uses the weights of the previous training step as its starting point. More generally, any model that is trained using mini-batch stochastic gradient descent is fine. Other algorithms can be made to work with collective learning as well. For example, a random forest can be trained iteratively by having each learner add new trees (see example in mli_random_forest_iris.py). For more discussion, see here.

"},{"location":"colearn/about/#the-driver","title":"The driver","text":"

The driver implements the voting protocol, so it handles selecting a learner to train, sending the update out for voting, calculating the vote and accepting or declining the update. Here we have a very minimal driver that doesn't use networking or a blockchain. Eventually the driver will be a smart contract. This is the code that implements one round of voting:

def run_one_round(round_index: int, learners: Sequence[MachineLearningInterface],\nvote_threshold=0.5):\nproposer = round_index % len(learners)\nnew_weights = learners[proposer].mli_propose_weights()\nprop_weights_list = [ln.mli_test_weights(new_weights) for ln in learners]\napproves = sum(1 if v.vote else 0 for v in prop_weights_list)\nvote = False\nif approves >= len(learners) * vote_threshold:\nvote = True\nfor j, learner in enumerate(learners):\nlearner.mli_accept_weights(prop_weights_list[j])\nreturn prop_weights_list, vote\n

The driver has a list of learners, and each round it selects one learner to be the proposer. The proposer does some training and proposes an updated set of weights. The driver then sends the proposed weights to each of the learners, and they each vote on whether this is an improvement. If the number of approving votes is greater than the vote threshold the proposed weights are accepted, and if not they're rejected.

"},{"location":"colearn/about/#the-machine-learning-interface","title":"The Machine Learning Interface","text":"
# ------------------------------------------------------------------------------\n#\n#   Copyright 2021 Fetch.AI Limited\n#\n#   Licensed under the Creative Commons Attribution-NonCommercial International\n#   License, Version 4.0 (the \"License\"); you may not use this file except in\n#   compliance with the License. You may obtain a copy of the License at\n#\n#       http://creativecommons.org/licenses/by-nc/4.0/legalcode\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n#\n# ------------------------------------------------------------------------------\nimport abc\nfrom enum import Enum\nfrom typing import Any, Optional\nimport onnx\nimport onnxmltools\nimport sklearn\nimport tensorflow as tf\nimport torch\nfrom pydantic import BaseModel\nfrom tensorflow import keras\nmodel_classes_keras = (tf.keras.Model, keras.Model, tf.estimator.Estimator)\nmodel_classes_scipy = (torch.nn.Module)\nmodel_classes_sklearn = (sklearn.base.ClassifierMixin)\ndef convert_model_to_onnx(model: Any):\n\"\"\"\n    Helper function to convert a ML model to onnx format\n    \"\"\"\nif isinstance(model, model_classes_keras):\nreturn onnxmltools.convert_keras(model)\nif isinstance(model, model_classes_sklearn):\nreturn onnxmltools.convert_sklearn(model)\nif 'xgboost' in model.__repr__():\nreturn onnxmltools.convert_sklearn(model)\nif isinstance(model, model_classes_scipy):\nraise Exception(\"Pytorch models not yet supported to onnx\")\nelse:\nraise Exception(\"Attempt to convert unsupported model to onnx: {model}\")\nclass DiffPrivBudget(BaseModel):\ntarget_epsilon: float\ntarget_delta: float\nconsumed_epsilon: float\nconsumed_delta: float\nclass ErrorCodes(Enum):\nDP_BUDGET_EXCEEDED = 1\nclass TrainingSummary(BaseModel):\ndp_budget: Optional[DiffPrivBudget]\nerror_code: Optional[ErrorCodes]\nclass Weights(BaseModel):\nweights: Any\ntraining_summary: Optional[TrainingSummary]\nclass DiffPrivConfig(BaseModel):\ntarget_epsilon: float\ntarget_delta: float\nmax_grad_norm: float\nnoise_multiplier: float\nclass ProposedWeights(BaseModel):\nweights: Weights\nvote_score: float\ntest_score: float\nvote: Optional[bool]\nclass ModelFormat(Enum):\nPICKLE_WEIGHTS_ONLY = 1\nONNX = 2\nclass ColearnModel(BaseModel):\nmodel_format: ModelFormat\nmodel_file: Optional[str]\nmodel: Optional[Any]\ndef deser_model(model: Any) -> onnx.ModelProto:\n\"\"\"\n    Helper function to recover a onnx model from its deserialized form\n    \"\"\"\nreturn onnx.load_model_from_string(model)\nclass MachineLearningInterface(abc.ABC):\n@abc.abstractmethod\ndef mli_propose_weights(self) -> Weights:\n\"\"\"\n        Trains the model. Returns new weights. Does not change the current weights of the model.\n        \"\"\"\npass\n@abc.abstractmethod\ndef mli_test_weights(self, weights: Weights) -> ProposedWeights:\n\"\"\"\n        Tests the proposed weights and fills in the rest of the fields\n        \"\"\"\n@abc.abstractmethod\ndef mli_accept_weights(self, weights: Weights):\n\"\"\"\n        Updates the model with the proposed set of weights\n        :param weights: The new weights\n        \"\"\"\npass\n@abc.abstractmethod\ndef mli_get_current_weights(self) -> Weights:\n\"\"\"\n        Returns the current weights of the model\n        \"\"\"\npass\n@abc.abstractmethod\ndef mli_get_current_model(self) -> ColearnModel:\n\"\"\"\n        Returns the current model\n        \"\"\"\npass \n

There are four methods that need to be implemented:

  1. propose_weights causes the model to do some training and then return a new set of weights that are proposed to the other learners. This method shouldn't change the current weights of the model - that only happens when accept_weights is called.
  2. test_weights - the models takes some new weights and returns a vote on whether the new weights are an improvement. As with propose_weights, this shouldn't change the current weights of the model - that only happens when accept_weights is called.
  3. accept_weights - the models accepts some weights that have been voted on and approved by the set of learners. The old weights of the model are discarded and replaced by the new weights.
  4. current_weights should return the current weights of the model.

For more details about directly implementing the machine learning interface see the tutorial here

"},{"location":"colearn/demo/","title":"How to run the demo","text":"

You can try collective learning for yourself using the simple demo in run_demo. This demo creates n learners for one of six learning tasks and co-ordinates the collective learning between them.

There are six potential models for the demo

  • KERAS_MNIST is the Tensorflow implementation of a small model for the standard handwritten digits recognition dataset
  • KERAS_MNIST_RESNET is the Tensorflow implementation of a Resnet model for the standard handwritten digits recognition dataset
  • KERAS_CIFAR10 is the Tensorflow implementation of the classical image recognition dataset
  • PYTORCH_XRAY is Pytorch implementation of a binary classification task that requires predicting pneumonia from images of chest X-rays. The data need to be downloaded from Kaggle
  • PYTORCH_COVID_XRAY is Pytorch implementation of a 3 class classification task that requires predicting no finding, covid or pneumonia from images of chest X-rays. This dataset is not currently publicly available.
  • FRAUD The fraud dataset consists of information about credit card transactions, and the task is to predict whether transactions are fraudulent or not. The data need to be downloaded from Kaggle

Use the -h flag to see the options:

python -m colearn_examples.ml_interface.run_demo -h\n

Arguments to run the demo:

--data_dir:       Directory containing training data, not required for MNIST and CIFAR10\n--test_dir:       Optional directory containing test data. A fraction of the training set will be used as a test set when not specified\n--model:          Model to train, options are KERAS_MNIST KERAS_MNIST_RESNET KERAS_CIFAR10 PYTORCH_XRAY PYTORCH_COVID_XRAY FRAUD\n--n_learners:     Number of individual learners\n--n_rounds:       Number of training rounds\n--vote_threshold: Minimum fraction of positive votes to accept the new model\n--train_ratio:    Fraction of training dataset to be used as test-set when no test-set is specified\n--seed:           Seed for initialising model and shuffling datasets\n--learning_rate:  Learning rate for optimiser\n--batch_size:     Size of training batch\n
"},{"location":"colearn/demo/#running-mnist","title":"Running MNIST","text":"

The simplest task to run is MNIST because the data are downloaded automatically from tensorflow_datasets. The command below runs the MNIST task with five learners for 15 rounds.

python -m colearn_examples.ml_interface.run_demo --model KERAS_MNIST --n_learners 5 --n_rounds 15\n

You should see a graph of the vote score and the test score (the score used here is categorical accuracy). The new model is accepted if the fraction of positive votes (green colour) is higher than 0.5. The new model is rejected if the fraction of negative votes (red color) is lower than 0.5.

As you can see, there are five learners, and initially they perform poorly. In round one, learner 0 is selected to propose a new set of weights.

"},{"location":"colearn/demo/#other-datasets","title":"Other datasets","text":"

To run the CIFAR10 dataset:

python -m colearn_examples.ml_interface.run_demo --model KERAS_CIFAR10 --n_learners 5 --n_rounds 15\n

The Fraud and X-ray datasets need to be downloaded from kaggle (this requires a kaggle account). To run the fraud dataset:

python -m colearn_examples.ml_interface.run_demo --model FRAUD --n_learners 5 --n_rounds 15 --data_dir ./data/fraud\n

To run the X-ray dataset:

python -m colearn_examples.ml_interface.run_demo --model PYTORCH_XRAY --n_learners 5 --n_rounds 15 --data_dir ./data/xray\n
"},{"location":"colearn/dev_notes/","title":"Developer Notes","text":"

These are some notes for developers working on the colearn code repo

"},{"location":"colearn/dev_notes/#google-cloud-storage","title":"Google Cloud Storage","text":"

To have access to the google cloud storage you need to set up your google authentication and have the $GOOGLE_APPLICATION_CREDENTIALS set up correctly. For more details ask or see the contract-learn documentation

"},{"location":"colearn/dev_notes/#build-image","title":"Build image","text":"

To build ML server image and push to google cloud use the following command:

cd docker\npython3 ./build.py --publish --allow_dirty\n# Check this worked correctly\ndocker images\n
"},{"location":"colearn/differential_privacy/","title":"What is differential privacy?","text":"

To make a machine learning system that protects privacy we first need to have a definition of what privacy is. Differential privacy (DP) is one such definition. First we need to have three concepts: the database is a collection of data about individuals (for example, their medical records), and we want to make a query about that data (for example \"How much does smoking increase someone's risk of cancer?\"). DP says that privacy is preserved if the result of the query cannot be used to determine if any particular individual is present in the database.

So if person A has their medical data in a database, and the query that we want to make on that database is \"How much does smoking increase someone's risk of cancer\" then the result of that query shouldn't disclose whether or not person A's details are in the database.

From this comes the idea of sensitivity of a query. The sensitivity of a query determines how much the result of the query depends on an individual's data. For example, the query \"How much does smoking increase the risk of cancer for adults in the UK?\" is less sensitive than the query \"How much does smoking increase the risk of cancer for men aged 50-55 in Cambridge?\" because the second query uses a smaller set of individuals.

"},{"location":"colearn/differential_privacy/#epsilon-differential-privacy","title":"Epsilon-differential privacy","text":"

EDP is a scheme for preserving differential privacy. In EDP all queries have random noise added to them, so they are no longer deterministic. So if the query was \"What fraction of people in the database are male\", and the true result is 0.5 then the results of calling this query three times might be 0.53, 0.49 and 0.51. This makes it harder to tell if an individual's data is in the database, because the effect of adding a person can't be distinguished from the effect of the random noise. Intuitively this is a bit like blurring an image: adding noise obscures personal information. The amount of personal information that is revealed isn't zero, but it is guaranteed to be below a certain threshold.

The level of privacy that is provided is controlled by the parameter epsilon; the greater epsilon is the more noise is added and the more privacy is preserved. Queries that are more sensitive have more noise added, because they reveal more information about individuals. It is important to add as little noise as possible, because adding more noise obscures the patterns that you want to extract from the data.

"},{"location":"colearn/differential_privacy/#differential-privacy-when-training-neural-networks","title":"Differential privacy when training neural networks","text":"

Each training step for a neural network can be though of as a complicated query on a database of training data. Differential privacy mechanisms tell you how much noise you need to add to guarantee a certain level of privacy. The opacus and tensorflow-privacy libraries implement epsilon-differential privacy for training neural networks for pytorch and keras respectively.

"},{"location":"colearn/differential_privacy/#how-to-use-differential-privacy-with-colearn","title":"How to use differential privacy with colearn","text":"

By using opacus and tensorflow-privacy we can make collective learning use differential privacy. The learner that is proposing weights does so using a DP-enabled optimiser.

To see an example of using this see dp_pytorch and dp_keras.

"},{"location":"colearn/examples/","title":"Examples that use Collective Learning","text":"

This is a list of examples that we've implemented to show you how to use Collective Learning locally. See and example of the gRPC server for the next step towards decentralized Colearn.

"},{"location":"colearn/examples/#mnist","title":"Mnist","text":"

Uses the standard Mnist database of handwritten images

  • mnist_keras. Uses the KerasLearner helper class. Discussed in more detail here.
  • mnist_pytorch. Uses the PytorchLearner helper class. Discussed in more detail here.
"},{"location":"colearn/examples/#fraud","title":"Fraud","text":"

The fraud dataset consists of information about credit card transactions. The task is to predict whether transactions are fraudulent or not. The data needs to be downloaded from Kaggle, and the data directory passed in with the flag --data_dir.

  • fraud_mli. Uses the MachineLearningInterface directly and detects fraud in bank transactions.
  • fraud_keras. Loads data from numpy arrays and uses KerasLearner.
"},{"location":"colearn/examples/#cifar10","title":"Cifar10","text":"

Uses the standard Cifar10 database of images

  • cifar_keras. Uses the KerasLearner helper class.
  • cifar_pytorch. Uses the PytorchLearner helper class.
"},{"location":"colearn/examples/#xray","title":"Xray","text":"

A binary classification task that requires predicting pneumonia from images of chest X-rays. The data need to be downloaded from Kaggle, and the data directory passed in with the flag --data_dir

  • xray_keras. Uses the KerasLearner helper class.
  • xray_pytorch. Uses the PytorchLearner helper class.
"},{"location":"colearn/examples/#iris","title":"Iris","text":"

Uses the standard Iris dataset. The aim of this task is to classify examples into one of three iris species based on measurements of the flower.

  • iris_random_forest. Uses the MachineLearningInterface directly and a random forest for classification.
"},{"location":"colearn/grpc_examples/","title":"Mnist gRPC Example","text":"

To run the Keras Mnist gRPC example run:

python -m colearn_examples.grpc.run_grpc_demo --n_learners 5 --dataloader_tag KERAS_MNIST --model_tag KERAS_MNIST \\\n--data_locations /tmp/mnist/0,/tmp/mnist/1,/tmp/mnist/2,/tmp/mnist/3,/tmp/mnist/4\n

Note

This requires colearn[keras]

You can verify that the example is working correctly by running the probe:

python -m colearn_grpc.scripts.probe_grpc_server --port 9995\n

For more about the gRPC components of Colearn see the gRPC Tutorial

"},{"location":"colearn/grpc_tutorial/","title":"gRPC tutorial","text":"

This tutorial explains how to set up the gRPC learner server. It assumes that you can already run colearn locally, and that you have already defined your own models and dataloaders (if you're going to do so). If you haven't done this then see the tutorials in the Getting Started section.

"},{"location":"colearn/grpc_tutorial/#architecture-of-colearn","title":"Architecture of colearn","text":"

There are two main parts to a collective learning system: the learner and the backend. The backend controls the learner, and manages the smart contracts and IPFS, and acts as a control hub for all the associated learners. The learner is the part that executes machine learning code. This consists of proposing, evaluating and accepting new weights as detailed in the Machine Learning Interface. The learner and the backend communicate via gRPC; the learner runs a gRPC server, and the backend runs a gRPC client that makes requests of the learner. This separation means that the learner can run on specialised hardware (e.g. a compute server) and does not need to be co-located with the backend.

"},{"location":"colearn/grpc_tutorial/#architecture-of-grpc-server","title":"Architecture of gRPC server","text":"

The gRPC interface is defined in colearn_grpc/proto/interface.proto. This defines the functions that the gRPC server exposes and the format for messages between the server and the client.

As we covered in the earlier tutorials, the machine learning part of colearn is contained inside the MachineLearningInterface (MLI). To recap: the MLI provides methods for proposing, evaluating and accepting weights. If you want to use your own models with colearn then you need to write an object that implements the MLI (for example, an instance of a python class that inherits from MachineLearningInterface). For more about the MLI see the MLI tutorial.

The gRPC server has an MLI factory, and it uses its MLI factory to make objects that implement the MachineLearningInterface. The MLI factory needs to implement the MLI factory interface. You could write your own MLI factory, but it's easier to use the one we provide. Below we will discuss the MLI factory interface and then talk about how to use the example factory.

"},{"location":"colearn/grpc_tutorial/#mli-factory-interface","title":"MLI Factory interface","text":"

The MLI Factory (as the name suggests) is a factory class for creating objects that implement the machine learning interface:

# ------------------------------------------------------------------------------\n#\n#   Copyright 2021 Fetch.AI Limited\n#\n#   Licensed under the Creative Commons Attribution-NonCommercial International\n#   License, Version 4.0 (the \"License\"); you may not use this file except in\n#   compliance with the License. You may obtain a copy of the License at\n#\n#       http://creativecommons.org/licenses/by-nc/4.0/legalcode\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n#\n# ------------------------------------------------------------------------------\nimport abc\nfrom typing import Dict, Set, Any\nimport os.path\nfrom pkg_resources import get_distribution, DistributionNotFound\nfrom colearn.ml_interface import MachineLearningInterface\nclass MliFactory(abc.ABC):\n\"\"\"\n    Interface a class must implement to be used as a factory by the GRPC Server\n    \"\"\"\n_version = \"0.0.0\"\n# https://stackoverflow.com/questions/17583443\ntry:\n_dist = get_distribution('colearn')\n# Normalize case for Windows systems\ndist_loc = os.path.normcase(_dist.location)\nhere = os.path.normcase(__file__)\nif not here.startswith(os.path.join(dist_loc, 'colearn')):\n# not installed, but there is another version that *is*\nraise DistributionNotFound\nexcept DistributionNotFound:\npass\nelse:\n_version = _dist.version\ndef get_version(self) -> str:\n\"\"\"\n        Returns the version of this library....\n        \"\"\"\nreturn self._version\n@abc.abstractmethod\ndef get_models(self) -> Dict[str, Dict[str, Any]]:\n\"\"\"\n        Returns the models this factory produces.\n        The key is the name of the model and the values are their default parameters\n        \"\"\"\npass\n@abc.abstractmethod\ndef get_dataloaders(self) -> Dict[str, Dict[str, Any]]:\n\"\"\"\n        Returns the dataloaders this factory produces.\n        The key is the name of the dataloader and the values are their default parameters\n        \"\"\"\npass\n@abc.abstractmethod\ndef get_compatibilities(self) -> Dict[str, Set[str]]:\n\"\"\"\n        A model is compatible with a dataloader if they can be used together to\n        construct a MachineLearningInterface with the get_MLI function.\n        Returns a dictionary that defines which model is compatible\n        with which dataloader.\n        \"\"\"\npass\n@abc.abstractmethod\ndef get_mli(self,\nmodel_name: str, model_params: str,\ndataloader_name: str, dataset_params: str) -> MachineLearningInterface:\n\"\"\"\n        @param model_name: name of a model, must be in the set return by get_models\n        @param model_params: user defined parameters for the model\n        @param dataloader_name: name of a dataloader to be used:\n            - must be in the set returned by get_dataloaders\n            - must be compatible with model_name as defined by get_compatibilities\n        @param dataset_params: user defined parameters for the dataset\n        @return: Instance of MachineLearningInterface\n        Constructs an object that implements MachineLearningInterface whose\n        underlying model is model_name and dataset is loaded by dataloader_name.\n        \"\"\"\npass \n

The MLI Factory stores the constructors for dataloaders and models and also a list of the dataloaders that are compatible with each model. Each constructor is stored under a specific name. For example, \"KERAS_MNIST_MODEL\" is the model for keras mnist. The gRPC server uses the MLI factory to construct MLI objects. The MLI Factory needs to implement four methods:

  • get_models - returns the names of the models that are registered with the factory and their parameters.
  • get_dataloaders - returns the names of the dataloaders that are registered with the factory and their parameters.
  • get_compatibilities - returns a list of dataloaders for each model that can be used with that model.
  • get_mli - takes the name and parameters for the model and dataloader and constructs the MLI object. Returns the MLI object.
"},{"location":"colearn/grpc_tutorial/#using-the-example-mli-factory","title":"Using the example MLI Factory","text":"

The example MLI factory is defined in colearn_grpc/example_mli_factory.py. It stores the models and dataloaders that it knows about in factoryRegistry.py To add a new model and dataloader to the factory you need to do the following things:

  1. Define a function that loads the dataset given the location of the dataset.
  2. Define a function that takes in the dataset and loads the MLI model.
  3. Register both these functions with the factory registry.

Registering a dataloader looks like this:

@FactoryRegistry.register_dataloader(dataloader_tag)\ndef prepare_data_loaders(location: str,\ntrain_ratio: float = 0.9,\nbatch_size: int = 32) -> Tuple[PrefetchDataset, PrefetchDataset]:\n

Registering a model is similar, but you additionally have to specify the dataloaders that this model is compatible with.

@FactoryRegistry.register_model_architecture(model_tag, [dataloader_tag])\ndef prepare_learner(data_loaders: Tuple[PrefetchDataset, PrefetchDataset],\nsteps_per_epoch: int = 100,\nvote_batches: int = 10,\nlearning_rate: float = 0.001\n) -> KerasLearner:\n

You can see an example of how to do this in colearn_examples/grpc/mnist_grpc.py. The FactoryRegistry decorators get evaluated when the functions are imported, so ensure that the functions are imported before constructing the gRPC server (more on that later).

Constraints on the dataloader function:

  1. The first parameter should be a mandatory parameter called \"location\" which stores the location of the dataset.
  2. The subsequent parameters should have default arguments.
  3. The return type should be specified with a type annotation, and this should be the same type that is expected by the model functions that use this dataloader.
  4. The arguments that you pass to the dataloader function must be JSON-encodable. Native python types are fine (e.g. str, dict, list, float).

Constraints on the model function:

  1. The first parameter should be a mandatory parameter called \"data_loaders\". This must have the same type as the return type of the compatible dataloaders.
  2. The subsequent parameters should have default arguments.
  3. The return type of model_function should be MachineLearningInterface or a subclass of it (e.g. KerasLearner).
  4. The dataloaders listed as being compatible with the model should already be registered with FactoryRegistry before the model is registered.
  5. The arguments that you pass to the model function must be JSON-encodable. Native python types are fine (e.g. str, dict, list, float).
"},{"location":"colearn/grpc_tutorial/#making-it-all-work-together","title":"Making it all work together","text":"

It can be challenging to ensure that all the parts talk to each other, so we have provided some examples and helper scripts. It is recommended to first make an all-in-one script following the example of colearn_examples/grpc/mnist_grpc.py. Once this is working you can run colearn_grpc/scripts/run_n_servers.py or colearn_grpc/scripts/run_grpc_server.py to run the server(s). The script colearn_grpc/scripts/probe_grpc_server.py will connect to a gRPC server and print the dataloaders and models that are registered on it (pass in the address as a parameter). The client side of the gRPC communication can then be run using colearn_examples/grpc/run_grpc_demo.py. More details are given below.

A note about running tensorflow in multiple processes: on a system with a GPU, tensorflow will try to get all the GPU memory when it starts up. This means that running tensorflow in multiple processes on the same machine will fail. To prevent this happening, tensorflow should be told to use only the CPU by setting the environment variable CUDA_VISIBLE_DEVIES to -1. This can be done in a python script (before importing tensorflow) by using:

import os\nos.environ[\"CUDA_VISIBLE_DEVICES\"] = \"-1\"\n
"},{"location":"colearn/grpc_tutorial/#testing-locally-with-an-all-in-one-script","title":"Testing locally with an all-in-one script","text":"

You can test this locally by following the example in colearn_examples/grpc/mnist_grpc.py. Define your dataloader and model functions as specified above, and register them with the factory. Then create n_learners gRPC servers:

n_learners = 5\nfirst_server_port = 9995\n# make n servers\nfor i in range(n_learners):\nport = first_server_port + i\nserver = GRPCServer(mli_factory=ExampleMliFactory(),\nport=port)\nserver_process = Process(target=server.run)\nserver_process.start()\n

And then create n_learners gRPC clients:

all_learner_models = []\nfor i in range(n_learners):\nport = first_server_port + i\nml_system = ExampleGRPCLearnerClient(f\"client {i}\", f\"127.0.0.1:{port}\")\nml_system.start()\ndataloader_params = {\"location\": data_folders[i]}\nml_system.setup_ml(dataset_loader_name=dataloader_tag,\ndataset_loader_parameters=json.dumps(dataloader_params),\nmodel_arch_name=model_tag,\nmodel_parameters=json.dumps({}))\nall_learner_models.append(ml_system)\n

ExampleGRPCLearnerClient inherits from the MachineLearningInterface so you can use it with the training functions as before:

for round_index in range(n_rounds):\nresults.data.append(\ncollective_learning_round(all_learner_models,\nvote_threshold, round_index)\n)\n
"},{"location":"colearn/grpc_tutorial/#testing-remotely","title":"Testing remotely","text":"

We expect that the gRPC learner part will often be on a compute cluster and be separate from the gRPC client side. To test the gRPC in a setup like this you can start the servers on the computer side and the client part separately. For one gRPC server:

python3 ./colearn_grpc/scripts/run_grpc_server.py --port 9995 --metrics_port 9091\n

For multiple gRPC servers:

python3 ./colearn_grpc/scrips/run_n_grpc_servers.py --n_learners 5 --port 9995 --metrics_port 9091\n

The servers by default will start on port 9995 and use subsequent ports from there, so if three servers are required they will run on ports 9995, 9996 and 9997.

If you have written your own dataloaders and models then you need to make sure that those functions are defined or imported before the server is created. These are the imports of the default dataloaders and models in colearn_grpc/scripts/run_grpc_server.py:

# These are imported so that they are registered in the FactoryRegistry\nimport colearn_keras.keras_mnist\nimport colearn_keras.keras_cifar10\nimport colearn_pytorch.pytorch_xray\nimport colearn_pytorch.pytorch_covid_xray\nimport colearn_other.fraud_dataset\n

Once the gRPC server(s) are running, set up whatever networking and port forwarding is required. You can check that the gRPC server is accessible by using the probe script:

python3 ./colearn_grpc/scripts/probe_grpc_server.py --port 9995\n

If the connection is successful this will print a list of the models and datasets registered on the server. These are the defaults that are registered:

info: Attempt number 0 to connect to 127.0.0.1:9995\ninfo: Successfully connected to 127.0.0.1:9995!\n{'compatibilities': {'FRAUD': ['FRAUD'],\n                     'KERAS_CIFAR10': ['KERAS_CIFAR10'],\n                     'KERAS_MNIST': ['KERAS_MNIST'],\n                     'KERAS_MNIST_RESNET': ['KERAS_MNIST'],\n                     'PYTORCH_COVID_XRAY': ['PYTORCH_COVID_XRAY'],\n                     'PYTORCH_XRAY': ['PYTORCH_XRAY']},\n 'data_loaders': {'FRAUD': '{\"train_ratio\": 0.8}',\n                  'KERAS_CIFAR10': '{\"train_ratio\": 0.9, \"batch_size\": 32}',\n                  'KERAS_MNIST': '{\"train_ratio\": 0.9, \"batch_size\": 32}',\n                  'PYTORCH_COVID_XRAY': '{\"train_ratio\": 0.8, \"batch_size\": 8, '\n                                        '\"no_cuda\": false}',\n                  'PYTORCH_XRAY': '{\"test_location\": null, \"train_ratio\": 0.96, '\n                                  '\"batch_size\": 8, \"no_cuda\": false}'},\n 'model_architectures': {'FRAUD': '{}',\n                         'KERAS_CIFAR10': '{\"steps_per_epoch\": 100, '\n                                          '\"vote_batches\": 10, '\n                                          '\"learning_rate\": 0.001}',\n                         'KERAS_MNIST': '{\"steps_per_epoch\": 100, '\n                                        '\"vote_batches\": 10, \"learning_rate\": '\n                                        '0.001}',\n                         'KERAS_MNIST_RESNET': '{\"steps_per_epoch\": 100, '\n                                               '\"vote_batches\": 10, '\n                                               '\"learning_rate\": 0.001}',\n                         'PYTORCH_COVID_XRAY': '{\"learning_rate\": 0.001, '\n                                               '\"steps_per_epoch\": 40, '\n                                               '\"vote_batches\": 10, \"no_cuda\": '\n                                               'false, \"vote_on_accuracy\": '\n                                               'true}',\n                         'PYTORCH_XRAY': '{\"learning_rate\": 0.001, '\n                                         '\"steps_per_epoch\": 40, '\n                                         '\"vote_batches\": 10, \"no_cuda\": '\n                                         'false, \"vote_on_accuracy\": true}'}}\n

Then run python -m colearn_examples.grpc.run_grpc_demo on the other side to run the usual demo. The script takes as arguments the model name and dataset name that should be run, along with the number of learners and the data location for each learner.

python -m colearn_examples.grpc.run_grpc_demo --n_learners 5 --dataloader_tag KERAS_MNIST --model_tag KERAS_MNIST \\\n--data_locations /tmp/mnist/0,/tmp/mnist/1,/tmp/mnist/2,/tmp/mnist/3,/tmp/mnist/4\n
"},{"location":"colearn/grpc_tutorial/#using-the-mli-factory-interface","title":"Using the MLI Factory interface","text":"

An alternative method of using your own dataloaders and models with the gRPC server is to use the MLI Factory interface. This is defined in colearn_grpc/mli_factory_interface.py. An example is given in colearn_examples/grpc/mlifactory_grpc_mnist.py. The MLI Factory is implemented as shown:

dataloader_tag = \"KERAS_MNIST_EXAMPLE_DATALOADER\"\nmodel_tag = \"KERAS_MNIST_EXAMPLE_MODEL\"\nclass SimpleFactory(MliFactory):\ndef get_dataloaders(self) -> Dict[str, Dict[str, Any]]:\nreturn {dataloader_tag: dict(train_ratio=0.9,\nbatch_size=32)}\ndef get_models(self) -> Dict[str, Dict[str, Any]]:\nreturn {model_tag: dict(steps_per_epoch=100,\nvote_batches=10,\nlearning_rate=0.001)}\ndef get_compatibilities(self) -> Dict[str, Set[str]]:\nreturn {model_tag: {dataloader_tag}}\ndef get_mli(self, model_name: str, model_params: str, dataloader_name: str,\ndataset_params: str) -> MachineLearningInterface:\ndataloader_params = json.loads(dataset_params)\ndata_loaders = prepare_data_loaders(**dataloader_params)\nmodel_params = json.loads(model_params)\nmli_model = prepare_learner(data_loaders=data_loaders, **model_params)\nreturn mli_model\n

An instance of the SimpleFactory class needs to be passed to the gRPC server on creation:

n_learners = 5\nfirst_server_port = 9995\n# make n servers\nserver_processes = []\nfor i in range(n_learners):\nport = first_server_port + i\nserver = GRPCServer(mli_factory=SimpleFactory(),\nport=port)\nserver_process = Process(target=server.run)\nprint(\"starting server\", i)\nserver_process.start()\nserver_processes.append(server_process)\n

The rest of the example follows the grpc_mnist.py example.

"},{"location":"colearn/installation/","title":"Installation","text":"

The core package, colearn, contains only the MachineLearningInterface and a simple driver that implements the Collective Learning Protocol. To install only the core package:

pip install colearn\n

To make collective learning easier to use we have defined extra packages with helpers for model development in Keras and Pytorch.

To install with Keras/Pytorch extras:

pip install colearn[keras]\npip install colearn[pytorch]\n

To install both the Keras and Pytorch extras use:

pip install colearn[all]\n

To run stand-alone examples:

 python -m colearn_examples.ml_interface.run_demo\n

For more examples see the Examples Page

"},{"location":"colearn/installation/#installing-from-source","title":"Installing From Source","text":"

Alternatively, to install the latest code from the repo:

  1. Download the source code from github:
git clone https://github.com/fetchai/colearn.git && cd colearn\n
  1. Create and launch a clean virtual environment with Python 3.7. (This library has currently only been tested with Python 3.7).
pipenv --python 3.7 && pipenv shell\n
  1. Install the package from source:

    pip install -e .[all]\n
  2. Run one of the examples:

    python colearn_examples/ml_interface/pytorch_mnist.py\n

If you are developing the colearn library then install it in editable mode so that new changes are effective immediately:

pip install -e .[all]\n
"},{"location":"colearn/installation/#running-the-tests","title":"Running the tests","text":"

Tests can be run with:

tox\n
"},{"location":"colearn/installation/#documentation","title":"Documentation","text":"

To run the documentation, first install mkdocs and plugins:

pip install .[docs] 

Then run:

mkdocs serve\n
"},{"location":"colearn/intro_tutorial_keras/","title":"Using collective learning with keras","text":"

This tutorial is a simple guide to trying out the collective learning protocol with your own machine learning code. Everything runs locally.

The most flexible way to use the collective learning backends is to make a class that implements the Collective Learning MachineLearningInterface defined in ml_interface.py. For more details on how to use the MachineLearningInterface see here

However, the simpler way is to use one of the helper classes that we have provided that implement most of the interface for popular ML libraries. In this tutorial we are going to walk through using the KerasLearner. First we are going to define the model architecture, then we are going to load the data and configure the model, and then we will run Collective Learning.

A standard script for machine learning with Keras looks like the one below

# ------------------------------------------------------------------------------\n#\n#   Copyright 2021 Fetch.AI Limited\n#\n#   Licensed under the Creative Commons Attribution-NonCommercial International\n#   License, Version 4.0 (the \"License\"); you may not use this file except in\n#   compliance with the License. You may obtain a copy of the License at\n#\n#       http://creativecommons.org/licenses/by-nc/4.0/legalcode\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n#\n# ------------------------------------------------------------------------------\nimport tensorflow as tf\nimport tensorflow_datasets as tfds\nfrom colearn_keras.utils import normalize_img\nn_rounds = 20\nwidth = 28\nheight = 28\nn_classes = 10\nl_rate = 0.001\nbatch_size = 64\n# Load the data\ntrain_dataset, info = tfds.load('mnist', split='train', as_supervised=True, with_info=True)\nn_train = info.splits['train'].num_examples\ntest_dataset = tfds.load('mnist', split='test', as_supervised=True)\ntrain_dataset = train_dataset.map(normalize_img,\nnum_parallel_calls=tf.data.experimental.AUTOTUNE)\ntrain_dataset = train_dataset.shuffle(n_train)\ntrain_dataset = train_dataset.batch(batch_size)\ntest_dataset = test_dataset.map(normalize_img,\nnum_parallel_calls=tf.data.experimental.AUTOTUNE)\ntest_dataset = test_dataset.batch(batch_size)\n# Define the model\ninput_img = tf.keras.Input(shape=(width, height, 1), name=\"Input\")\nx = tf.keras.layers.Conv2D(64, (3, 3), activation=\"relu\", padding=\"same\", name=\"Conv1_1\")(input_img)\nx = tf.keras.layers.BatchNormalization(name=\"bn1\")(x)\nx = tf.keras.layers.MaxPooling2D((2, 2), name=\"pool1\")(x)\nx = tf.keras.layers.Conv2D(128, (3, 3), activation=\"relu\", padding=\"same\", name=\"Conv2_1\")(x)\nx = tf.keras.layers.BatchNormalization(name=\"bn4\")(x)\nx = tf.keras.layers.MaxPooling2D((2, 2), name=\"pool2\")(x)\nx = tf.keras.layers.Flatten(name=\"flatten\")(x)\nx = tf.keras.layers.Dense(n_classes, activation=\"softmax\", name=\"fc1\")(x)\nmodel = tf.keras.Model(inputs=input_img, outputs=x)\nopt = tf.keras.optimizers.Adam(lr=l_rate)\nmodel.compile(\nloss=\"sparse_categorical_crossentropy\",\nmetrics=[tf.keras.metrics.SparseCategoricalAccuracy()],\noptimizer=opt)\n# Train and evaluate model\nfor round in range(n_rounds):\nmodel.fit(train_dataset, steps_per_epoch=40)\nresult = model.evaluate(x=test_dataset, return_dict=True, steps=10)\nprint(f\"Performance at round {round} is {result}\")\n

There are three steps:

  1. Load the data
  2. Define the model
  3. Train the model

In this tutorial we are going to see how to modify each step to use collective learning. We'll end up with code like this:

# ------------------------------------------------------------------------------\n#\n#   Copyright 2021 Fetch.AI Limited\n#\n#   Licensed under the Creative Commons Attribution-NonCommercial International\n#   License, Version 4.0 (the \"License\"); you may not use this file except in\n#   compliance with the License. You may obtain a copy of the License at\n#\n#       http://creativecommons.org/licenses/by-nc/4.0/legalcode\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n#\n# ------------------------------------------------------------------------------\nimport os\nimport tensorflow as tf\nimport tensorflow_datasets as tfds\nfrom colearn.training import initial_result, collective_learning_round, set_equal_weights\nfrom colearn.utils.plot import ColearnPlot\nfrom colearn.utils.results import Results, print_results\nfrom colearn_keras.keras_learner import KerasLearner\nfrom colearn_keras.utils import normalize_img\n\"\"\"\nMNIST training example using Keras\nUsed dataset:\n- MNIST is set of 60 000 black and white hand written digits images of size 28x28x1 in 10 classes\nWhat script does:\n- Loads MNIST dataset from Keras\n- Sets up a Keras learner\n- Randomly splits dataset between multiple learners\n- Does multiple rounds of learning process and displays plot with results\n\"\"\"\nn_learners = 5\nvote_threshold = 0.5\nvote_batches = 2\ntesting_mode = bool(os.getenv(\"COLEARN_EXAMPLES_TEST\", \"\"))  # for testing\nn_rounds = 20 if not testing_mode else 1\nwidth = 28\nheight = 28\nn_classes = 10\nl_rate = 0.001\nbatch_size = 64\n# Load data for each learner\ntrain_dataset, info = tfds.load('mnist', split='train', as_supervised=True, with_info=True)\nn_datapoints = info.splits['train'].num_examples\ntrain_datasets = [train_dataset.shard(num_shards=n_learners, index=i) for i in range(n_learners)]\ntest_dataset = tfds.load('mnist', split='test', as_supervised=True)\nvote_datasets = [test_dataset.shard(num_shards=2 * n_learners, index=i) for i in range(n_learners)]\ntest_datasets = [test_dataset.shard(num_shards=2 * n_learners, index=i) for i in range(n_learners, 2 * n_learners)]\nfor i in range(n_learners):\ntrain_datasets[i] = train_datasets[i].map(\nnormalize_img, num_parallel_calls=tf.data.experimental.AUTOTUNE)\ntrain_datasets[i] = train_datasets[i].shuffle(n_datapoints // n_learners)\ntrain_datasets[i] = train_datasets[i].batch(batch_size)\nvote_datasets[i] = vote_datasets[i].map(\nnormalize_img, num_parallel_calls=tf.data.experimental.AUTOTUNE)\nvote_datasets[i] = vote_datasets[i].batch(batch_size)\ntest_datasets[i] = test_datasets[i].map(\nnormalize_img, num_parallel_calls=tf.data.experimental.AUTOTUNE)\ntest_datasets[i] = test_datasets[i].batch(batch_size)\n# Define model\ndef get_model():\ninput_img = tf.keras.Input(\nshape=(width, height, 1), name=\"Input\"\n)\nx = tf.keras.layers.Conv2D(\n64, (3, 3), activation=\"relu\", padding=\"same\", name=\"Conv1_1\"\n)(input_img)\nx = tf.keras.layers.BatchNormalization(name=\"bn1\")(x)\nx = tf.keras.layers.MaxPooling2D((2, 2), name=\"pool1\")(x)\nx = tf.keras.layers.Conv2D(\n128, (3, 3), activation=\"relu\", padding=\"same\", name=\"Conv2_1\"\n)(x)\nx = tf.keras.layers.BatchNormalization(name=\"bn4\")(x)\nx = tf.keras.layers.MaxPooling2D((2, 2), name=\"pool2\")(x)\nx = tf.keras.layers.Flatten(name=\"flatten\")(x)\nx = tf.keras.layers.Dense(\nn_classes, activation=\"softmax\", name=\"fc1\"\n)(x)\nmodel = tf.keras.Model(inputs=input_img, outputs=x)\nopt = tf.keras.optimizers.Adam(lr=l_rate)\nmodel.compile(\nloss=\"sparse_categorical_crossentropy\",\nmetrics=[tf.keras.metrics.SparseCategoricalAccuracy()],\noptimizer=opt)\nreturn model\nall_learner_models = []\nfor i in range(n_learners):\nall_learner_models.append(KerasLearner(\nmodel=get_model(),\ntrain_loader=train_datasets[i],\nvote_loader=vote_datasets[i],\ntest_loader=test_datasets[i],\ncriterion=\"sparse_categorical_accuracy\",\nminimise_criterion=False,\nmodel_evaluate_kwargs={\"steps\": vote_batches},\n))\nset_equal_weights(all_learner_models)\n# Train the model using Collective Learning\nresults = Results()\nresults.data.append(initial_result(all_learner_models))\nplot = ColearnPlot(score_name=all_learner_models[0].criterion)\nfor round_index in range(n_rounds):\nresults.data.append(\ncollective_learning_round(all_learner_models,\nvote_threshold, round_index)\n)\nprint_results(results)\nplot.plot_results_and_votes(results)\nplot.block()\nprint(\"Colearn Example Finished!\")\n

The first thing is to modify the data loading code. Each learner needs to have their own training and testing set from the data. This is easy to do with keras:

train_datasets = [train_dataset.shard(num_shards=n_learners, index=i) for i in range(n_learners)]\n

The model definition is very similar too, except that each learner will need its own copy of the model, so we've moved it into a function.

To use collective learning, we need to create an object that implements the MachineLearningInterface. To make it easier to use the MachineLearningInterface with keras, we've defined KerasLearner. KerasLearner implements standard training and evaluation routines as well as the MachineLearningInterface methods.

# ------------------------------------------------------------------------------\n#\n#   Copyright 2021 Fetch.AI Limited\n#\n#   Licensed under the Creative Commons Attribution-NonCommercial International\n#   License, Version 4.0 (the \"License\"); you may not use this file except in\n#   compliance with the License. You may obtain a copy of the License at\n#\n#       http://creativecommons.org/licenses/by-nc/4.0/legalcode\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n#\n# ------------------------------------------------------------------------------\nfrom inspect import signature\nfrom typing import Optional\ntry:\nimport tensorflow as tf\nexcept ImportError:\nraise Exception(\"Tensorflow is not installed. To use the tensorflow/keras \"\n\"add-ons please install colearn with `pip install colearn[keras]`.\")\nfrom tensorflow import keras\nfrom colearn.ml_interface import MachineLearningInterface, Weights, ProposedWeights, ColearnModel, ModelFormat, convert_model_to_onnx\nfrom colearn.ml_interface import DiffPrivBudget, DiffPrivConfig, TrainingSummary, ErrorCodes\nfrom tensorflow_privacy.privacy.analysis.compute_dp_sgd_privacy import compute_dp_sgd_privacy\nfrom tensorflow_privacy.privacy.optimizers.dp_optimizer_keras import make_keras_optimizer_class\nclass KerasLearner(MachineLearningInterface):\n\"\"\"\n    Tensorflow Keras learner implementation of machine learning interface\n    \"\"\"\ndef __init__(self, model: keras.Model,\ntrain_loader: tf.data.Dataset,\nvote_loader: tf.data.Dataset,\ntest_loader: Optional[tf.data.Dataset] = None,\nneed_reset_optimizer: bool = True,\nminimise_criterion: bool = True,\ncriterion: str = 'loss',\nmodel_fit_kwargs: Optional[dict] = None,\nmodel_evaluate_kwargs: Optional[dict] = None,\ndiff_priv_config: Optional[DiffPrivConfig] = None):\n\"\"\"\n        :param model: Keras model used for training\n        :param train_loader: Training dataset\n        :param test_loader: Optional test set. Subset of training set will be used if not specified.\n        :param need_reset_optimizer: True to clear optimizer history before training, False to kepp history.\n        :param minimise_criterion: Boolean - True to minimise value of criterion, False to maximise\n        :param criterion: Function to measure model performance\n        :param model_fit_kwargs: Arguments to be passed on model.fit function call\n        :param model_evaluate_kwargs: Arguments to be passed on model.evaluate function call\n        :param diff_priv_config: Contains differential privacy (dp) budget related configuration\n        \"\"\"\nself.model: keras.Model = model\nself.train_loader: tf.data.Dataset = train_loader\nself.vote_loader: tf.data.Dataset = vote_loader\nself.test_loader: Optional[tf.data.Dataset] = test_loader\nself.need_reset_optimizer = need_reset_optimizer\nself.minimise_criterion: bool = minimise_criterion\nself.criterion = criterion\nself.model_fit_kwargs = model_fit_kwargs or {}\nself.diff_priv_config = diff_priv_config\nself.cumulative_epochs = 0\nif self.diff_priv_config is not None:\nself.diff_priv_budget = DiffPrivBudget(\ntarget_epsilon=self.diff_priv_config.target_epsilon,\ntarget_delta=self.diff_priv_config.target_delta,\nconsumed_epsilon=0.0,\n# we will always use the highest available delta now\nconsumed_delta=self.diff_priv_config.target_delta\n)\nif 'epochs' in self.model_fit_kwargs.keys():\nself.epochs_per_proposal = self.model_fit_kwargs['epochs']\nelse:\nself.epochs_per_proposal = signature(self.model.fit).parameters['epochs'].default\nif model_fit_kwargs:\n# check that these are valid kwargs for model fit\nsig = signature(self.model.fit)\ntry:\nsig.bind_partial(**self.model_fit_kwargs)\nexcept TypeError:\nraise Exception(\"Invalid arguments for model.fit\")\nself.model_evaluate_kwargs = model_evaluate_kwargs or {}\nif model_evaluate_kwargs:\n# check that these are valid kwargs for model evaluate\nsig = signature(self.model.evaluate)\ntry:\nsig.bind_partial(**self.model_evaluate_kwargs)\nexcept TypeError:\nraise Exception(\"Invalid arguments for model.evaluate\")\nself.vote_score: float = self.test(self.vote_loader)\ndef reset_optimizer(self):\n\"\"\"\n        Recompiles the Keras model. This way the optimizer history get erased,\n        which is needed before a new training round, otherwise the outdated history is used.\n        \"\"\"\ncompile_args = self.model._get_compile_args()  # pylint: disable=protected-access\nopt_config = self.model.optimizer.get_config()\nif self.diff_priv_config is not None:\n# tensorflow_privacy optimizers get_config() miss the additional parameters\n# was fixed here: https://github.com/tensorflow/privacy/commit/49db04e3561638fc02795edb5774d322cdd1d7d1\n# but it is not yet in the stable version, thus I need here to do the same.\nopt_config.update({\n'l2_norm_clip': self.model.optimizer._l2_norm_clip,  # pylint: disable=protected-access\n'noise_multiplier': self.model.optimizer._noise_multiplier,  # pylint: disable=protected-access\n'num_microbatches': self.model.optimizer._num_microbatches,  # pylint: disable=protected-access\n})\nnew_opt = make_keras_optimizer_class(\ngetattr(keras.optimizers, opt_config['name'])\n).from_config(opt_config)\ncompile_args['optimizer'] = new_opt\nelse:\ncompile_args['optimizer'] = getattr(keras.optimizers,\nopt_config['name']).from_config(opt_config)\nself.model.compile(**compile_args)\ndef mli_propose_weights(self) -> Weights:\n\"\"\"\n        Trains model on training set and returns new weights after training\n        - Current model is reverted to original state after training\n        :return: Weights after training\n        \"\"\"\ncurrent_weights = self.mli_get_current_weights()\nif self.diff_priv_config is not None:\nepsilon_after_training = self.get_privacy_budget()\nif epsilon_after_training > self.diff_priv_budget.target_epsilon:\nreturn Weights(\nweights=current_weights,\ntraining_summary=TrainingSummary(\ndp_budget=self.diff_priv_budget,\nerror_code=ErrorCodes.DP_BUDGET_EXCEEDED\n)\n)\nself.train()\nnew_weights = self.mli_get_current_weights()\nself.set_weights(current_weights)\nif self.diff_priv_config is not None:\nself.diff_priv_budget.consumed_epsilon = epsilon_after_training\nself.cumulative_epochs += self.epochs_per_proposal\nnew_weights.training_summary = TrainingSummary(dp_budget=self.diff_priv_budget)\nreturn new_weights\ndef mli_test_weights(self, weights: Weights) -> ProposedWeights:\n\"\"\"\n        Tests given weights on training and test set and returns weights with score values\n        :param weights: Weights to be tested\n        :return: ProposedWeights - Weights with vote and test score\n        \"\"\"\ncurrent_weights = self.mli_get_current_weights()\nself.set_weights(weights)\nvote_score = self.test(self.vote_loader)\nif self.test_loader:\ntest_score = self.test(self.test_loader)\nelse:\ntest_score = 0\nvote = self.vote(vote_score)\nself.set_weights(current_weights)\nreturn ProposedWeights(weights=weights,\nvote_score=vote_score,\ntest_score=test_score,\nvote=vote,\n)\ndef vote(self, new_score) -> bool:\n\"\"\"\n        Compares current model score with proposed model score and returns vote\n        :param new_score: Proposed score\n        :return: bool positive or negative vote\n        \"\"\"\nif self.minimise_criterion:\nreturn new_score < self.vote_score\nelse:\nreturn new_score > self.vote_score\ndef mli_accept_weights(self, weights: Weights):\n\"\"\"\n        Updates the model with the proposed set of weights\n        :param weights: The new weights\n        \"\"\"\nself.set_weights(weights)\nself.vote_score = self.test(self.vote_loader)\ndef get_train_batch_size(self) -> int:\n\"\"\"\n        Calculates train batch size.\n        \"\"\"\nif hasattr(self.train_loader, '_batch_size'):\nreturn self.train_loader._batch_size  # pylint: disable=protected-access\nelse:\nreturn self.train_loader._input_dataset._batch_size  # pylint: disable=protected-access\ndef get_privacy_budget(self) -> float:\n\"\"\"\n        Calculates, what epsilon will apply after another model training.\n        Need to calculate it in advance to see if another training would result in privacy budget violation.\n        \"\"\"\nbatch_size = self.get_train_batch_size()\niterations_per_epoch = tf.data.experimental.cardinality(self.train_loader).numpy()\nn_samples = batch_size * iterations_per_epoch\nplanned_epochs = self.cumulative_epochs + self.epochs_per_proposal\nepsilon, _ = compute_dp_sgd_privacy(\nn=n_samples,\nbatch_size=batch_size,\nnoise_multiplier=self.diff_priv_config.noise_multiplier,  # type: ignore\nepochs=planned_epochs,\ndelta=self.diff_priv_budget.target_delta\n)\nreturn epsilon\ndef mli_get_current_weights(self) -> Weights:\n\"\"\"\n        :return: The current weights of the model\n        \"\"\"\nreturn Weights(weights=self.model.get_weights())\ndef mli_get_current_model(self) -> ColearnModel:\n\"\"\"\n        :return: The current model and its format\n        \"\"\"\nreturn ColearnModel(\nmodel_format=ModelFormat(ModelFormat.ONNX),\nmodel_file=\"\",\nmodel=convert_model_to_onnx(self.model),\n)\ndef set_weights(self, weights: Weights):\n\"\"\"\n        Rewrites weight of current model\n        :param weights: Weights to be stored\n        \"\"\"\nself.model.set_weights(weights.weights)\ndef train(self):\n\"\"\"\n        Trains the model on the training dataset\n        \"\"\"\nif self.need_reset_optimizer:\n# erase the outdated optimizer memory (momentums mostly)\nself.reset_optimizer()\nself.model.fit(self.train_loader, **self.model_fit_kwargs)\ndef test(self, loader: tf.data.Dataset) -> float:\n\"\"\"\n        Tests performance of the model on specified dataset\n        :param loader: Dataset for testing\n        :return: Value of performance metric\n        \"\"\"\nresult = self.model.evaluate(x=loader, return_dict=True,\n**self.model_evaluate_kwargs)\nreturn result[self.criterion]\n

We create a set of KerasLearners by passing in the model and the datasets:

all_learner_models = []\nfor i in range(n_learners):\nall_learner_models.append(KerasLearner(\nmodel=get_model(),\ntrain_loader=train_datasets[i],\nvote_loader=vote_datasets[i],\ntest_loader=test_datasets[i],\ncriterion=\"sparse_categorical_accuracy\",\nminimise_criterion=False,\nmodel_evaluate_kwargs={\"steps\": vote_batches},\n))\n

Then we give all the models the same weights to start off with:

set_equal_weights(all_learner_models)\n

And then we can move on to the final stage, which is training with Collective Learning. The function collective_learning_round performs one round of collective learning. One learner is selected to train and propose an update. The other learners vote on the update, and if the vote passes then the update is accepted. Then a new round begins.

# Train the model using Collective Learning\nresults = Results()\nresults.data.append(initial_result(all_learner_models))\nfor round in range(n_rounds):\nresults.data.append(\ncollective_learning_round(all_learner_models,\nvote_threshold, round)\n)\nplot_results(results, n_learners, block=False,\nscore_name=all_learner_models[0].criterion)\nplot_votes(results, block=False)\nplot_results(results, n_learners, block=False,\nscore_name=all_learner_models[0].criterion)\nplot_votes(results, block=True)\n
"},{"location":"colearn/intro_tutorial_mli/","title":"Using collective learning","text":"

This tutorial is a simple guide to trying out the collective learning protocol with your own machine learning code. Everything runs locally.

The most flexible way to use the collective learning backends is to make a class that implements the Collective Learning MachineLearningInterface defined in ml_interface.py. This tutorial will walk through implementing the MachineLearningInterface. If you're already using keras or pytorch you might find it easier to use the KerasLearner or Pytorchlearner classes. See the other tutorials for details of how to do that.

"},{"location":"colearn/intro_tutorial_mli/#the-machinelearninginterface","title":"The MachineLearningInterface","text":"
# ------------------------------------------------------------------------------\n#\n#   Copyright 2021 Fetch.AI Limited\n#\n#   Licensed under the Creative Commons Attribution-NonCommercial International\n#   License, Version 4.0 (the \"License\"); you may not use this file except in\n#   compliance with the License. You may obtain a copy of the License at\n#\n#       http://creativecommons.org/licenses/by-nc/4.0/legalcode\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n#\n# ------------------------------------------------------------------------------\nimport abc\nfrom enum import Enum\nfrom typing import Any, Optional\nimport onnx\nimport onnxmltools\nimport sklearn\nimport tensorflow as tf\nimport torch\nfrom pydantic import BaseModel\nfrom tensorflow import keras\nmodel_classes_keras = (tf.keras.Model, keras.Model, tf.estimator.Estimator)\nmodel_classes_scipy = (torch.nn.Module)\nmodel_classes_sklearn = (sklearn.base.ClassifierMixin)\ndef convert_model_to_onnx(model: Any):\n\"\"\"\n    Helper function to convert a ML model to onnx format\n    \"\"\"\nif isinstance(model, model_classes_keras):\nreturn onnxmltools.convert_keras(model)\nif isinstance(model, model_classes_sklearn):\nreturn onnxmltools.convert_sklearn(model)\nif 'xgboost' in model.__repr__():\nreturn onnxmltools.convert_sklearn(model)\nif isinstance(model, model_classes_scipy):\nraise Exception(\"Pytorch models not yet supported to onnx\")\nelse:\nraise Exception(\"Attempt to convert unsupported model to onnx: {model}\")\nclass DiffPrivBudget(BaseModel):\ntarget_epsilon: float\ntarget_delta: float\nconsumed_epsilon: float\nconsumed_delta: float\nclass ErrorCodes(Enum):\nDP_BUDGET_EXCEEDED = 1\nclass TrainingSummary(BaseModel):\ndp_budget: Optional[DiffPrivBudget]\nerror_code: Optional[ErrorCodes]\nclass Weights(BaseModel):\nweights: Any\ntraining_summary: Optional[TrainingSummary]\nclass DiffPrivConfig(BaseModel):\ntarget_epsilon: float\ntarget_delta: float\nmax_grad_norm: float\nnoise_multiplier: float\nclass ProposedWeights(BaseModel):\nweights: Weights\nvote_score: float\ntest_score: float\nvote: Optional[bool]\nclass ModelFormat(Enum):\nPICKLE_WEIGHTS_ONLY = 1\nONNX = 2\nclass ColearnModel(BaseModel):\nmodel_format: ModelFormat\nmodel_file: Optional[str]\nmodel: Optional[Any]\ndef deser_model(model: Any) -> onnx.ModelProto:\n\"\"\"\n    Helper function to recover a onnx model from its deserialized form\n    \"\"\"\nreturn onnx.load_model_from_string(model)\nclass MachineLearningInterface(abc.ABC):\n@abc.abstractmethod\ndef mli_propose_weights(self) -> Weights:\n\"\"\"\n        Trains the model. Returns new weights. Does not change the current weights of the model.\n        \"\"\"\npass\n@abc.abstractmethod\ndef mli_test_weights(self, weights: Weights) -> ProposedWeights:\n\"\"\"\n        Tests the proposed weights and fills in the rest of the fields\n        \"\"\"\n@abc.abstractmethod\ndef mli_accept_weights(self, weights: Weights):\n\"\"\"\n        Updates the model with the proposed set of weights\n        :param weights: The new weights\n        \"\"\"\npass\n@abc.abstractmethod\ndef mli_get_current_weights(self) -> Weights:\n\"\"\"\n        Returns the current weights of the model\n        \"\"\"\npass\n@abc.abstractmethod\ndef mli_get_current_model(self) -> ColearnModel:\n\"\"\"\n        Returns the current model\n        \"\"\"\npass \n

There are four methods that need to be implemented:

  1. propose_weights causes the model to do some training and then return a new set of weights that are proposed to the other learners. This method shouldn't charge the current weights of the model - that only happens when accept_weights is called.
  2. test_weights - the models takes some new weights and returns a vote on whether the new weights are an improvement. As in propose_weights, this shouldn't change the current weights of the model - that only happens when accept_weights is called.
  3. accept_weights - the model accepts some weights that have been voted on and approved by the set of learners. The old weighs of the model are discarded and replaced by the new weights.
  4. current_weights should return the current weights of the model.
"},{"location":"colearn/intro_tutorial_mli/#algorithms-that-work-with-colearn","title":"Algorithms that work with colearn","text":"

These conditions need to be fulfilled for algorithms to work with collective learning:

  • Model fitting must be incremental so that the previous model is used as the starting point for training. This is easy to achieve for neural networks because neural network training is always iterative, but for other learning algorithms more care must be taken. Some examples of getting this wrong:
from sklearn.linear_model import LinearRegression\nmodel = LinearRegression()\nmodel.fit(X, y)\n
from sklearn.ensemble import RandomForestClassifier\nmodel = RandomForestClassifier(n_estimators=10)  # it would be okay with warm_start=True\nmodel.fit(X, y)\n
from xgboost import XGBRegressor\nmodel = XGBRegressor()\nmodel.fit(X, y)\n

None of the training methods here use the previous result when fit is called for a second time; instead they start again from scratch. Good examples of incremental training can be seen in the examples. Many sklearn models have a warm_start parameter which can be set to True to use the previous training result. XGBoost has an xgb_model parameter for passing in the previous training results.

  • The model mustn't overfit when propose_weights() is called. You should limit training so that a learner will not overfit their training data in one round. For example, if a learner overfits their own training data then the other learners will reject the proposed update because it is not a good fit for their data. For a neural network a good approach is to restrict the number of batches that are used each round; for random forest, restrict the trees that are added each round.
"},{"location":"colearn/intro_tutorial_mli/#implementation-for-fraud-detection-task","title":"Implementation for fraud detection task","text":"

Here is the class that implements the MachineLearningInterface for the task of detecting fraud in bank transactions.

class FraudSklearnLearner(MachineLearningInterface):\ndef __init__(self, train_data, train_labels, test_data, test_labels,\nbatch_size: int = 10000,\nsteps_per_round: int = 1):\nself.steps_per_round = steps_per_round\nself.batch_size = batch_size\nself.train_data = train_data\nself.train_labels = train_labels\nself.test_data = test_data\nself.test_labels = test_labels\nself.class_labels = np.unique(train_labels)\nself.train_sampler = infinite_batch_sampler(train_data.shape[0], batch_size)\nself.model = SGDClassifier(max_iter=1, verbose=0, loss=\"modified_huber\")\nself.model.partial_fit(self.train_data[0:1], self.train_labels[0:1],\nclasses=self.class_labels)  # this needs to be called before predict\nself.vote_score = self.test(self.train_data, self.train_labels)\ndef mli_propose_weights(self) -> Weights:\ncurrent_weights = self.mli_get_current_weights()\nfor i in range(self.steps_per_round):\nbatch_indices = next(self.train_sampler)\ntrain_data = self.train_data[batch_indices]\ntrain_labels = self.train_labels[batch_indices]\nself.model.partial_fit(train_data, train_labels, classes=self.class_labels)\nnew_weights = self.mli_get_current_weights()\nself.set_weights(current_weights)\nreturn new_weights\ndef mli_test_weights(self, weights: Weights) -> ProposedWeights:\ncurrent_weights = self.mli_get_current_weights()\nself.set_weights(weights)\nvote_score = self.test(self.train_data, self.train_labels)\ntest_score = self.test(self.test_data, self.test_labels)\nvote = self.vote_score <= vote_score\nself.set_weights(current_weights)\nreturn ProposedWeights(weights=weights,\nvote_score=vote_score,\ntest_score=test_score,\nvote=vote\n)\ndef mli_accept_weights(self, weights: Weights):\nself.set_weights(weights)\nself.vote_score = self.test(self.train_data, self.train_labels)\ndef mli_get_current_weights(self):\n# return Weights(weights=copy.deepcopy(self.model))\nreturn Weights(weights=dict(coef_=self.model.coef_,\nintercept_=self.model.intercept_))\ndef set_weights(self, weights: Weights):\n# self.model = weights.weights\nself.model.coef_ = weights.weights['coef_']\nself.model.intercept_ = weights.weights['intercept_']\ndef test(self, data, labels):\ntry:\nreturn self.model.score(data, labels)\nexcept sklearn.exceptions.NotFittedError:\nreturn 0\n

Let's step through this and see how it works. The propose_weights method saves the current weights of the model. Then it performs some training of the model, and gets the new weights. It returns the new weights, and resets the model weights to be the old weights.

    def mli_propose_weights(self) -> Weights:\ncurrent_weights = self.mli_get_current_weights()\nfor i in range(self.steps_per_round):\nbatch_indices = next(self.train_sampler)\ntrain_data = self.train_data[batch_indices]\ntrain_labels = self.train_labels[batch_indices]\nself.model.partial_fit(train_data, train_labels, classes=self.class_labels)\nnew_weights = self.mli_get_current_weights()\nself.set_weights(current_weights)\nreturn new_weights\n

The test_weights method takes as a parameter the proposed weights that it needs to vote on. It saves the current weights of the model, and then sets the model weights to be the proposed weights. It tests the model and votes based on whether the score that it is monitoring has improved. The vote score can be any metric that you like. You could use loss, accuracy, mean squared error or any custom metric. If the vote score is the loss then the model would only vote True if the score has decreased. Here we're using accuracy, so the vote is true if the score increases. This method then resets the weights to the old values and returns the vote along with some scores for monitoring purposes.

    def mli_test_weights(self, weights: Weights) -> ProposedWeights:\ncurrent_weights = self.mli_get_current_weights()\nself.set_weights(weights)\nvote_score = self.test(self.train_data, self.train_labels)\ntest_score = self.test(self.test_data, self.test_labels)\nvote = self.vote_score <= vote_score\nself.set_weights(current_weights)\nreturn ProposedWeights(weights=weights,\nvote_score=vote_score,\ntest_score=test_score,\nvote=vote\n)\n

The accept_weights method sets the weights of the model to be the new weights. It also updates the vote score to be the current performance.

Note

You could implement a cache here. These weights will already have been tested in test_weights, so the vote score could be retrieved from the cache instead of recomputed.

    def mli_accept_weights(self, weights: Weights):\nself.set_weights(weights)\nself.vote_score = self.test(self.train_data, self.train_labels)\n

The final method is the simplest - get_current_weights just returns the current weights of the model. These weights are wrapped inside a Weights object.

    def mli_get_current_weights(self):\nreturn Weights(weights=dict(coef_=self.model.coef_,\nintercept_=self.model.intercept_))\n
"},{"location":"colearn/intro_tutorial_mli/#the-rest-of-the-example","title":"The rest of the example","text":"

The data is loaded and preprocessed and then split into equal parts for each learner. Then a list of FraudLearner instances is created, each with its own dataset.

    all_learner_models = []\nfor i in range(n_learners):\nall_learner_models.append(\nFraudLearner(\ntrain_data=learner_train_data[i],\ntrain_labels=learner_train_labels[i],\ntest_data=learner_test_data[i],\ntest_labels=learner_test_labels[i]\n))\n

Then we give all the models the same weights to start off with:

set_equal_weights(all_learner_models)\n

And then we can move on to the final stage, which is training with Collective Learning. The function collective_learning_round performs one round of collective learning. One learner is selected to train and propose an update. The other learners vote on the update, and if the vote passes then the update is accepted. Then a new round begins.

# Train the model using Collective Learning\nresults = Results()\nresults.data.append(initial_result(all_learner_models))\nfor round in range(n_rounds):\nresults.data.append(\ncollective_learning_round(all_learner_models,\nvote_threshold, round)\n)\nplot_results(results, n_learners, block=False,\nscore_name=all_learner_models[0].criterion)\nplot_votes(results, block=False)\nplot_results(results, n_learners, block=False,\nscore_name=all_learner_models[0].criterion)\nplot_votes(results, block=True)\n
"},{"location":"colearn/intro_tutorial_pytorch/","title":"Using collective learning with pytorch","text":"

This tutorial is a simple guide to trying out the collective learning protocol with your own machine learning code. Everything runs locally.

The most flexible way to use the collective learning backends is to make a class that implements the Collective Learning MachineLearningInterface defined in ml_interface.py. For more details on how to use the MachineLearningInterface see here

However, the simpler way is to use one of the helper classes that we have provided that implement most of the interface for popular ML libraries. In this tutorial we are going to walk through using the PytorchLearner. First we are going to define the model architecture, then we are going to load the data and configure the model, and then we will run Collective Learning.

A standard script for machine learning with Pytorch looks like the one below

# ------------------------------------------------------------------------------\n#\n#   Copyright 2021 Fetch.AI Limited\n#\n#   Licensed under the Creative Commons Attribution-NonCommercial International\n#   License, Version 4.0 (the \"License\"); you may not use this file except in\n#   compliance with the License. You may obtain a copy of the License at\n#\n#       http://creativecommons.org/licenses/by-nc/4.0/legalcode\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n#\n# ------------------------------------------------------------------------------\nfrom torchsummary import summary\nfrom torchvision import transforms, datasets\nimport torch.utils.data\nimport torch.nn as nn\nimport torch.nn.functional as nn_func\n# define some constants\nbatch_size = 64\nseed = 42\nn_rounds = 20\ntrain_fraction = 0.9\nlearning_rate = 0.001\nheight = 28\nwidth = 28\nn_classes = 10\nnum_test_batches = 10\nno_cuda = False\ncuda = not no_cuda and torch.cuda.is_available()\ndevice = torch.device(\"cuda\" if cuda else \"cpu\")\nkwargs = {'num_workers': 1, 'pin_memory': True} if cuda else {}\n# Load the data\ndata = datasets.MNIST('/tmp/mnist', transform=transforms.ToTensor(), download=True)\nn_train = int(train_fraction * len(data))\nn_test = len(data) - n_train\ntrain_data, test_data = torch.utils.data.random_split(data, [n_train, n_test])\ntrain_dataloader = torch.utils.data.DataLoader(train_data, batch_size=batch_size, shuffle=True, **kwargs)\ntest_dataloader = torch.utils.data.DataLoader(test_data, batch_size=batch_size, shuffle=True, **kwargs)\n# Define the model\nclass Net(nn.Module):\ndef __init__(self):\nsuper(Net, self).__init__()\nself.conv1 = nn.Conv2d(1, 20, 5, 1)\nself.conv2 = nn.Conv2d(20, 50, 5, 1)\nself.fc1 = nn.Linear(4 * 4 * 50, 500)\nself.fc2 = nn.Linear(500, n_classes)\ndef forward(self, x):\nx = nn_func.relu(self.conv1(x.view(-1, 1, height, width)))\nx = nn_func.max_pool2d(x, 2, 2)\nx = nn_func.relu(self.conv2(x))\nx = nn_func.max_pool2d(x, 2, 2)\nx = x.view(-1, 4 * 4 * 50)\nx = nn_func.relu(self.fc1(x))\nx = self.fc2(x)\nreturn nn_func.log_softmax(x, dim=1)\nmodel = Net()\nopt = torch.optim.Adam(model.parameters(), lr=learning_rate)\ncriterion = torch.nn.NLLLoss()\n# Train and evaluate the model\nfor round in range(n_rounds):\n# train model\nmodel.train()\nfor batch_idx, (data, labels) in enumerate(train_dataloader):\nopt.zero_grad()\n# Data needs to be on same device as model\ndata = data.to(device)\nlabels = labels.to(device)\noutput = model(data)\nloss = criterion(output, labels)\nloss.backward()\nopt.step()\n# evaluate model\nmodel.eval()\ntotal_score = 0\nall_labels = []\nall_outputs = []\nwith torch.no_grad():\nfor batch_idx, (data, labels) in enumerate(test_dataloader):\nif batch_idx == num_test_batches:\nbreak\ndata = data.to(device)\nlabels = labels.to(device)\noutput = model(data)\ntotal_score += criterion(output, labels)\navg_loss = float(total_score / (num_test_batches * batch_size))\nprint(f\"Average loss at round {round} is {avg_loss}\")\n

There are three steps:

  1. Load the data
  2. Define the model
  3. Train the model

In this tutorial we are going to see how to modify each step to use collective learning. We'll end up with code like this:

# ------------------------------------------------------------------------------\n#\n#   Copyright 2021 Fetch.AI Limited\n#\n#   Licensed under the Creative Commons Attribution-NonCommercial International\n#   License, Version 4.0 (the \"License\"); you may not use this file except in\n#   compliance with the License. You may obtain a copy of the License at\n#\n#       http://creativecommons.org/licenses/by-nc/4.0/legalcode\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n#\n# ------------------------------------------------------------------------------\nimport os\nfrom typing_extensions import TypedDict\nimport torch.nn as nn\nimport torch.nn.functional as nn_func\nimport torch.utils.data\nfrom torchsummary import summary\nfrom torchvision import transforms, datasets\nfrom colearn.training import initial_result, collective_learning_round, set_equal_weights\nfrom colearn.utils.plot import ColearnPlot\nfrom colearn.utils.results import Results, print_results\nfrom colearn_pytorch.utils import categorical_accuracy\nfrom colearn_pytorch.pytorch_learner import PytorchLearner\n\"\"\"\nMNIST training example using PyTorch\nUsed dataset:\n- MNIST is set of 60 000 black and white hand written digits images of size 28x28x1 in 10 classes\nWhat script does:\n- Loads MNIST dataset from torchvision.datasets\n- Randomly splits dataset between multiple learners\n- Does multiple rounds of learning process and displays plot with results\n\"\"\"\n# define some constants\nn_learners = 5\nbatch_size = 64\ntesting_mode = bool(os.getenv(\"COLEARN_EXAMPLES_TEST\", \"\"))  # for testing\nn_rounds = 20 if not testing_mode else 1\nvote_threshold = 0.5\ntrain_fraction = 0.9\nvote_fraction = 0.05\nlearning_rate = 0.001\nheight = 28\nwidth = 28\nn_classes = 10\nvote_batches = 2\nscore_name = \"categorical accuracy\"\nno_cuda = False\ncuda = not no_cuda and torch.cuda.is_available()\ndevice = torch.device(\"cuda\" if cuda else \"cpu\")\nDataloaderKwargs = TypedDict('DataloaderKwargs', {'num_workers': int, 'pin_memory': bool}, total=False)\nkwargs: DataloaderKwargs = {'num_workers': 1, 'pin_memory': True} if cuda else {}\n# Load the data and split for each learner.\nDATA_DIR = os.environ.get('PYTORCH_DATA_DIR',\nos.path.expanduser(os.path.join('~', 'pytorch_datasets')))\ndata = datasets.MNIST(DATA_DIR, transform=transforms.ToTensor(), download=True)\nn_train = int(train_fraction * len(data))\nn_vote = int(vote_fraction * len(data))\nn_test = len(data) - n_train - n_vote\ntrain_data, vote_data, test_data = torch.utils.data.random_split(data, [n_train, n_vote, n_test])\ndata_split = [len(train_data) // n_learners] * n_learners\nlearner_train_data = torch.utils.data.random_split(train_data, data_split)\nlearner_train_dataloaders = [torch.utils.data.DataLoader(\nds,\nbatch_size=batch_size, shuffle=True, **kwargs) for ds in learner_train_data]\ndata_split = [len(vote_data) // n_learners] * n_learners\nlearner_vote_data = torch.utils.data.random_split(vote_data, data_split)\nlearner_vote_dataloaders = [torch.utils.data.DataLoader(\nds,\nbatch_size=batch_size, shuffle=True, **kwargs) for ds in learner_vote_data]\ndata_split = [len(test_data) // n_learners] * n_learners\nlearner_test_data = torch.utils.data.random_split(test_data, data_split)\nlearner_test_dataloaders = [torch.utils.data.DataLoader(\nds,\nbatch_size=batch_size, shuffle=True, **kwargs) for ds in learner_test_data]\n# Define the model\nclass Net(nn.Module):\ndef __init__(self):\nsuper(Net, self).__init__()\nself.conv1 = nn.Conv2d(1, 20, 5, 1)\nself.conv2 = nn.Conv2d(20, 50, 5, 1)\nself.fc1 = nn.Linear(4 * 4 * 50, 500)\nself.fc2 = nn.Linear(500, n_classes)\ndef forward(self, x):\nx = nn_func.relu(self.conv1(x.view(-1, 1, height, width)))\nx = nn_func.max_pool2d(x, 2, 2)\nx = nn_func.relu(self.conv2(x))\nx = nn_func.max_pool2d(x, 2, 2)\nx = x.view(-1, 4 * 4 * 50)\nx = nn_func.relu(self.fc1(x))\nx = self.fc2(x)\nreturn nn_func.log_softmax(x, dim=1)\n# Make n instances of PytorchLearner with model and torch dataloaders\nall_learner_models = []\nfor i in range(n_learners):\nmodel = Net().to(device)\nopt = torch.optim.Adam(model.parameters(), lr=learning_rate)\nlearner = PytorchLearner(\nmodel=model,\ntrain_loader=learner_train_dataloaders[i],\nvote_loader=learner_vote_dataloaders[i],\ntest_loader=learner_test_dataloaders[i],\ndevice=device,\noptimizer=opt,\ncriterion=torch.nn.NLLLoss(),\nnum_test_batches=vote_batches,\nvote_criterion=categorical_accuracy,\nminimise_criterion=False\n)\nall_learner_models.append(learner)\n# Ensure all learners starts with exactly same weights\nset_equal_weights(all_learner_models)\nsummary(all_learner_models[0].model, input_size=(width, height), device=str(device))\n# Train the model using Collective Learning\nresults = Results()\nresults.data.append(initial_result(all_learner_models))\nplot = ColearnPlot(score_name=score_name)\nfor round_index in range(n_rounds):\nresults.data.append(\ncollective_learning_round(all_learner_models,\nvote_threshold, round_index)\n)\nprint_results(results)\nplot.plot_results_and_votes(results)\nplot.block()\nprint(\"Colearn Example Finished!\")\n

The first thing is to modify the data loading code. Each learner needs to have their own training and testing set from the data. This is easy to do with the pytorch random_split utility:

data_split = [len(test_data) // n_learners] * n_learners\nlearner_test_data = torch.utils.data.random_split(test_data, data_split)\n

The model definition is the same as before. To use collective learning, we need to create an object that implements the MachineLearningInterface. To make it easier to use the MachineLearningInterface with pytorch, we've defined PytorchLearner. PytorchLearner implements standard training and evaluation routines as well as the MachineLearningInterface methods.

# ------------------------------------------------------------------------------\n#\n#   Copyright 2021 Fetch.AI Limited\n#\n#   Licensed under the Creative Commons Attribution-NonCommercial International\n#   License, Version 4.0 (the \"License\"); you may not use this file except in\n#   compliance with the License. You may obtain a copy of the License at\n#\n#       http://creativecommons.org/licenses/by-nc/4.0/legalcode\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n#\n# ------------------------------------------------------------------------------\nfrom typing import Optional, Callable\nfrom collections import OrderedDict, defaultdict\ntry:\nimport torch\nexcept ImportError:\nraise Exception(\n\"Pytorch is not installed. To use the pytorch \"\n\"add-ons please install colearn with `pip install colearn[pytorch]`.\"\n)\nimport torch.nn\nimport torch.optim\nimport torch.utils\nimport torch.utils.data\nfrom torch.nn.modules.loss import _Loss\nfrom colearn.ml_interface import (\nMachineLearningInterface,\nWeights,\nProposedWeights,\nColearnModel,\nconvert_model_to_onnx,\nModelFormat,\nDiffPrivBudget,\nDiffPrivConfig,\nTrainingSummary,\nErrorCodes,\n)\nfrom opacus import PrivacyEngine\n_DEFAULT_DEVICE = torch.device(\"cpu\")\nclass PytorchLearner(MachineLearningInterface):\n\"\"\"\n    Pytorch learner implementation of machine learning interface\n    \"\"\"\ndef __init__(\nself,\nmodel: torch.nn.Module,\noptimizer: torch.optim.Optimizer,\ntrain_loader: torch.utils.data.DataLoader,\nvote_loader: torch.utils.data.DataLoader,\ntest_loader: Optional[torch.utils.data.DataLoader] = None,\nneed_reset_optimizer: bool = True,\ndevice=_DEFAULT_DEVICE,\ncriterion: Optional[_Loss] = None,\nminimise_criterion=True,\nvote_criterion: Optional[Callable[[torch.Tensor, torch.Tensor], float]] = None,\nnum_train_batches: Optional[int] = None,\nnum_test_batches: Optional[int] = None,\ndiff_priv_config: Optional[DiffPrivConfig] = None,\n):\n\"\"\"\n        :param model: Pytorch model used for training\n        :param optimizer: Training optimizer\n        :param train_loader: Train dataset\n        :param test_loader: Optional test dataset - subset of training set will be used if not specified\n        :param need_reset_optimizer: True to clear optimizer history before training, False to kepp history.\n        :param device: Pytorch device - CPU or GPU\n        :param criterion: Loss function\n        :param minimise_criterion: True to minimise value of criterion, False to maximise\n        :param vote_criterion: Function to measure model performance for voting\n        :param num_train_batches: Number of training batches\n        :param num_test_batches: Number of testing batches\n        :param diff_priv_config: Contains differential privacy (dp) budget related configuration\n        \"\"\"\n# Model has to be on same device as data\nself.model: torch.nn.Module = model.to(device)\nself.optimizer: torch.optim.Optimizer = optimizer\nself.criterion = criterion\nself.train_loader: torch.utils.data.DataLoader = train_loader\nself.vote_loader: torch.utils.data.DataLoader = vote_loader\nself.test_loader: Optional[torch.utils.data.DataLoader] = test_loader\nself.need_reset_optimizer = need_reset_optimizer\nself.device = device\nself.num_train_batches = num_train_batches or len(train_loader)\nself.num_test_batches = num_test_batches\nself.minimise_criterion = minimise_criterion\nself.vote_criterion = vote_criterion\nself.dp_config = diff_priv_config\nself.dp_privacy_engine = PrivacyEngine()\nif diff_priv_config is not None:\n(\nself.model,\nself.optimizer,\nself.train_loader,\n) = self.dp_privacy_engine.make_private(\nmodule=self.model,\noptimizer=self.optimizer,\ndata_loader=self.train_loader,\nmax_grad_norm=diff_priv_config.max_grad_norm,\nnoise_multiplier=diff_priv_config.noise_multiplier,\n)\nself.vote_score = self.test(self.vote_loader)\ndef mli_get_current_weights(self) -> Weights:\n\"\"\"\n        :return: The current weights of the model\n        \"\"\"\ncurrent_state_dict = OrderedDict()\nfor key in self.model.state_dict():\ncurrent_state_dict[key] = self.model.state_dict()[key].clone()\nw = Weights(\nweights=current_state_dict, training_summary=self.get_training_summary()\n)\nreturn w\ndef mli_get_current_model(self) -> ColearnModel:\n\"\"\"\n        :return: The current model and its format\n        \"\"\"\nreturn ColearnModel(\nmodel_format=ModelFormat(ModelFormat.ONNX),\nmodel_file=\"\",\nmodel=convert_model_to_onnx(self.model),\n)\ndef set_weights(self, weights: Weights):\n\"\"\"\n        Rewrites weight of current model\n        :param weights: Weights to be stored\n        \"\"\"\nself.model.load_state_dict(weights.weights)\ndef reset_optimizer(self):\n\"\"\"\n        Clear optimizer state, such as number of iterations, momentums.\n        This way, the outdated history can be erased.\n        \"\"\"\nself.optimizer.__setstate__({\"state\": defaultdict(dict)})\ndef train(self):\n\"\"\"\n        Trains the model on the training dataset\n        \"\"\"\nif self.need_reset_optimizer:\n# erase the outdated optimizer memory (momentums mostly)\nself.reset_optimizer()\nself.model.train()\nfor batch_idx, (data, labels) in enumerate(self.train_loader):\nif batch_idx == self.num_train_batches:\nbreak\nself.optimizer.zero_grad()\n# Data needs to be on same device as model\ndata = data.to(self.device)\nlabels = labels.to(self.device)\noutput = self.model(data)\nloss = self.criterion(output, labels)\nloss.backward()\nself.optimizer.step()\ndef mli_propose_weights(self) -> Weights:\n\"\"\"\n        Trains model on training set and returns new weights after training\n        - Current model is reverted to original state after training\n        :return: Weights after training\n        \"\"\"\ncurrent_weights = self.mli_get_current_weights()\ntraining_summary = current_weights.training_summary\nif (\ntraining_summary is not None\nand training_summary.error_code is not None\nand training_summary.error_code == ErrorCodes.DP_BUDGET_EXCEEDED\n):\nreturn current_weights\nself.train()\nnew_weights = self.mli_get_current_weights()\nself.set_weights(current_weights)\ntraining_summary = new_weights.training_summary\nif (\ntraining_summary is not None\nand training_summary.error_code is not None\nand training_summary.error_code == ErrorCodes.DP_BUDGET_EXCEEDED\n):\ncurrent_weights.training_summary = training_summary\nreturn current_weights\nreturn new_weights\ndef mli_test_weights(self, weights: Weights) -> ProposedWeights:\n\"\"\"\n        Tests given weights on training and test set and returns weights with score values\n        :param weights: Weights to be tested\n        :return: ProposedWeights - Weights with vote and test score\n        \"\"\"\ncurrent_weights = self.mli_get_current_weights()\nself.set_weights(weights)\nvote_score = self.test(self.vote_loader)\nif self.test_loader:\ntest_score = self.test(self.test_loader)\nelse:\ntest_score = 0\nvote = self.vote(vote_score)\nself.set_weights(current_weights)\nreturn ProposedWeights(\nweights=weights, vote_score=vote_score, test_score=test_score, vote=vote\n)\ndef vote(self, new_score) -> bool:\n\"\"\"\n        Compares current model score with proposed model score and returns vote\n        :param new_score: Proposed score\n        :return: bool positive or negative vote\n        \"\"\"\nif self.minimise_criterion:\nreturn new_score < self.vote_score\nelse:\nreturn new_score > self.vote_score\ndef test(self, loader: torch.utils.data.DataLoader) -> float:\n\"\"\"\n        Tests performance of the model on specified dataset\n        :param loader: Dataset for testing\n        :return: Value of performance metric\n        \"\"\"\nif not self.criterion:\nraise Exception(\"Criterion is unspecified so test method cannot be used\")\nself.model.eval()\ntotal_score = 0\nall_labels = []\nall_outputs = []\nbatch_idx = 0\ntotal_samples = 0\nwith torch.no_grad():\nfor batch_idx, (data, labels) in enumerate(loader):\ntotal_samples += labels.shape[0]\nif self.num_test_batches and batch_idx == self.num_test_batches:\nbreak\ndata = data.to(self.device)\nlabels = labels.to(self.device)\noutput = self.model(data)\nif self.vote_criterion is not None:\nall_labels.append(labels)\nall_outputs.append(output)\nelse:\ntotal_score += self.criterion(output, labels).item()\nif batch_idx == 0:\nraise Exception(\"No batches in loader\")\nif self.vote_criterion is None:\nreturn float(total_score / total_samples)\nelse:\nreturn self.vote_criterion(\ntorch.cat(all_outputs, dim=0), torch.cat(all_labels, dim=0)\n)\ndef mli_accept_weights(self, weights: Weights):\n\"\"\"\n        Updates the model with the proposed set of weights\n        :param weights: The new weights\n        \"\"\"\nself.set_weights(weights)\nself.vote_score = self.test(self.vote_loader)\ndef get_training_summary(self) -> Optional[TrainingSummary]:\n\"\"\"\n        Differential Privacy Budget\n        :return: the target and consumed epsilon so far\n        \"\"\"\nif self.dp_config is None:\nreturn None\ndelta = self.dp_config.target_delta\ntarget_epsilon = self.dp_config.target_epsilon\nconsumed_epsilon = self.dp_privacy_engine.get_epsilon(delta)\nbudget = DiffPrivBudget(\ntarget_epsilon=target_epsilon,\nconsumed_epsilon=consumed_epsilon,\ntarget_delta=delta,\nconsumed_delta=delta,  # delta is constatnt per training\n)\nerr = (\nErrorCodes.DP_BUDGET_EXCEEDED\nif consumed_epsilon >= target_epsilon\nelse None\n)\nreturn TrainingSummary(\ndp_budget=budget,\nerror_code=err,\n)\n

We create a set of PytorchLearners by passing in the model and the datasets:

all_learner_models = []\nfor i in range(n_learners):\nmodel = Net()\nopt = torch.optim.Adam(model.parameters(), lr=learning_rate)\nlearner = PytorchLearner(\nmodel=model,\ntrain_loader=learner_train_dataloaders[i],\nvote_loader=learner_vote_dataloaders[i],\ntest_loader=learner_test_dataloaders[i],\ndevice=device,\noptimizer=opt,\ncriterion=torch.nn.NLLLoss(),\nnum_test_batches=vote_batches,\nvote_criterion=categorical_accuracy,\nminimise_criterion=False\n)\nall_learner_models.append(learner)\n

Then we give all the models the same weights to start off with:

set_equal_weights(all_learner_models)\n

And then we can move on to the final stage, which is training with Collective Learning. The function collective_learning_round performs one round of collective learning. One learner is selected to train and propose an update. The other learners vote on the update, and if the vote passes then the update is accepted. Then a new round begins.

# Train the model using Collective Learning\nresults = Results()\nresults.data.append(initial_result(all_learner_models))\nfor round in range(n_rounds):\nresults.data.append(\ncollective_learning_round(all_learner_models,\nvote_threshold, round)\n)\nplot_results(results, n_learners, score_name=score_name)\nplot_votes(results)\n# Plot the final result with votes\nplot_results(results, n_learners, score_name=score_name)\nplot_votes(results, block=True)\n
"},{"location":"colearn/mli_factory/","title":"MLI Factory","text":"

The machine learning interface factory are the minimum methods a client needs to implement to work with the GRPC Server (and become a Learner).

There are two main types of functions:

  • Supported Systems (get_models, get_dataloaders, get_compatibilities)
  • Get a MachineLearningInterface (get_mli)

When the GRPC server is connected to the Orchestrator, it will query the supported system functions to know what the MLI Factory can serve.

Later when the Orchestrator wants to run something on this Learner it will call get_mli with a model_arch_name, a dataloader_name and more parameters for both. The object returned is then used to run the experiment through the MLI.

"},{"location":"colearn/mli_factory/#supported-systems","title":"Supported Systems","text":"

The supported systems functions get_models and get_dataloaders should return a set of which will be stored (not currently implemented) in the api database. The idea being that the user can change these values on the UI while preparing to start/join an experiment."},{"location":"colearn/mli_factory/#examplemlifactory","title":"ExampleMliFactory","text":"

An example MLIFactory that will implement all the tasks in run_demo. This is the one used by contract_learn.

"},{"location":"colearn/tasks/","title":"1. CIFAR10 dataset","text":""},{"location":"colearn/tasks/#11-information-and-installation","title":"1.1. Information and installation","text":""},{"location":"colearn/tasks/#111-information-about-the-dataset","title":"1.1.1. Information about the dataset","text":"
  • The CIFAR-10 dataset consists of 60000 32x32x3 colour images in 10 classes, with 6000 images per class.
  • The 10 different classes represent airplanes, cars, birds, cats, deer, dogs, frogs, horses, ships, and trucks
  • Input for NN are raw 32x32 3 channels GRB images
  • NN output is distribution of probabilities for each class i.e. 10 values that sums up to 1

  • Code folder: here

  • Invoke parameter: -t CIFAR10
"},{"location":"colearn/tasks/#112-requirements","title":"1.1.2. Requirements","text":"
  • Cifar dataset is loaded from tensorflow.keras.datasets.cifar10 and no stored data are required
"},{"location":"colearn/tasks/#12-models","title":"1.2. Models","text":""},{"location":"colearn/tasks/#121-cifar10conv-keras-model","title":"1.2.1. CIFAR10Conv Keras model","text":"
_________________________________________________________________\nLayer (type)                    Output Shape        Param #   \n=================================================================\nInput (InputLayer)              (32, 32, 3)         0             \n_________________________________________________________________\nConv1_1 (Conv2D)                (32, 32, 64)        1792          \nbn1_1 (BatchNormalization)      (32, 32, 64)        256           \nConv1_2 (Conv2D)                (32, 32, 64)        36928         \nbn1_2 (BatchNormalization)      (32, 32, 64)        256           \npool1 (MaxPooling2D)            (16, 16, 64)        0             \n_________________________________________________________________\nConv2_1 (Conv2D)                (16, 16, 128        73856         \nbn2_1 (BatchNormalization)      (16, 16, 128        512           \nConv2_2 (Conv2D)                (16, 16, 128        147584    \nbn2_2 (BatchNormalization)      (16, 16, 128        512           \npool2 (MaxPooling2D)            (8, 8, 128)         0             \n_________________________________________________________________\nConv3_1 (Conv2D)                (8, 8, 256)         295168    \nbn3_1 (BatchNormalization)      (8, 8, 256)         1024          \nConv3_2 (Conv2D)                (8, 8, 256)         590080    \nbn3_2 (BatchNormalization)      (8, 8, 256)         1024          \nConv3_3 (Conv2D)                (8, 8, 256)         590080    \nbn3_3 (BatchNormalization)      (8, 8, 256)         1024          \n_________________________________________________________________\nflatten (Flatten)               (16384)             0             \nfc1 (Dense)                     (100)               1638500   \nfc2 (Dense)                     (10)                1010          \n=================================================================\nTotal params: 3,379,606\nTrainable params: 3,377,302\nNon-trainable params: 2,304\n_________________________________________________________________\n
"},{"location":"colearn/tasks/#122-cifar10conv2-keras-model","title":"1.2.2. CIFAR10Conv2 Keras model","text":"
_________________________________________________________\nLayer (type)                Output Shape        Param #   \n=========================================================\nInput (InputLayer)          (32, 32, 3)         0             \n_________________________________________________________\nConv1_1 (Conv2D)            (32, 32, 32)        896           \nConv1_2 (Conv2D)            (32, 32, 32)        9248          \npool1 (MaxPooling2D)        (16, 16, 32)        0             \n_________________________________________________________\nConv2_1 (Conv2D)            (16, 16, 64)        18496         \nConv2_2 (Conv2D)            (16, 16, 64)        36928         \npool2 (MaxPooling2D)        (8, 8, 64)          0             \n_________________________________________________________\nConv3_1 (Conv2D)            (8, 8, 128)         73856         \nConv3_2 (Conv2D)            (8, 8, 128)         147584    \npool3 (MaxPooling2D)        (4, 4, 128)         0             \n_________________________________________________________\nflatten (Flatten)           (2048)              0             \nfc1 (Dense)                 (128)               262272    \nfc2 (Dense)                 (10)                1290          \n=========================================================\nTotal params: 550,570\nTrainable params: 550,570\nNon-trainable params: 0\n_________________________________________________________\n
"},{"location":"colearn/tasks/#123-cifar10resnet50-keras-model","title":"1.2.3. CIFAR10Resnet50 Keras model","text":"
________________________________________________________\nLayer (type)                 Output Shape     Param #   \n========================================================\nInput (InputLayer)           (32, 32, 3)]     0             \n________________________________________________________\nresnet50 (Model)             (1, 1, 2048)     23587712  \n________________________________________________________\nGlobal_average_pooling2d     (2048)           0             \nflatten (Flatten)            (2048)           0             \nfc1 (Dense)                  (10)             20490         \n========================================================\nTotal params: 23,608,202\nTrainable params: 23,555,082\nNon-trainable params: 53,120\n________________________________________________________\n
"},{"location":"colearn/tasks/#2-covid-x-ray-dataset","title":"2. Covid X-RAY dataset","text":""},{"location":"colearn/tasks/#21-information-and-installation","title":"2.1. Information and installation","text":""},{"location":"colearn/tasks/#211-information-about-the-dataset","title":"2.1.1. Information about the dataset","text":"
  • The Covid X-Ray dataset consists of grayscale images, there are 478 covid images and 203 normal images.
  • To increase the number of images normal/pneumonia dataset is added
  • Final dataset, which is a combination of two previously mentioned datasets, contains 1434 images, 478 images for each class.
  • Images are cropped and resized to 512x512 pixel and spatial domain (Texture, GLDM, GLCM) and frequency domain (FFT and Wavelet) features are used to create 256 dimensional vector representation of each image. PCA is applied after to reduce dimensionality to 64 values which represents the first 64 highest eigenvalues of the covariance matrix.
  • Input for NN are 64 values for each image
  • NN output is distribution of probabilities for each class i.e. 3 values
  • Code folder: here
  • Invoke parameter: -t COVID
"},{"location":"colearn/tasks/#212-requirements","title":"2.1.2 Requirements","text":"
  • Download Covid dataset: here
  • Download pneumonia dataset: here
"},{"location":"colearn/tasks/#22-models","title":"2.2. Models","text":""},{"location":"colearn/tasks/#221-covid-xray-keras-model","title":"2.2.1. Covid XRAY Keras model","text":"
_________________________________________________________\nLayer (type)              Output Shape        Param #   \n=========================================================\ninput_1 (InputLayer)      (64)                0             \n_________________________________________________________\ndense (Dense)             (128)               8320          \ndropout (Dropout)         (128)               0             \n_________________________________________________________\ndense_1 (Dense)           (16)                2064          \ndropout_1 (Dropout)       (16)                0             \n_________________________________________________________\ndense_2 (Dense)           (3)                 51            \n=========================================================\nTotal params: 10,435\nTrainable params: 10,435\nNon-trainable params: 0\n_________________________________________________________\n
"},{"location":"colearn/tasks/#3-fraud-dataset","title":"3. FRAUD dataset","text":""},{"location":"colearn/tasks/#31-information-and-installation","title":"3.1. Information and installation","text":""},{"location":"colearn/tasks/#311-information-about-the-dataset","title":"3.1.1. Information about the dataset","text":"
  • EEE-CIS Fraud Detection, contains multiple files with credit card transactions
  • Raw dataset files are automatically merged and pre-processed and input files for neural network are created
  • X.csv with data - has 431 values for each transaction
  • Y.csv with labels - v has 1 value for each transaction

    • 0 = not a fraud
    • 1 = fraud
  • Code folder: here

  • Invoke parameter: -t FRAUD
"},{"location":"colearn/tasks/#312-requirements","title":"3.1.2. Requirements","text":"
  • Download dataset: here
"},{"location":"colearn/tasks/#32-models","title":"3.2. Models","text":""},{"location":"colearn/tasks/#321-frauddense1-keras-model","title":"3.2.1. FraudDense1 Keras model","text":"
_________________________________________________________\nLayer (type)             Output Shape          Param #   \n=========================================================\nInput (InputLayer)       (431)                 0             \n_________________________________________________________\ndense (Dense)            (512)                 221184    \nBatch_normalization      (512)                 2048          \n_________________________________________________________\ndense_1 (Dense)          (512)                 262656    \nBatch_normalization_1    (512)                 2048          \n_________________________________________________________\ndense_2 (Dense)          (512)                 262656    \nBatch_normalization_2    (512)                 2048          \n_________________________________________________________\nfc1 (Dense)              (1)                   513           \n=========================================================\nTotal params: 753,153\nTrainable params: 750,081\nNon-trainable params: 3,072\n_________________________________________________________\n
"},{"location":"colearn/tasks/#322-fraudsvm-scikit-learn-model","title":"3.2.2. FraudSVM Scikit-learn model","text":"
  • Model is defined as SGDClassifier(max_iter=1, verbose=0, loss=\"modified_huber\")
  • Which is support vector machine linear classifier
"},{"location":"colearn/tasks/#4-mnist","title":"4. MNIST","text":""},{"location":"colearn/tasks/#41-information-and-installation","title":"4.1. Information and installation","text":""},{"location":"colearn/tasks/#411-information-about-the-dataset","title":"4.1.1. Information about the dataset","text":"
  • This is a dataset of 70,000 28x28x1 grayscale images of the 10 digits
  • Input for NN are raw 28x28 1 channel images
  • NN output is distribution of probabilities for each class i.e. 10 values that sums up to 1

  • Code folder: here

  • Invoke parameter: -t MNIST
"},{"location":"colearn/tasks/#412-requirements","title":"4.1.2 Requirements","text":"
  • MNIST dataset is loaded from tensorflow.keras.datasets.cifar10 and no stored data are required
"},{"location":"colearn/tasks/#42-models","title":"4.2. Models","text":""},{"location":"colearn/tasks/#421-mnistconv-keras-model","title":"4.2.1. MNISTConv Keras model","text":"
_________________________________________________________\nLayer (type)                   Output Shape       Param #   \n=========================================================\nInput (InputLayer)             (28, 28, 1)        0             \n_________________________________________________________\nConv1_1 (Conv2D)               (28, 28, 64)       640           \nbn1 (BatchNormalization)       (28, 28, 64)       256           \npool1 (MaxPooling2D)           (14, 14, 64)       0             \n_________________________________________________________\nConv2_1 (Conv2D)               (14, 14, 128)      73856         \nbn4 (BatchNormalization)       (14, 14, 128)      512           \npool2 (MaxPooling2D)           (7, 7, 128)        0             \n_________________________________________________________\nflatten (Flatten)              (6272)             0             \nfc1 (Dense)                    (10)               62730         \n=========================================================\nTotal params: 137,994\nTrainable params: 137,610\nNon-trainable params: 384\n_________________________________________________________\n
"},{"location":"colearn/tasks/#422-mnist-pytorch-model","title":"4.2.2. MNIST Pytorch model","text":"
---------------------------------------------------------\nLayer (type)           Output Shape             Param #\n=========================================================\nInput                  [28,28,1]                0\nConv2d-1               [20, 24, 24]             520\nConv2d-2               [50, 8, 8]               25,050\nLinear-3               [500]                    400,500\nLinear-4               [10]                     5,010\n=========================================================\nTotal params: 431,080\nTrainable params: 431,080\nNon-trainable params: 0\n---------------------------------------------------------\n
"},{"location":"colearn/tasks/#423-mnistsupermini-keras-model","title":"4.2.3. MNISTSupermini Keras model","text":"
________________________________________________________________________________________\nLayer (type)                Output Shape    Param #      Connected to                         \n========================================================================================\ninput_1 (InputLayer)        (28, 28, 1)     0                                                \n________________________________________________________________________________________\nconv2d (Conv2D)             (26, 26, 8)     80           input_1[0][0]                        \nBatch_normalization         (26, 26, 8)     32           conv2d[0][0]                         \nMax_pooling2d               (13, 13, 8)     0            batch_normalization[0][0]            \ndropout (Dropout)           (13, 13, 8)     0            max_pooling2d[0][0]                  \n________________________________________________________________________________________\nSeparable_conv2d            (11, 11, 26)    306          dropout[0][0]                        \nbatch_normalization_1       (11, 11, 26)    104          separable_conv2d[0][0]               \ndropout_1 (Dropout)         (11, 11, 26)    0            batch_normalization_1[0][0]          \n________________________________________________________________________________________\nSeparable_conv2d_1          (11, 11, 26)    936          dropout_1[0][0]                      \n                                                         dropout_2[0][0]                      \n                                                         dropout_3[0][0]                      \n________________________________________________________________________________________\nBatch_normalization_2        (11, 11, 26)   104          separable_conv2d_1[0][0]             \ndropout_2 (Dropout)          (11, 11, 26)   0            batch_normalization_2[0][0]          \n________________________________________________________________________________________\nBatch_normalization_3        (11, 11, 26)   104          separable_conv2d_1[1][0]             \ndropout_3 (Dropout)          (11, 11, 26)   0            batch_normalization_3[0][0]          \n________________________________________________________________________________________\nBatch_normalization_4        (11, 11, 26)   104          separable_conv2d_1[2][0]             \ndropout_4 (Dropout)          (11, 11, 26)   0            batch_normalization_4[0][0]          \n________________________________________________________________________________________\nGlobal_average_pooling2d     (26)           0            dropout_4[0][0]                      \ndense (Dense)                (16)           432          global_average_pooling2d[0][0]   \nBatch_normalization_5        (16)           64           dense[0][0]                          \ndropout_5 (Dropout)          (16)           0            batch_normalization_5[0][0]          \ndense_1 (Dense)              (10)           170          dropout_5[0][0]                      \n========================================================================================\nTotal params: 2,436\nTrainable params: 2,180\nNon-trainable params: 256\n________________________________________________________________________________________\n
"},{"location":"colearn/tasks/#5-pneumonia-xray","title":"5. Pneumonia XRAY","text":""},{"location":"colearn/tasks/#51-information-and-installation","title":"5.1. Information and installation","text":""},{"location":"colearn/tasks/#511-information-about-the-dataset","title":"5.1.1. Information about the dataset","text":"
  • The Chest X-Ray Images (Pneumonia) dataset consists of 5856 grayscale images of various sizes in 2 classes (normal/pneumonia).
  • Labels are determined by folder name - NORMAL or PNEUMONIA
  • Input for NN are raw resized 128x128 1 channel images
  • NN output is distribution of probabilities for each class i.e. 2 values

  • Code folder: here

  • Invoke parameter: -t XRAY
"},{"location":"colearn/tasks/#512-requirements","title":"5.1.2 Requirements","text":"
  • Download dataset: here
"},{"location":"colearn/tasks/#52-models","title":"5.2. Models","text":""},{"location":"colearn/tasks/#521-xraysupermini-keras-model","title":"5.2.1. XraySupermini Keras model","text":"
_________________________________________________________________\nLayer (type)                     Output Shape            Param #   \n=================================================================\nInput (InputLayer)               [(128, 128, 1)]         0             \n_________________________________________________________________\nConv1_1 (Conv2D)                 (128, 128, 32)          320           \n_________________________________________________________________\nbn1 (BatchNormalization)         (128, 128, 32)          128           \n_________________________________________________________________\npool1 (MaxPooling2D)             (32, 32, 32)            0             \n_________________________________________________________________\nConv2_1 (Conv2D)                 (32, 32, 64)            18496         \n_________________________________________________________________\nbn2 (BatchNormalization)         (32, 32, 64)            256           \n_________________________________________________________________\nGlobal_max_pooling2d             (64)                    0             \n_________________________________________________________________\nfc1 (Dense)                      (1)                     65            \n=================================================================\nTotal params: 19,265\nTrainable params: 19,073\nNon-trainable params: 192\n_________________________________________________________________\n
"},{"location":"colearn/tasks/#522-xrayresnet50-keras-model","title":"5.2.2. XrayResnet50 Keras model","text":"
_________________________________________________________________\nLayer (type)                     Output Shape            Param #   \n=================================================================\nInput (InputLayer)               [(128, 128, 1)]         0             \n_________________________________________________________________\nresnet50 (Model)                 (4, 4, 2048)            23581440  \n_________________________________________________________________\nglobal_average_pooling2d         (2048)                  0             \n_________________________________________________________________\nflatten (Flatten)                (2048)                  0             \n_________________________________________________________________\nfc1 (Dense)                      (1)                     2049          \n=================================================================\nTotal params: 23,583,489\nTrainable params: 23,530,369\nNon-trainable params: 53,120\n_________________________________________________________________\n
"},{"location":"colearn/tasks/#523-xraypretrainedresnet50-keras-model","title":"5.2.3. XrayPretrainedResnet50 Keras model","text":"
_____________________________________________________________________________________\nLayer (type)                Output Shape    Param #   Connected to                \n=====================================================================================\nInput (InputLayer)          (128, 128, 1)   0                                       \n_____________________________________________________________________________________\nconcatenate (Concatenate)   (128, 128, 3)   0         Input[0][0]                                                                                     Input[0][0]                                                                                    Input[0][0]                 \n_____________________________________________________________________________________\ntf_op_layer_mul             (128, 128, 3)  0          concatenate[0][0]           \ntf_op_layer_strided_slice   (128, 128, 3)  0          tf_op_layer_mul[0][0]       \ntf_op_layer_BiasAdd         (128, 128, 3)  0          tf_op_layer_strided_slice[0][0]  \n_____________________________________________________________________________________\nresnet50 (Model)            (4, 4, 2048)   23587712   tf_op_layer_BiasAdd[0][0]            \n_____________________________________________________________________________________\nglobal_average_pooling2d    (2048)         0          resnet50[1][0]              \nflatten (Flatten)           (2048)         0          global_average_pooling2d[0][0]   \n_____________________________________________________________________________________\nfc1 (Dense)                 (1)            2049       flatten[0][0]              \n=====================================================================================\nTotal params: 23,589,761\nTrainable params: 23,536,641\nNon-trainable params: 53,120\n_____________________________________________________________________________________\n
"},{"location":"colearn/tasks/#524-xraydropout-keras-model","title":"5.2.4. XrayDropout Keras model","text":"
_________________________________________________________________\nLayer (type)                     Output Shape            Param #   \n=================================================================\nInput (InputLayer)               [(128, 128, 1)]         0             \n_________________________________________________________________\nConv1_1 (Conv2D)                 (128, 128, 128)         1280          \nbn1 (BatchNormalization)         (128, 128, 128)         512           \npool1 (MaxPooling2D)             (32, 32, 128)           0             \n_________________________________________________________________\nConv2_1 (Conv2D)                 (32, 32, 256)           295168    \nbn2 (BatchNormalization)         (32, 32, 256)           1024          \npool2 (MaxPooling2D)             (8, 8, 256)             0             \n_________________________________________________________________\nflatten (Flatten)                (16384)                 0             \nfc1 (Dense)                      (128)                   2097280   \nbn3 (BatchNormalization)         (128)                   512           \ndropout (Dropout)                (128)                   0             \n_________________________________________________________________\nfc2 (Dense)                      (64)                    8256          \nbn4 (BatchNormalization)         (64)                    256           \ndropout_1 (Dropout)              (64)                    0             \n_________________________________________________________________\nfc3 (Dense)                      (1)                     65            \n=================================================================\nTotal params: 2,404,353\nTrainable params: 2,403,201\nNon-trainable params: 1,152\n_________________________________________________________________\n
"},{"location":"colearn/tasks/#525-xraydropout2-keras-model","title":"5.2.5. XrayDropout2 Keras model","text":"
_________________________________________________________________\nLayer (type)                     Output Shape            Param #   \n=================================================================\nInput (InputLayer)               (128, 128, 1)           0             \n_________________________________________________________________\nConv1_1 (Conv2D)                 (128, 128, 64)          640           \nbn1 (BatchNormalization)         (128, 128, 64)          256           \npool1 (MaxPooling2D)             (64, 64, 64)            0             \n_________________________________________________________________\nConv2_1 (Conv2D)                 (64, 64, 128)           73856         \nbn2 (BatchNormalization)         (64, 64, 128)           512           \npool2 (MaxPooling2D)             (32, 32, 128)           0             \n_________________________________________________________________\nConv3_1 (Conv2D)                 (32, 32, 256)           295168    \nbn3 (BatchNormalization)         (32, 32, 256)           1024          \npool3 (MaxPooling2D)             (16, 16, 256)           0             \n_________________________________________________________________\nConv4_1 (Conv2D)                 (16, 16, 512)           1180160   \nbn4 (BatchNormalization)         (16, 16, 512)           2048          \npool4 (MaxPooling2D)             (8, 8, 512)             0             \n_________________________________________________________________\nConv5_1 (Conv2D)                 (8, 8, 512)             2359808   \nbn5 (BatchNormalization)         (8, 8, 512)             2048          \npool5 (MaxPooling2D)             (4, 4, 512)             0             \n_________________________________________________________________\nflatten (Flatten)                (8192)                  0             \nfc1 (Dense)                      (256)                   2097408   \nbn6 (BatchNormalization)         (256)                   1024          \ndropout (Dropout)                (256)                   0             \n_________________________________________________________________\nfc2 (Dense)                      (128)                   32896         \nbn7 (BatchNormalization)         (128)                   512           \ndropout_1 (Dropout)              (128)                   0             \n_________________________________________________________________\nfc3 (Dense)                      (64)                    8256          \nbn8 (BatchNormalization)         (64)                    256           \ndropout_2 (Dropout)              (64)                    0             \n_________________________________________________________________\nfc4 (Dense)                      (1)                     65            \n=================================================================\nTotal params: 6,055,937\nTrainable params: 6,052,097\nNon-trainable params: 3,840\n_________________________________________________________________\n
"},{"location":"colearn/tasks/#526-xrayvgg16-keras-model","title":"5.2.6. XrayVGG16 Keras model","text":"
_____________________________________________________________________________________\nLayer (type)                 Output Shape     Param #    Connected to                \n=====================================================================================\nInput (InputLayer)           (128, 128, 1)    0                  \n_____________________________________________________________________________________\nconcatenate (Concatenate)    (128, 128, 3)    0          Input[0][0]                 \n                                                         Input[0][0]                 \n                                                         Input[0][0]                 \n_____________________________________________________________________________________\ntf_op_layer_mul               (128, 128, 3)    0         concatenate[0][0]           \nTf_op_layer_strided_slice     (28, 128, 3)     0         tf_op_layer_mul[0][0]       \ntf_op_layer_BiasAdd           (128, 128, 3)    0         tf_op_layer_strided_slice[0][0]  \n_____________________________________________________________________________________\nvgg16 (Model)                 (4, 4, 512)      14714688  tf_op_layer_BiasAdd[0][0]            \n_____________________________________________________________________________________\nflatten (Flatten)             (8192)           0         vgg16[1][0]                  \nfc1 (Dense)                   (1)              8193      flatten[0][0]                \n=====================================================================================\nTotal params: 14,722,881\nTrainable params: 14,722,881\nNon-trainable params: 0\n_____________________________________________________________________________________\n
"},{"location":"colearn/tasks/#527-xraymini-keras-model","title":"5.2.7. XrayMini Keras model","text":"
_________________________________________________________________\nLayer (type)                     Output Shape            Param #   \n=================================================================\nInput (InputLayer)               [(128, 128, 1)]         0             \n_________________________________________________________________\nConv1_1 (Conv2D)                 (128, 128, 128)         1280          \nbn1 (BatchNormalization)         (128, 128, 128)         512           \npool1 (MaxPooling2D)             (32, 32, 128)           0             \n_________________________________________________________________\nConv2_1 (Conv2D)                 (32, 32, 256)           295168    \nbn2 (BatchNormalization)         (32, 32, 256)           1024          \npool2 (MaxPooling2D)             (8, 8, 256)             0             \n_________________________________________________________________\nflatten (Flatten)                (16384)                 0             \nfc1 (Dense)                      (1)                     16385         \n=================================================================\nTotal params: 314,369\nTrainable params: 313,601\nNon-trainable params: 768\n_________________________________________________________________\n
"},{"location":"colearn/tasks/#527-xrayonemb-keras-model","title":"5.2.7. XrayOneMB Keras model","text":"
_________________________________________________________________\nLayer (type)                   Output Shape            Param #   \n=================================================================\nInput (InputLayer)             (128, 128, 1)           0             \n_________________________________________________________________\nConv1_1 (Conv2D)               (128, 128, 64)          640           \nbn1_1 (BatchNormalization)     (128, 128, 64)          256           \nConv1_2 (Conv2D)               (128, 128, 64)          36928         \nbn1_2 (BatchNormalization)     (128, 128, 64)          256           \npool1 (MaxPooling2D)           (64, 64, 64)            0             \n_________________________________________________________________\nConv2_1 (Conv2D)               (64, 64, 64)            36928         \nbn2_1 (BatchNormalization)     (64, 64, 64)            256           \nConv2_2 (Conv2D)               (64, 64, 64)            36928         \nbn2_2 (BatchNormalization)     (64, 64, 64)            256           \npool2 (MaxPooling2D)           (32, 32, 64)            0             \n_________________________________________________________________\nConv3_1 (Conv2D)               (32, 32, 128)           73856         \nbn3_1 (BatchNormalization)     (32, 32, 128)           512           \nConv3_2 (SeparableConv2D)      (32, 32, 128)           17664         \nbn3_2 (BatchNormalization)     (32, 32, 128)           512           \npool3 (MaxPooling2D)           (16, 16, 128)           0             \n_________________________________________________________________\nConv4_1 (SeparableConv2D)      (16, 16, 128)           17664         \nbn4_1 (BatchNormalization)     (16, 16, 128)           512           \n_________________________________________________________________\nConv4_2 (SeparableConv2D)      (16, 16, 128)           17664         \nbn4_2 (BatchNormalization)     (16, 16, 128)           512           \n_________________________________________________________________\npool4 (AveragePooling2D)       (4, 4, 128)             0             \nflatten (Flatten)              (2048)                  0             \n_________________________________________________________________\nfc1 (Dense)                    (1)                     2049          \n=================================================================\nTotal params: 243,393\nTrainable params: 241,857\nNon-trainable params: 1,536\n_________________________________________________________________\n
"},{"location":"create-react-app/example_dapp/","title":"\"Hello Cosmos\"","text":"

\"Hello World\" but with friends!

Projects generated from the template will include a fully functional example DApp frontend, integrating with the current Fetch test-net directly as well as a cosmwasm contract deployed to the test-net.

The \"Hello Cosmos\" example DApp demonstrates the following kinds of interactions:

  • CosmWasm contract query
  • CosmWasm contract call
  • Native token transfer
  • Fetch / Keplr wallet integration
"},{"location":"create-react-app/example_dapp/#features","title":"Features","text":"
  • Print greetings submitted by other users
  • Connect with Fetch wallet
    • Print address
    • Print balance
  • Submit greeting
    • (one greeting per address)
  • Tip other users
    • (half testnet balance)
"},{"location":"create-react-app/introduction/","title":"Create React App Template","text":"

Create React App is a CLI utility which facilitates generation of react app boilerplate via templates.

In order to expedite distributed application (DApp) development, we maintain a create-react-app template: cra-template-cosmjs-keplr. Projects generated from this template include all the necessary dependencies and build configuration needed to:

  • Interact with the Fetch or Keplr wallet
    • fetchai/fetch-wallet (github)
  • Interact with cosmos-based networks
    • @cosmjs/stargate (npm)
    • @cosmjs/cosmwasm-stargate (npm)

To generate a new project:

npx create-react-app --template cosmjs-keplr\n

The generated project also comes with an example DApp, \"Hello Cosmos\", which demonstrates Cosmjs and Keplr API usage in the form of a simple frontend.

"},{"location":"fetch-wallet/","title":"Getting Started","text":"

The Fetch Wallet, originally forked from the Keplr wallet, is a generic wallet for interacting with the Fetch blockchain network and other ledgers built using the Cosmos-SDK, and supports the Inter-Blockchain Communication (IBC) protocol.

Some of its highlights include:

  • Private keys and mnemonics are encrypted (using scrypt) and are stored locally on device. This means neither Fetch nor the websites visited have access to these sensitive data.
  • Multiple accounts on the Fetch ledger and other Cosmos-based ledgers.
  • Support for Ledger hardware wallets for enhanced security.
  • Native and IBC token transfers.
"},{"location":"fetch-wallet/#compatibility","title":"Compatibility","text":"

The Fetch wallet works on all Chromium-based web browsers, including Chrome, Brave, Edge and Decentr.

"},{"location":"fetch-wallet/#get-the-wallet","title":"Get the Wallet","text":"

Install the Fetch wallet from the Chrome web store.

Info

At this time, you cannot run the Keplr and Fetch wallets together because they interfere. Please disable the Keplr wallet before using the Fetch wallet.

"},{"location":"fetch-wallet/#version","title":"Version","text":""},{"location":"fetch-wallet/#first-time-use","title":"First-time use","text":"

The first time you open the wallet, you will see the following options:

  • Create a new account
  • Import existing account
  • Import ledger
  • Migrate from ETH

After account creation or import is completed, the wallet will be accessible from your browser's extensions.

"},{"location":"fetch-wallet/#how-to-contribute","title":"How to contribute","text":"

You can contribute to this project by engaging with its repository on GitHub:

GitHub repository

"},{"location":"fetch-wallet/account_management/","title":"Account Management","text":""},{"location":"fetch-wallet/account_management/#welcome-page","title":"Welcome Page","text":"

You can create a new account, import an existing account, connect your hardware wallet, or recover migrated Ethereum accounts from the welcome page. To get there:

"},{"location":"fetch-wallet/account_management/#first-time-use","title":"First time Use","text":"

The first time you open the wallet, you will be presented with the welcome page.

"},{"location":"fetch-wallet/account_management/#not-the-first-time","title":"Not the First Time","text":"
  1. Ensure you are logged into the wallet.
  2. Click the account icon in the top right corner of the dashboard, then + Add Account.

Info

In some contexts, the term account refers to an address that has, at some point in time, had a balance (and therefore a state on the ledger). In this context, it is not necessary for an account to have a balance, for example for it to be imported.

"},{"location":"fetch-wallet/account_management/#creating-a-new-account","title":"Creating a new account","text":"

Using the wallet, you can create a new account and address on the Fetch ledger:

  1. On the welcome page, click Create new account.
  2. Choose a 12 or 24 word long mnemonic seed and securely back it up.

    Warning

    KEEP IT SAFE! Anyone with your mnemonic seed can access your wallet and take your assets.

    Danger

    DON'T LOSE IT! Lost mnemonic seed cannot be recovered! If you lose your mnemonic seed you will lose access to your wallet.

  3. Give your account a name and set a password if one is not set (i.e. if it is the first time you open the wallet, or in case you have removed all of your accounts). The password will be used the next time you want to use the wallet or make important changes to your account. Hit Next.

  4. Rearrange the mnemonic phrases by clicking on them in the correct order to confirm your mnemonic seed. Then click Register.
"},{"location":"fetch-wallet/account_management/#existing-account","title":"Existing account","text":""},{"location":"fetch-wallet/account_management/#importing-an-existing-account","title":"Importing an existing account","text":"

If you have an account on the Fetch network, for example having had one already on the Fetch wallet and want to access it again, have an account on another wallet (e.g. Cosmostation, Keplr, ...) and wish to bring it to the Fetch wallet, or having created an address using one of our tools (e.g. the AEA framework), you can import it into the Fetch wallet:

  1. On the welcome page, click Import existing account.
  2. Enter your mnemonic seed (set of words) or private key (hexidecimal).

    Warning

    KEEP IT SAFE! Anyone with your mnemonic seed or private key can access your wallet and take your assets.

    Tip

    It is more convenient, and preferable, to use the mnemonic over private keys.

  3. Give your account a name and set a password if one is not set (i.e. if it is the first time you open the wallet, or in case you have removed all of your accounts). The password will be used the next time you want to use the wallet or make important changes to your account. Hit Next.

"},{"location":"fetch-wallet/account_management/#using-a-hardware-wallet","title":"Using a Hardware Wallet","text":"

If you have a Ledger hardware wallet and wish to keep your key and mnemonics on that device while using the Fetch wallet:

Info

Currently only ledger hardware wallets are supported.

  1. On the welcome page, click Import ledger.
  2. Give your account a name and set a password if one is not set (i.e. if it is the first time you open the wallet, or in case you have removed all of your accounts). The password will be used the next time you want to use the wallet or make important changes to your account. Hit Next.
  3. Follow the instructions on the popup to connect your device.

Warning

Please ensure you keep your mnemonic seed somewhere safe where others cannot access it. If you lose it, your wallet will be inaccessible once you log out. The password for your account should also be kept safe but is not necessary for recovery if you have your mnemonic seed.

Info

If you lose your password, you need to uninstall and re-install the Fetch wallet and select Import existing account. Then use the mnemonic seed for your account and choose a new password.

"},{"location":"fetch-wallet/account_management/#switching-accounts","title":"Switching accounts","text":"

If you have multiple accounts set up on the Fetch wallet, to switch between them:

  1. Ensure you are logged into the wallet.
  2. Click the account icon in the top right corner of the dashboard.
  3. Select the account you want to switch to.
"},{"location":"fetch-wallet/account_management/#removing-an-account","title":"Removing an Account","text":"

To remove an account from your Fetch wallet:

  1. Ensure you are logged into the wallet.
  2. Click the account icon in the top right corner of the dashboard.
  3. Hit ... (the three dots icon) for the account you want to remove and choose Delete Account.
  4. Enter your wallet password.

    Warning

    If you have not yet backed up your mnemonic seed, click on Back-up account and enter you password to view it. Then back it up safely. If you lose your mnemonic seed you will lose access to your account.

  5. Click Confirm to remove the account from your wallet.

"},{"location":"fetch-wallet/address_book/","title":"Address Book","text":"

The Fetch wallet's Address Book makes it easy to transfer funds to accounts you frequently use. To add an address to the address book:

  1. From the wallet dashboard click the icon in the top left, then Address Book.
  2. Select the network this address belongs to, then + Add New.
  3. Fill in the details:
    • Name: the name you give to this account
    • Address: the account's address
    • Default memo: the memo used by default when sending funds to this account
  4. Hit Save.

Tip

When sending funds, instead of writing the address of the recipient you can hit the icon to the right to select an account from the address book.

"},{"location":"fetch-wallet/connections/","title":"Connections","text":"

Some webpages can connect to your wallet and enable interactions that are otherwise not possible.

Example

By default, the fetch ledger browser only shows a list of active validators for the Fetch network along with some staking-related details (e.g. the validators' commission rates and number of delegated FETs).

However if you have your wallet set up on the browser, you will see a button for connecting your wallet in the top right of the page, and if you do, you will get the option of staking your FETs with any of the validators on the list and accessing your staking dashboard. To learn more, visit the staking guide.

If you view such webpages using a browser with the Fetch wallet, you will see additional options. If you use a browser with no wallet, those options will not be shown.

You can manage these connections on the wallet:

  1. From the wallet dashboard, click the icon in the top left, then Settings.
  2. Click on Manage Connections.

    Info

    Here you will see a list of webpages which have been connected to your wallet. Visiting these pages will not ask you for a connection with your wallet again; the page will automatically connect.

    You have the option of removing and thus revoking these connections. If you do, the next time you visit that page, it will not automatically connect to your wallet.

  3. To remove a connection, click the icon on the connection you wish to remove.

"},{"location":"fetch-wallet/deposit/","title":"Deposit Tokens","text":"

To transfer funds to your account on the Fetch wallet:

In the wallet or application you are using to send the funds, use your account's address as the destination account to which the funds must go.

"},{"location":"fetch-wallet/deposit/#to-copy-your-accounts-address","title":"To copy your account's address","text":"
  1. Ensure you are logged into the wallet.
  2. Either click on the account address at the top of the dashboard (under the account name):
  3. Or select Deposit and scan the QR code.

Once you send the tokens, the balance should be updated.

Failure

If your origin wallet says that the address (which should start with \"fetch\") is invalid, it is probably expecting an Ethereum address (beginning with \"0x\") and is most likely trying to send ERC20 FET. In this case, you need to use the token bridge to swap your ERC20 FET for native FET.

Warning

You should not send ERC20 FET to this wallet. If you do, you will lose your tokens. The Fetch wallet can only hold native FET tokens and not ERC20 FET tokens.

"},{"location":"fetch-wallet/migrate_erc20/","title":"Recover Migrated Ethereum Account","text":"

Prior to the transition to our native Cosmos SDK-based ledger, ERC20 FETs staked using accounts on Ethereum were migrated to native accounts on the Fetch main network. These accounts correspond to the private key of their associated Ethereum accounts.

These native accounts can be accessed by transforming the original Ethereum keypair into a native Fetch keypair and address:

  1. On the welcome page, click Migrate from ETH.
  2. In the next page, click Migrate a Metamask Private Key.
  3. Enter the address and private key of the Ethereum account which staked ERC20 FETs.
  4. Give your account a name and set a password if one is not set (i.e. if it is the first time you open the wallet, or in case you have removed all of your accounts). The password will be used the next time you want to use the wallet or make important changes to your account. Hit Next.
"},{"location":"fetch-wallet/send_tokens/","title":"Send Tokens","text":"

On this page, you can find instructions on how to send tokens using native transfers and the Inter-Blockchain Communication (IBC) protocol.

"},{"location":"fetch-wallet/send_tokens/#native-network-transfer","title":"Native network transfer","text":"
  1. Ensure you are logged into the wallet.
  2. Select Send.
  3. Fill in the details of your transaction:
    • Recipient: the address you want to send the tokens to
    • Token: the token denomination or type
    • Amount: the number of tokens you want to send with this transaction (you can see your current balance above the Amount)
    • Memo (Optional): some transactions (e.g. to/from some exchanges) require a specific memo. If not needed, you can leave it blank.
    • Fee: the transaction fee. Choose from Low, Average and High

    Tip

    Usually, the lower the transaction fee, the longer you need to wait for your transaction to be settled on the network.

  4. Press Send.
  5. In the summary screen, review the details and if everything is correct, select Approve.

Info

You can check the status of your transaction via the explorer.

"},{"location":"fetch-wallet/send_tokens/#ibc-transfer","title":"IBC transfer","text":"
  1. Ensure you are logged into the wallet.
  2. Make sure IBC transfers are enabled and that your selected network supports it.

    Tip

    To enable IBC transfers in the wallet:

    From dashboard, click the icon in the top left, then Settings. Toggle the Show Advanced IBC Transfers switch on.

    Note

    If your selected network does not support IBC transfers, you will not see an IBC Transfer section in the dashboard.

  3. Click the Transfer button in the IBC Transfer section.

  4. Fill in the details of your transaction:

    • Destination Chain: the destination blockchain.

      Info

      If you do not see your desired chain, you need to set up IBC channels first.

    • Recipient: the address you want to send the tokens to

    • Memo (Optional): an optional memo.
  5. Press Next.

  6. In the next page, provide the following:
    • Token: the denomination or type of the tokens being sent
    • Amount: the number of tokens to be sent
    • Fee: the transaction fee. Choose from Low, Average, High.
  7. Hit Submit.
  8. The wallet now shows you a summary of the transaction. Review it and if you are happy, hit Approve to complete the transfer.

Warning

Do not send tokens via IBC directly to an exchange. In most cases, this will result in the loss of your funds.

"},{"location":"fetch-wallet/send_tokens/#first-time-origindestination-transfer","title":"First-time origin/destination transfer","text":"

Before being able to make an IBC transfer between any two chains for the first time, an IBC channel must be configured in the wallet:

  1. Follow the instructions for making an IBC transfer, up to Step 4.
  2. Click the Select Chain drop-down.
  3. Select + New IBC Transfer Channel.
  4. Select the Destination Chain and enter the source Channel ID (e.g. channel-100).

    Tip

    To find out the IBC channel ID between any two chains:

    1. Head over to this page
    2. At the top, select the sending chain, for example Fetch.AI. Then below it, click IBC RELAYERS.
    3. Select the destination chain, for example OSMOSIS.
    4. You can now see the channels between the two chains. Select an active channel (in green) and note the sending chain's channel ID. For Fetch.AI to Osmosis, the sending chain's (Fetch.AI's) channel ID is channel-10.
    5. Enter the channel ID in the Fetch wallet (channel-10 in our example).

    Info

    Remember to write the channel ID in lower case (i.e. channel-X)

    Warning

    If there are no green channels, the relayers are temporarily inactive. You need to wait until one becomes active again.

  5. Click Save.

Failure

If you input an incorrect Channel ID, either the wallet will not accept it and shows you an error, or your transaction could get stuck in an inactive channel.

"},{"location":"fetch-wallet/stake/","title":"Staking","text":"

Clicking the Stake button on the Fetch wallet dashboard opens the Fetch ledger browser. From here, you can connect your wallet and stake your FETs. To learn how, follow the staking guide.

"},{"location":"fetch-wallet/stake/#claim-rewards","title":"Claim rewards","text":"

If your account has any tokens staked, you can withdraw your rewards from the wallet:

  1. Ensure you are logged into the wallet.
  2. From the wallet dashboard, select Claim.

    Info

    This will claim the total rewards accrued for your stakes across every validator.

  3. The wallet shows you a summary of the transaction. Review it, select a transaction fee, and if you are happy, hit Approve to complete the operation.

You should now see the rewards added to your Total Balance.

Info

To claim rewards from each validator individually, see the claim rewards using the staking dashboard.

"},{"location":"learn_the_concepts/agent-based_and_multi-agents_systems/","title":"Agent-Based and Multi-Agents Systems","text":"

An agent is a piece of software that represents an entity (individual, organisation, or object) and acts continuously and autonomously (with limited or no interference) on their behalf.

The most important defining characteristic of an agent is its autonomy, that is the ability to act on its own without external direction from its owner in response to situations it encounters.

Note

What differentiates agents with other software paradigms (e.g. smart contracts, web apps) is that as well as being reactive, meaning they respond to other agents and changes in their environments, they are also proactive, which means that they take the initiative and perform actions to achieve their goals.

"},{"location":"learn_the_concepts/agent-based_and_multi-agents_systems/#key-features-of-agents","title":"Key features of agents","text":"
  1. Representation: Agents are owned by and operate on behalf of an entity, for example an individual, family, company, government, or object, and look after their owner's interests.

  2. Autonomy: Agents operate with limited or no interference and do not need to be constantly told what to do. They perform actions continuously according to their internal reasoning system.

  3. Self-Interested: Each agent primarily looks after its own interests (which is aligned with those of its owner) and not necessarily the interests of other agents.

  4. Proactive: Agents have goals to achieve allowing them to take the initiative and perform actions that get them closer to achieving their goals. This means an agent typically compares the outcome of different actions relative to its goals and selects the one that takes it closer to them.

  5. Reactive: Agents respond to other agents, services, etc., and changes in their environments. A great example of this behaviour is a heating system with a thermostat that constantly monitors its environment and turns the heating on or off when the temperature changes.

Note

Agent characteristics and behaviors may vary in their extent and sophistication. An agent may differ from other agents, in the amount of information in its decisions process, its internal models of the external world, its view of the possible reactions of other agents in response to its actions, and the size of the memory of past events the agent retains and uses in making its decisions. Further instances of differentiating factors are the amounts of resources used by agents or accumulated as a result of their interactions.

"},{"location":"learn_the_concepts/agent-based_and_multi-agents_systems/#agent-based-modelling","title":"Agent-Based Modelling","text":"

In agent-based modelling (ABM), a system is represented as a group of independent decision-making units, i.e. agents. Each agent evaluates its own circumstances and makes decisions based on a set of rules. Agents can act in a variety of ways that are suitable for the system they are representing (e.g. producing, consuming, or selling). An agent-based model, at its most basic form, consists of a system of agents and the connections between them. Even a straightforward agent-based model has the potential to display intricate behavioural patterns and provide important details about the behaviour of the emulated real-world system.

Agent-based modelling is used in various application areas, spanning the physical, biological, social, and management sciences. Agent-based models have also been developed in the fields of economics, sociology, anthropology, and cognitive science. Various social phenomena have been investigated using agent-based models that are not easily modeled using other approaches.

Example

Agent-based models can be used to analyze existing and hypothetical markets, for instance, modelling possible futures for a market directed towards space tourism, to analyze how companies represented by agents would compete and offer products to customers in this hypothetical market.

"},{"location":"learn_the_concepts/agent-based_and_multi-agents_systems/#multi-agents-systems","title":"Multi-Agents Systems","text":"

A Multi-Agents System (MAS) is a group of agents that interact with each other and the environment to achieve specific goals. In such systems, agents may not have full knowledge of both the environment and the internal state of other agents.

Note

Interactions between agents is an important feature that enables them to use knowledge of other agents and learn more about the environment in a compressed time period. This type of interaction may be cooperative or competitive. In a cooperative interaction, agents work with each other towards a common goal. The aim of this interaction is to enable agents to distribute and share their knowledge and use the intelligence and capabilities of each other to solve problems. In a competitive interaction, agents may compete to obtain individual resources and achieve individual goals.

"},{"location":"learn_the_concepts/agent-based_and_multi-agents_systems/#benefits","title":"Benefits","text":"
  1. Decentralization: multi-agents systems are decentralized. The authority to make management decisions about the system is distributed among the participants. A multi-agents system is run for its participants by its participants.

  2. Heterogeneity: agents in multi-agent systems are owned and operated by different entities and so are typically not uniformly designed. For instance, the agents may be composed of different hardware or software components.

  3. Scalability: multi-agents systems are easy to scale. You simply add agents to the system to grow the system.

  4. Robustness: multi-agents systems are intrinsically very robust as no single failure point takes down the system.

  5. Adaptability: multi-agents systems adapt to changing circumstances effectively as each agent autonomously adjusts to changing circumstances.

"},{"location":"learn_the_concepts/agent-based_and_multi-agents_systems/#drawbacks","title":"Drawbacks","text":"
  1. Complexity: multi-agents systems are quite complex to set up, maintain and troubleshoot. You have to use a pre-existing framework or build one, and getting all the agents to communicate effectively together can be a challenge. As these are decentralized systems (i.e. no central authority), managing the system is more complicated than a centralized one, as participants need to be involved in management decisions. The complexity of MASs can increase rapidly with the number of agents, the interactions among them, and the complexity of their behavior.

  2. Unpredictability: with every agent acting in their own interest, it might be easy to predict what an individual agent is trying to achieve. However, it is much more difficult to predict the direction the whole system moves towards, as this is the result of the many interactions between every individual agent in the system.

Multi-agent systems have received tremendous attention from scholars in different disciplines, ranging from computer science to civil engineering, in order to solve complex problems by subdividing them into smaller tasks. The individual tasks are allocated to autonomous agents, and each one of them decides on a proper action to solve the task using multiple inputs.

"},{"location":"learn_the_concepts/glossary/","title":"Glossary","text":"
  • ACN (Agent Communication Network): it is a peer-to-peer communication network for autonomous economic agents.

  • Address: an address is a string of alphanumeric characters (that may also be seen as a QR code that can be read by a smartphone) and it is used on a blockchain network to transmit and receive transactions. The address in Ethereum starts with 0x (e.g. ) and is also called public key.

  • AEA (Autonomous Economic Agent): it is an intelligent agent acting on an owner's behalf, with limited or no interference, and whose goal is to generate economic value to its owner. AEAs are a special type of agent.

  • AEA Registry: it is a repository of packages for the Autonomous Economic Agent (AEA) framework. Five types of packages are currently supported: entire agents and four agent components, including skills, connections, protocols and contracts.

  • Airdrop: it is a mean of distributing tokens to wallet addresses using digital money or tokens. Airdrops are occasionally used for marketing in return for easy actions like sharing, referring, or downloading an app.

  • Altcoin: any cryptocurrency alternative to Bitcoin. Many alternative cryptocurrencies are modified forks of Bitcoin (e.g. Litecoin).

  • AML (Anti-Money Laundering): this refers to a group of international rules implemented to reduce the likelihood that criminal organizations or individuals would use money laundering. In different countries, these guidelines and legislation are applied to cryptocurrencies with diverse results.

  • Arbitrage: arbitrage is the simultaneous purchase and sale of the same asset in different markets in order to profit from tiny differences in the asset's listed price.

  • API (Application Programming Interface): it is a software bridge that enables communication between two independent programmes. APIs specify how different components will communicate with one another.

  • Automated Market Maker: an automated market maker (AMM) is a system that automatically facilitates buy and sell orders on a decentralized exchange. In contrast to regular market makers, AMMs function by using self-executing computer programs, also known as smart contracts. These smart contracts automatically clear transactions between buyers and sellers.

  • BEP-20: according to Binance Academy, \"BEP-20 is a token standard on Binance Smart Chain that extends ERC-20, the most common Ethereum token standard. You can think of it as a blueprint for tokens that defines how they can be spent, who can spend them, and other rules for their usage. Due to its similarity to Binance Chain\u2019s BEP-2 and Ethereum\u2019s ERC-20, it\u2019s compatible with both.BEP-20 was conceived as a technical specification for Binance Smart Chain, with the goal of providing a flexible format for developers to launch a range of different tokens. These could represent anything from shares in a business to dollars stored in a bank vault\".

  • Bid and Ask Prices: the term bid and ask refers to a two-way price quotation that indicates the best potential price at which a security can be sold and bought at a given point in time. The bid price represents the maximum price that a buyer is willing to pay for a share of stock or other security. The ask price represents the minimum price that a seller is willing to take for that same security. A trade or transaction occurs when a buyer in the market is willing to pay the best offer available, or is willing to sell at the highest bid. The difference between bid and ask prices, or the spread, is a key indicator of the liquidity of the asset. In general, the smaller the spread, the better the liquidity.

  • Block: it is an essential part of every blockchain. Imagine a blockchain as a continuously updated ledger that is synchronized between any number of distinct nodes (in fact, the term \"distributed ledger technology\" is also used to describe it). A block of transactions is cryptographically locked together and formally recorded when a predetermined number of transactions have been added to the ledger and consensus has been obtained among the nodes that the transactions are authentic. This \"block\" serves as the foundation for the following one. As a result, they are all connected in a chain, thus the name blockchain.

  • Block Depth: A block's position index in the blockchain relative to the latest (most recently added) block. For instance, a block that is five blocks before the latest block will have a block depth of 5.

  • Block Explorer: it is a software with a graphical user interface (GUI), that allows users to read and analyze the data contained on a blockchain.

  • Block Height: a block's position index in the blockchain relative to the genesis (zeroeth) block. It represents the number of blocks on a blockchain that are interconnected. For instance, the initial block (i.e. Genesis Block), would have height 0 whereas the 5th block added to a chain would have a block height of 5.

  • Block Reward: the payment made to a miner for successfully hashing a block of transactions. Cryptocurrencies and transaction fees are two possible types of block rewards. Whether or not all of the coins have previously been successfully mined depends on the policy followed by the relevant cryptocurrency.

  • Block Period: the length of time it takes for a block of transactions to be confirmed by the network, either by miners under PoW or by validators under PoS, is referred to as \"block time.\" Also see \"Proof of Work\" and \"Proof of Stake\".

  • Blockchain: the blockchain is a digital ledger of all the transactions ever made in a particular cryptocurrency. It\u2019s composed of individual blocks that are chained to each other through a cryptographic signature. Each time a block\u2019s capacity is reached, a new block is added to the chain. The blockchain is repeatedly copied and saved onto thousands of computers all around the world, and it must always match each copy. As there is no master copy stored in one location, it\u2019s considered decentralized.

  • Bug: mistakes in software or other aspects of a program. These mistakes may produce an error in the form of unexpected results or erratic behavior. In the best case, a bug may only affect software performance. In the worst case, it may make the software crash.

  • Bug Bounty: a reward given for disclosing bugs and vulnerabilities in computer programming.

  • Circulating Supply: this term refers to the number of cryptocurrency coins or tokens that are publicly available and circulating in a specific market. The circulating supply of a cryptocurrency can increase or decrease over time.

  • Cold Wallet: a physical storage device used to store cryptocurrencies offline (e.g. a flash drive, hard disk).

  • Collateral: a collateral is something of value given as a guarantee to obtain something else. For instance, a borrower may offer their car as a collateral to a lender when taking out a loan. The vehicle acts as a safeguard or warranty in case the borrower fails to pay their debts. Usually, collateralized loans present much lower interest rates when compared to non-collateralized ones. Collateral can come in different forms. Some of the most common types include mortgage collaterals, invoice financing and margin trading collaterals.

  • Collateralization Ratio: to borrow tokens from a DeFi protocol, one must put up more collateral than the loan is worth. Each protocol can use different collateralization ratios. However, users must maintain the designated ratio to prevent liquidation (i.e. liquidation ratio).

  • Command Line Interface (CLI): it is a text-based user interface. CLIs can provide more core functionality and access to system resources than a graphical user interface (GUI), but at the cost of usability. Because of this, CLIs are generally directed toward developers over the average user. They can be used to demonstrate the functionality underlying a program without expending development time building a more robust user interface.

  • Confirmation: when the blockchain transaction has been validated by the network, a confirmation occurs. This occurs through a process called mining under a Proof of Work (PoW) consensus mechanism and as validation under a Proof of Stake (PoS) consensus mechanism. Theoretically, a transaction that has been successfully confirmed cannot be changed or double spent. The difficulty of a double spend assault increases with the number of confirmations a transaction has.

  • Consensus: the process through which a number of peers, also known as nodes, on a blockchain network come to an agreement over the legitimacy of transactions presented to the network. Proof of Work (PoW) and Proof of Stake (PoS) are the two main consensus procedures.

  • Cryptocurrency: it is a digital currency which has a mathematical foundation that controls the generation of units of currency and using encryption methods to confirm the transfer of payments. Distributed ledger technology is used to keep track of cryptocurrency transactions, which are carried out without the assistance of a central bank.

  • Cryptography: it is the process of encoding data into unintelligible codes to make it secure and hidden. Only the required key may be used to decode and read the data.

  • Crypto Wallet: these wallets store public and private keys allowing users to send, receive, and store tokens. The tokens reside on the blockchain and the wallet accesses them.

  • DAO: a Decentralized Autonomous Organization (DAO) is a collection of individuals that get together without having any decisions made for them by a centralised authority figure or corporation. These are constructed using smart contracts on a blockchain. Members of DAOs frequently pay their way in by buying a governance token designed just for the DAO, which gives them the right to vote on decisions affecting how the money fund is used and handled. People from all around the world may be a part of these organisations, and they frequently interact on Discord channels.

  • Decentralization: it is the term used to describe the transition of power and control to a decentralized system from a centralized structure, government, or party.

  • Decentralized Application (DApp): a computer program that utilizes a blockchain for data storage, runs autonomously, is not controlled or operated from a single entity, is open source and has its use incentivized by the reward of fees or tokens.

  • Decentralized Exchange (DEX): this is a platform which uses smart contracts to exchange cryptocurrencies. Peer-to-peer trading takes place between liquidity pools. This contrasts with centralised exchanges, which are more comparable to cryptocurrency-focused banks or financial firms. The two have significant, ever-changing technological and regulatory variances from one another.

  • Decentralized Finance (DeFi): DeFi stands for decentralized finance. Traditional finance has always relied on a reputable middleman, and because of this, it has always been centralized. For instance, if you need to send money to a friend or family, you depend on your bank to transfer the funds to their account. DeFi, on the other hand, does not need any middlemen. Direct asset transfers are possible between participants. Theoretically, this speeds up and reduces the cost of transactions.

  • Deflationary Token: tokens are deflationary if a percentage is permanently removed from the marketplace over time. Buybacks and burns are a popular way of destroying tokens. This causes scarcity which hopefully makes the price rise.

  • Digital Asset: a scarce, electronically transferred, intangible digital asset having a market price.

  • Digital Identity: a networked or online identity that a person, business, or technological item adopts.

  • Digital Signature: a public key encryption-generated code that is added to an electronic document transmission to validate the document's contents.

  • Distributed Denial of Service (DDoS) Attack: a cyberattack strategy where the attacker repeatedly floods the system with requests in an effort to block the fulfillment of valid requests.

  • DLT (Distributed Ledger Technology): it is a database of data that is duplicated and shared among a network of computers spread out across many locations. It is a flexible way of information recording as opposed to a centralised ledger. Instead of being updated by a centralised authority, the ledger's or record's data is changed by network participants. The information kept on the ledger can be checked and audited, and it can be accessible to some or all users. Value-containing peer-to-peer transfers may be documented on a DLT. The transferred value could be cash, securities, or even private data.

  • Double Spend Attack: it is a malicious attempt to convince two separate parties that one of two conflicting transactions is valid. In such a situation, both transactions appear individually valid, but their combination is not. Thus, only one is included in the blockchain. Due to the nature of blockchain reorganizations, simply showing that a transaction is included in a block is not enough to verify that it is immutable. Transactions are only immutable once they have reached a depth in the chain where a chain reorganization is unlikely to affect them. Double spend attacks can be mitigated by waiting to ensure that a transaction is confirmed by the network and is acceptably immutable before acting on it.

  • ERC-20: it is the standard to which each Ethereum token complies. This standard defines the way each token behaves so that transactions are predictable. Other cryptocurrencies also use the ERC-20 standard, piggybacking on the Ethereum network in the process.

  • ERC-721: this is an additional standard for Ethereum smart contracts that permits the creation of non-fungible tokens, or NFTs. This token standard is used to symbolize a distinct digital asset that cannot be exchanged.

  • Exchange: service for trading cryptocurrency tokens for other tokens or fiat. Exchanges are highly regulated in the European Union, eastern Asia, and the United States of America; thus, many exchanges are located in countries with less oversight.

  • Fiat: according to Investopedia's definition, \"fiat money is a government-issued currency that is not backed by a physical commodity, such as gold or silver, but rather by the government that issued it. It usually requires fiat exchanged at a CEX or through local means such as Bitcoin ATMs to be able to purchase cryptocurrency with fiat currency\".

  • Fork: a fork establishes a different version of a blockchain and is frequently purposefully carried out to update a network. Hard forks result in a new version of the chain that must be embraced in order to continue participating, whereas soft forks result in two chains that are somewhat compatible. A disputed hard fork may result in the creation of two distinct blockchain networks.

  • Fork (Hard): type of fork that is permanently incompatible with the original network. Hard forks typically change transaction data structures, consensus algorithms, or add/remove blocks that would not have otherwise been included. For example, Bitcoin Cash is a hard fork of Bitcoin, and Ethereum Classic is a hard fork of Ethereum.

  • Fork (Soft): type of fork that is compatible with the data on the original chain. Blocks created on the original chain after a soft fork would be valid on the forked chain; however the reverse does not have to be true. For instance, when Ethereum upgraded to the Byzantium version, it was affected through a soft fork.

  • Gas fees: gas fees (i.e. transaction fees) are rewards paid to miners to incentivize them to support the network's transactions which become written to the blockchain. On the Ethereum network this gas fee unit amount is expressed in gwei. Operations to or from CEXs, DEXs Liquidity Pools and wallets require gas fees. The cost users incur due to gas fees will vary depending on the current supply and demand: when demand on Ethereum or an ERC-20 network is at its highest, these gas fees are at their highest.

  • Gas Limit: you can only spend a certain amount of gas each transaction on the Ethereum network, according to the gas limit. It may also be seen as a \"rough estimate\" of the amount of computer power your transaction will require.

  • Gas Price: the gas price is the amount paid to the network for the computational labour completed during a particular transaction. It is paid in ETH units known as Gwei. The price of gas may fluctuate dramatically depending on network congestion.

  • Governance Tokens: governance tokens are cryptocurrencies that represent voting power on a blockchain project. Governance tokens are used to vote on system parameters such as choice of autonomous market makers to back with liquidity from the liquidity pool, borrowing rates from the liquidity pool, usage of exchange fees, etc. Governance tokens are minted at an exponentially decreasing rate to incentivise early liquidity providers in the system. Minted tokens are distributed in proportion to the amount of liquidity supplied to the system at each block. Some fraction of the exchange fees and autonomous market maker spreads is used to buy back these tokens and burn them in order to reduce the quantity of tokens circulating and thus increasing their value.

  • Graphical User Interface (GUI): it is way of displaying information to the user through stylized, on-screen elements, such as windows and taskbars.

  • Hash: a programmatic function that accepts an input and produces the \"hash value\" or \"digital fingerprint,\" which is an alphanumeric string. Each block in the blockchain has its own hash value as well as the hash value used to validate the transaction that came before it. Transactions on the blockchain are verified via hashes.

  • Hard Fork: in accordance to Binance Academy, \"Hard forks are backward-incompatible software updates. Typically, these occur when nodes add new rules in a way that conflicts with the rules of old nodes. New nodes can only communicate with others that operate the new version. As a result, the blockchain splits, creating two separate networks: one with the old rules, and one with the new rules\".

  • Hot Wallet: a wallet that is continuously linked to the internet, such as one that is kept on a major exchange. Compared to hardware wallets or cold storage solutions, hot wallets are thought to offer less security.

  • IBC Transfers: the Inter-Blockchain Communication protocol (IBC) is an inter-module communication protocol that bridges different blockchains to facilitate communication and feature exchanges between networks with different infrastructure designs and consensus algorithms.

  • Initial Coin Offering (ICO): it is like an initial public offering (IPO) of stock, an initial coin offering is a way for a tokenized business to generate investment from the public. ICOs are regulated by the Securities and Exchange Commission (SEC), even if the tokens are not specifically securities because the language used in promoting a sale can serve to classify tokens as a security offering.

  • KYC (Know-Your-Customer): KYC guidelines fit into the broader scope of Anti-Money Laundering (AML) policies in traditional finance. There is no KYC or AML in DeFi.

  • Ledger: a book or collection of accounts in which account transactions are recorded.

  • Liquidation: liquidation is applied to borrowers. They can have their collateral liquidated if they do not maintain the set collateralization ratio.

  • Liquidation Ratio: this is the level at which the collateralization ratio dips that can trigger liquidation.

  • Liquidity Pool: a liquidity pool is a crowd sourced pool of cryptocurrencies or tokens locked in a smart contract that is used to facilitate trades between the assets on a decentralized exchange (DEX). Many decentralised finance (DeFi) platforms use automated market makers (AMMs), which enable digital assets to be traded in an automatic and permissionless manner through the use of liquidity pools, in place of traditional markets of buyers and sellers.

  • Liquidity Provider: a liquidity provider (LP) is a user who funds a liquidity pool with own crypto assets so as to facilitate trading on the platform and earn passive income on the liquidity deposited.

  • Mainnet: it represents the main network on which transactions on a certain distributed ledger actually occur. The Ethereum mainnet, for instance, serves as the public blockchain for network verification and transaction processing.

  • MAS (Multi-Agent System): a Multi-Agent System is a group of agents that interact with each other and the environment to achieve specific goals. In such systems, agents may not have full knowledge of both the environment and the internal state of other agents. Interactions between agents may be cooperative or competitive. In a cooperative interaction, agents work with each other towards a common goal. The aim of this interaction is to enable agents to distribute and share their knowledge and use the intelligence and capabilities of each other to solve problems. In a competitive interaction, agents may compete to obtain individual resources and achieve individual goals.

  • Metamask: Metamask is a popular Web 3.0 wallet used in DeFi. Other wallets you may hear about are the Binance Wallet and Torus.

  • Miner: a miner is an actor in a blockchain network that has the ability to create and submit new blocks to the chain. Which miner is allowed to produce a specific block may be predetermined, or miners may simultaneously compete to add the next block to the chain.

  • Mining: mining is the process through which cryptocurrency transactions are gathered, verified and recorded into a digital ledger known as blockchain. The work done by miners is essential for maintaining the integrity of the network and is also responsible for introducing new coins into the system. It is the method used by a Proof of Work (PoW) consensus mechanism to verify blocks or transactions before adding them to a blockchain. A miner must use a computer to solve a cryptographic puzzle in order to validate a block. The block is regarded as having been \"mined\" or validated after the computer has figured out the issue. The first computer to mine or verify the block receives a reward represented by cryptocurrencies (e.g. BTC, ETH).

  • Multi-Signature: multisig stands for multi-signature, which is a specific type of digital signature that makes it possible for two or more users to sign documents as a group. Therefore, a multi-signature is produced through the combination of multiple unique signatures. Basically speaking, the funds stored on a multi-signature address can only be accessed by using 2 or more signatures. Therefore, the use of a multisig wallet enables users to create an additional layer of security to their funds.

  • Network: it represents a set of actors that are collectively interconnected for a common purpose.

  • Node: a node is any computer that is a part of the blockchain network. A full node is a computer able to completely verify transactions and download all of the data associated with a particular blockchain. A lightweight or light node, in contrast, utilizes a separate validation mechanism and does not download the entire blockchain's data.

  • Nonce: this term has a variety of connotations, and depending on the situation, it is employed in many different ways. On the Ethereum mainnet, it refers to a distinct transaction identification number that rises in value with each subsequent transaction to assure different safety measures. It was originally created from a contraction of a term meaning \"not more than once\" (e.g. preventing a double-spend).

  • NFT: Non-Fungible Tokens are cryptographic assets built on a blockchain with unique identification codes and metadata that distinguish them from each other. These were born as ERC-721 crypto assets representing a unique or rare digital or real world item. They let us tokenize things like art, collectibles, even real estate through a securitisation process that takes place on the blockchain. Unlike cryptocurrencies which are identical to each other and can be used as a medium for commercial transactions, NFTs represent unique and irreplaceable tokens, thereby making it impossible for one non-fungible token to be equal to another (i.e. NFTs are not fungible). This ensures a highly transparent and flexible record of ownership. An NFT can only have one owner at a time. Ownership is managed through the unique ID and metadata that no other token can replicate. NFTs are minted through smart contracts which assign ownership and manage the transferability of the NFT itself. When someone mints an NFT, they execute a code stored in the smart contracts that conform to different standards (i.e. ERC-721). This information is added to the blockchain where the NFT will be managed.

  • Open Source Software: it is a software for which the source code that is available to the public. One of the benefits of open source software is that people from outside the core development team can support it, collaboratively creating new features or fixing bugs. Open source licenses typically include language that prevents anyone from reselling the core code without significant changes.

  • Oracle: within the blockchain context, an oracle is a data source used as a bridge between smart contracts and other external sources. More specifically, an oracle is an agent that not only communicates with external data sources but also verifies and authenticates that the data being provided is accurate. Thus, oracles are responsible for providing vital and reliable information to smart contracts, which in turn perform certain tasks.

  • P2P (Peer-to-Peer): P2P describes interactions that take place between two parties, often two different people. Any number of users can be a part of a P2P network. People may conduct transactions or connect with one another over a blockchain network without depending on a middleman or a single point of failure.

  • Private blockchain: a blockchain or distributed ledger with a closed network whose users are all under the supervision of the same organization. For new members on a private blockchain, there must be a verification procedure. Additionally, a private blockchain may place restrictions on who may take part in the network's consensus.

  • Private Key: this is effectively the encrypted password to someone's cryptocurrency assets, and it is also known as a secret key. It is an impossible-to-guess number that is really lengthy. By signing a transaction with your private key, you approve it. You can access and control your crypto assets using private keys.

  • Proof of Stake (PoS): this is a consensus mechanism in which transactions or blocks are verified by a single validator. Those that validate transactions, stake their cryptocurrencies (e.g. ETH), on those particular transactions. If someone accurately verifies a block (or collection of transactions), they are rewarded. A validator often forfeits the cryptocurrency they staked if they verify an invalid transaction. Comparatively speaking to Proof of Work consensus, PoS takes a tiny amount of computational power.

  • Proof of Work (PoW): this is a consensus mechanism in which a network's nodes or individuals collectively mine each block. Each miner in a PoW network must compete to solve a computational puzzle in order to hash a block. The complexity of successfully hashing each block is increased by the inclusion of solving for a target. The whole hashing procedure will have required some time and computing effort for each block that was hashed. The miner who successfully hashes the block first earns a reward in the form of cryptocurrency. As a result, a hashed block is regarded as Proof of Work. PoW requires a disproportionately higher amount of energy than other consensus processes.

  • Public Blockchain: a network that is completely open to the public, allowing anybody to take part in transactions, run the consensus protocol to help decide which blocks should be added to the chain, and keep track of the shared ledger.

  • Public Key: the wallet address that is visible to the whole public. You must provide your public key in order to receive funds into your account. It is paired with a private key.

  • Python: it is a high-level, all-purpose programming language.

  • Scalability: a change in scale or size to meet the demands of a network This term is used to describe a blockchain project's capacity in its intended use as well as its capability to manage network traffic and future expansion.

  • SDK: a Software Development Kit (SDK) is a set of tools provided by the manufacturer of (usually) a hardware platform, operating system (OS), or programming language. SDKs help software developers create applications for that specific platform, system, or programming language.

  • Slippage: in accordance to Investopedia's definition, \"slippage refers to the difference between the expected price of a trade and the price at which the trade is executed. Slippage can occur at any time but is most prevalent during periods of higher volatility when market orders are used. It can also occur when a large order is executed but there isn't enough volume at the chosen price to maintain the current bid/ask spread. Slippage occurs in all market venues, including equities, bonds, currencies and futures\".

  • Smart Contracts: these are programs having their terms encoded in computer code. These are not legal documents, even if they sometimes include agreements or agreements between parties that resemble a regular legal contract. On the Ethereum Virtual Machine, smart contracts are the most common type of programming. Smart contracts contains a series of automated operations that may be programmed and executed if a series of criteria are satisfied.

  • Soft Fork: a soft fork is an update that is backward-compatible, allowing upgraded nodes to continue connect with not upgraded ones. A soft fork often involves the insertion of a new rule that does not conflict with the existing ones.

  • Snapshot: in the context of cryptocurrencies, the process of capturing the state of a blockchain at a specific block height is frequently referred to as taking a snapshot. In this instance, the snapshot captures the entirety of the blockchain ledger, including all of the current addresses and the information linked with them (i.e. transactions, fees, balance, metadata, and so on). Prior to each round of an airdrop event, snapshots are frequently employed. Tokens are given during an airdrop based on the balance of each blockchain address. In this instance, snapshots are made to capture each token holder's balance at a certain moment in time (i.e. block height). Snapshots are also important during blockchain hard forks, as they mark the block height in which the main chain will be recorded before giving birth to the new chain.

  • Software Agent: a software agent is a computer program that acts on behalf of an entity (e.g. individual, organisation, business).

  • sOEF (Simple Open Economic Framework): the simple-OEF, or sOEF, is a search and discovery service for autonomous economic agents. For additional information: here.

  • Spread: when an order is made on an exchange or market, the disagreement of the difference in price between potential buy and sell offers of an asset is called the spread. A wide spread in price can lead to higher slippage.

  • Stablecoin: a stablecoin is a type of cryptocurrency that is designed to maintain a stable market price. Recently, this type of digital currency has grown in popularity, and we now have numerous stablecoin projects. Although the exact mechanisms vary from one coin to another, stablecoins are supposed to be somewhat resistant to market volatility, so they should not experience significant price changes.

  • Timing Risk: according to Investopedia's definition, \"timing risk is the speculation that an investor enters into when trying to buy or sell an asset based on future price predictions. Timing risk explains the potential for missing out on beneficial movements in price due to an error in timing\".

  • Testnet: a testing network for a new coin, project, product or for potential improvements to an existing product or offering. Testnets (e.g. Kovan Test Network) are used to test the viability and vulnerability of new ideas, concepts, code, and processes prior to moving on to a production network.

  • Token: a unique form of cryptocurrency. It is a method to specifically refer to a cryptocurrency that utilizes a certain blockchain. There are many types (see also ERC-20 and ERC-721).

  • Tokenization: it represents the concept of translating business strategies, goods, or services into discrete, tradeable units that are recorded on a blockchain or other system. Physical goods can be tokenized by associating their unique identifiers with on-chain references.

  • Trustless: in the context of decentralized technology, everyone has a copy of the ledger of every transaction, so there is no need for a third-party repository of truth where confidence is anchored. This means that everyone can independently verify the transactions. Because the system functions uniformly for everyone, the rules and protections incorporated into the blockchain in some ways serve as the foundation for increased trust amongst members.

  • Validator: it is a computer node in charge of validating blocks on a blockchain. It is a person who owns the ability to verify transactions and earn cryptocurrency on a proof-of-stake blockchain as part a reward for validating such blocks.

  • Volatility: volatility describes how quickly and how much the price of an asset changes. It is usually calculated in terms of standard deviations in the annual return of an asset over a set period of time. Because it is a measure of the rapidity and degree of price changes, volatility is often used as an effective measure of the investment risk associated with an asset.

  • Yield Farming: yield farming is the practice of staking or lending crypto assets in order to generate high returns or rewards in the form of additional cryptocurrency. Yield farming protocols incentivize liquidity providers to stake or lock up their crypto assets in a smart contract-based liquidity pool. These incentives can be a percentage of transaction fees or a governance token. These returns are expressed as an annual percentage yield (APY). As more investors add funds to the related liquidity pool, the value of the issued returns rise in value.

  • Volume: a market's volume, also known as trading volume, is the total number of units exchanged during a specific period of time. It is a calculation of the quantity of separate assets that were traded throughout the course of a specific time period. A buyer and a seller are involved in every transaction. The facilitating exchange records the transaction whenever they come to an agreement at a particular price. The trade volume is then calculated using this data. Any trade asset, such as stocks, bonds, fiat money, or cryptocurrencies, may be used to measure trading volume.

  • Wallet: a safe digital place or storage device for cryptographic assets. Wallets can be offline or online (e.g. hot and cold wallets).

  • Web3: it is a notion for a new version of the World Wide Web (WWW) that integrates ideas like decentralization, blockchain technology, and token-based economics is known as Web3 or Web 3.0. It has been likened to Web 2.0 by some engineers and journalists, who claim that data and information are consolidated in a limited number of businesses frequently referred to as \"Big Tech\". Gavin Wood, a co-founder of Ethereum, first used the phrase Web3 in 2014, and in 2021 venture capital firms, major technological companies, and cryptocurrency enthusiasts began to show interest in the concept.

  • 51% Attack: a 51% attack is occurring if one person or one group of individuals control more than half of the network's computing power or mining hash rate. By taking over mining activities, blocking or altering transactions, and double-spending coins, this organisation effectively has complete control over the network and the ability to disrupt a cryptocurrency.

"},{"location":"learn_the_concepts/peer_to_peer_systems/","title":"Peer-to-Peer Applications","text":""},{"location":"learn_the_concepts/peer_to_peer_systems/#what-is-a-peer-to-peer-system","title":"What is a Peer-to-Peer System?","text":"

A Peer-to-Peer (i.e. P2P) system is a network of users who communicate with each other without having to go through a middleman. Peer-to-Peer networks consist of a group of devices that collectively store and share files. Every participant (i.e. node) functions as a distinct peer. Normally, all nodes are equally powerful and carry out the same functions. We can outline three key characteristics featuring a P2P system:

  • No middlemen needed: One of the key characteristics of peer-to-peer systems is that middlemen are not always required, though they are allowed.

    Example

    When booking a hotel room with a travel agent, you are going through a middleman. However, booking a room directly with the hotel itself is direct communication, and an example of peer-to-peer interaction.

  • Direct links: direct communication between both parties can be established only in the presence of direct links between them.

  • Direct interaction: It is not enough just for parties to be able to communicate directly, they have to be able to actually interact. Peer-to-peer systems are characterized by direct interaction.

    Example

    In the hotel room booking example, making a booking with the hotel sending back a booking confirmation actually consists of a direct interaction between the two parties.

    Note

    P2P networks enable the sharing of files stored on linked devices' hard drives. A user can serve as the source of a file after they have downloaded it. In other words, when a node performs the role of a client, it downloads data from other network nodes. However, while they are acting as a server, they serve as the location for file downloads by other nodes. However, both operations can be carried out concurrently. Therefore, each node has a copy of the data and serves as both a client and a server to other nodes. This removes the need of a central administrator or server.

    P2P networks differ from more conventional client-server systems in this way, where client devices receive files from a centralized server. Since every node sends, receives, and saves data, P2P networks often get quicker and more effective as their user bases expand. P2P networks are also particularly resilient to cyberattacks thanks to their distributed architecture, and P2P networks do not have a single point of failure, in contrast to conventional models.

P2P systems can be grouped based on their architecture into the following three primary subtypes:

"},{"location":"learn_the_concepts/peer_to_peer_systems/#unstructured-p2p-networks","title":"Unstructured P2P Networks","text":"

P2P networks that are unstructured lack a defined node organization. Participants communicate with one another in a random fashion. These systems are thought to be resistant to high churn activity (i.e. several nodes frequently joining and leaving the network).

Note

Unstructured P2P networks are simpler to set up, but because search requests are sent to as many peers as possible, they may utilize more CPU and memory. This frequently causes the network to get overrun with requests, especially when only a few nodes are providing the needed material.

"},{"location":"learn_the_concepts/peer_to_peer_systems/#structured-p2p-networks","title":"Structured P2P Networks","text":"

The structured P2P network is organized into an arrangement based on a distributed hash table (i.e. DHT).

Info

DHT is an advanced form of lookup or search system that allows nodes to access data, such as files, through the use of a key instead of having to make a copy of the data on every node.

This contrasts with the idea behind unstructured P2P networks in which whole files may be stored on more than one node.

Note

Searching for material in a structured network is simpler and uses less power and memory than an unstructured network. The routing of requests and information rely on each peer knowing what is available for download and other criteria of the neighboring node, which must be relearned as peers leave or join the network as the neighbors change.

"},{"location":"learn_the_concepts/peer_to_peer_systems/#hybrid-p2p-networks","title":"Hybrid P2P Networks","text":"

In hybrid P2P networks some elements of the P2P architecture are combined with the traditional client-server approach. Hybrid models typically display better overall performance when compared to the other two categories. They typically incorporate some key benefits of each strategy, attaining notable levels of efficiency and decentralization at the same time.

"},{"location":"learn_the_concepts/peer_to_peer_systems/#benefits-and-drawbacks-of-a-p2p-system","title":"Benefits and Drawbacks of a P2P System","text":""},{"location":"learn_the_concepts/peer_to_peer_systems/#benefits","title":"Benefits","text":"
  • No need for a Specific Operating System or Software: Individual peers can be on any OS and in most cases do not need specialized software to share files. This is especially useful in remote P2P networks where users might not have the same hardware.

  • Cost: P2P networks do not need a costly server and can be joined together simply through USB or over the internet. Even more permanent connections (using copper wires in smaller offices, for example) is not as costly as creating a server or buying server software.

  • Egalitarian: Each peer has control over what can be accessed by others on the network by changing the sharing settings. This makes it easier to protect the integrity of the network as an issue with one node will not destroy the rest of the peers.

  • Easy to set up: Configuration is straightforward, with no need for the oversight of an administrator. Each node manages access and sharing themselves.

  • Scalable: P2P networks are easy to scale, with more nodes adding performance and giving more power. Adding more peers makes more storage and processing power available to the network and can improve download and upload speeds.

  • Easy Searching: The idea of a peer-to-peer network is that finding the right resources should, in theory, be easy. Even in an unstructured network, if the content you are searching for is not rare, it should be held by several peers and be available to download from multiple sources.

  • Aligns With Decentralized Systems: Peer-to-peer systems align closely with decentralized systems, as both require trust between parties in order to work effectively, and both do not require central authorities to manage the system. Peer-to-peer communication is the basis of how parties in decentralized systems communicate with each other.

  • More Freedom: By not having to go through a middleman, it gives users more freedom. They are not limited by the information for example provided by the hotel booking site but can find out far more by being in direct contact with the hotel and likewise the hotel can ask questions directly to their guests.

"},{"location":"learn_the_concepts/peer_to_peer_systems/#drawbacks","title":"Drawbacks","text":"
  • Decentralized: this makes it harder to arrange backups and file archiving. The safety and integrity of the content can be at risk if it is not managed, backed up regularly and deleted once it is obsolete. Due to the decentralized nature of such networks, participants may find it hard to provide themselves those useful services normally provided by the middlemen, such as marketing and promoting services, or market balancing services.

  • No Oversight: in most of P2P networks, the decentralized nature makes it hard for a single administrator to monitor contents, and these can be at risk from malware and viruses. Sharing files with an infected node can transmit malware through the network and could cause problems over several affected peers.

  • Slow transmission: simultaneous uploading and downloading of files can lead to a slower rate of transmission. The double function of uploading content while downloading other resources might actually make it slower.

  • Poor Internet Performance: file sharing through P2P networks uses a lot of bandwidth and CPU, which can slow the computer performance for the individual user, especially when it comes to the internet. If multiple files are being shared, there is a risk that productivity in other areas could be reduced.

  • Illegal Content: peer-to-peer networks can be used for downloading pirated music, movies, software and other copyrighted material, even if the sharer is unaware.

"},{"location":"learn_the_concepts/peer_to_peer_systems/#examples-of-peer-to-peer-systems","title":"Examples of Peer-to-Peer Systems","text":"

One of the most popular uses of P2P systems is represented by file sharing networks which allow members to directly share files with each other.

Example

Napster and LimeWire were P2P music sharing networks backed by the idea that peers connected through the internet could find and download any song they wanted, from several other users.

There exist also online gaming platforms which adopt a P2P structure for downloading games between users, such as Blizzard Entertainment which distributed Diablo III, StarCraft II, and World of Warcraft using P2P.

A more successful use for P2P systems was developed in the crypto world thanks to blockchains and how these relate to P2P networks. Bitcoin, Ether and many other cryptocurrencies were developed following the P2P mechanism.

An additional example of P2P architecture is represented by P2P crypto exchanges, on which users can immediately buy or sell cryptos from/to other users directly. The majority of P2P exchanges let you send and receive cryptocurrencies without requesting identity verification, in contrast to centralized exchanges where you must complete KYC in order to fulfill an order. Also, unlike centralized exchanges, P2P-based exchanges do not have a single point of failure. In most cases, a user may sign up for the exchange without having to undergo identification verification. A password and an email address are all that are needed for registration.

Example

P2P-based crypto exchanges include Paxful and Binance P2P.

"},{"location":"learn_the_concepts/blockchains/consensus/","title":"Consensus Mechanisms","text":"

Despite the absence of a central authority to confirm and authenticate the transactions, every Blockchain transaction is regarded as being totally safe and validated. Only the presence of the consensus protocol, a fundamental component of every Blockchain network, makes this feasible.

Info

A consensus algorithm allows every peer in the Blockchain network to agree on the distributed ledger's current state. Consensus algorithms accomplish dependability in the Blockchain network and build confidence amongst unidentified peers in a distributed computing setting in this way.

Hence, the consensus mechanism ensures that every new block added to the Blockchain is the sole version of the truth that has been accepted by every node.

When it comes to confirming the legitimacy of distributed blockchain systems, each consensus technique has a unique set of benefits and drawbacks. While PoW and PoS are the most common, new algorithms are always emerging. The following are the most utilized consensus mechanisms nowadays:

"},{"location":"learn_the_concepts/blockchains/consensus/#proof-of-work-pow","title":"Proof-of-Work (PoW)","text":"

One of the most prominent blockchain technologies is the PoW consensus process, which was initially made popular by Bitcoin. Miners and the power they need to do the computations required to validate transactions are the key elements that distinguish PoW systems. Miners use computer hardware to power network nodes that use processing power to solve algorithmic mathematical computational puzzles, known as proofs of work. The miner who completes the puzzle first validates the blockchain's most recent block of transactions. The successful miner broadcasts the new block to all other nodes, which in turn authenticate its accuracy and add that block to their copies of the blockchain. This verification procedure establishes consensus. A new block cannot be added to the network until this data has been verified. When a miner validates a new block of data this latter one is then added to the PoW blockchain, with the first miner completing the validation process being rewarded with newly created cryptocurrency, known as the block reward.

Note

PoW networks are constrained in terms of their speed and scalability due to how the high energy-intensive process these imply. Massive quantities of processing hash power must be used to solve the computational challenge, which uses a lot of energy. The more intensive the validation process, the more computational power is adopted to solve the puzzle and this leads to a higher competition among validators, which equals harder proofs of work and more energy consuming operations. Technology advancement in the blockchain sector has put a lot of emphasis on addressing the negative environmental effects of crypto mining, and several alternatives have surfaced.

PoW blockchains have typically offered stronger security while preserving significant decentralization, despite their speed and scalability restrictions. Due to the distributed nature of PoW systems, it would be very expensive for a bad actor to seize control of the majority of the network's computing power and take over the blockchain. Typically, it is unable to overcome the high expenses of the hardware, power, and computing.

"},{"location":"learn_the_concepts/blockchains/consensus/#proof-of-stake-pos","title":"Proof-of-Stake (PoS)","text":"

PoS blockchains only have validators for transaction validation, not miners. Similar to PoW systems, validators operate network nodes and validate data, but there is no energy-intensive computing procedure required to earn the privilege to validate.

Info

This consensus mechanism addresses many of the problems of PoW blockchains, including slowness, lack of scalability, wasteful energy use, and high entry barriers. Polkadot, Avalanche, and Cardano are a few instances of contemporary market-dominating PoS blockchains.

Validators stake some native tokens of the blockchain to qualify for selection as a validator node rather than working to solve proofs of work. The potential validator will effectively stake blockchain-native crypto tokens to act as collateral. On a PoS blockchain, when it is time to validate the data contained in a transaction block, the system chooses a validator at random to do so. The quantity of tokens a validator has staked is one such factor that might increase the likelihood that they will be picked as a validator.

The process starts with a new block after the block is confirmed, and the validator is typically compensated with network transaction fees. By asking validators to stake their tokens, PoS blockchains make the network safe and maintain validators' integrity. The entrance barrier to PoS blockchains for validators is arguably lower, when compared to PoW blockchains, since they do not need to invest in expensive hardware or incur significant electricity costs. However, you still need to have enough cryptocurrency to stake if you want to become a validator. PoS blockchains have been accused of being plutocratic since validators' control over the network is frequently proportionally correlated with the quantity of their stake. Thus, even though the PoS validation process uses significantly less energy and is quicker and less expensive, it does have some drawbacks, such as the potential for power concentration in the hands of individuals on the network who have amassed a sizable amount of the platform's governance cryptocurrency.

Note

Through a procedure known as slashing, validators that engage in malevolent or incompetent behaviour lose their stake and network access. By using this incentive system, validators are certain to benefit more from following the law than from breaching it.

There can be particular hardware requirements for various platforms adopting a PoS mechanism. While PoS is not nearly as resource-consuming as PoW, certain PoS blockchains' validator nodes may require strong hardware or software specifications because they may be the need of handling a lot of transactions at once.

"},{"location":"learn_the_concepts/blockchains/consensus/#delegated-proof-of-stake-dpos","title":"Delegated Proof-of-Stake (DPoS)","text":"

It is a well-liked development of the Proof of Stake idea in which network users choose delegates to validate the following block. Delegates may also be referred to as block producers or witnesses. By pooling your tokens into a staking pool and tying them to a specific delegate, you may cast a vote for delegates using DPoS. The argument for DPoS is that it is a more decentralized and equitable method of reaching consensus than just Proof-of-Stake.

"},{"location":"learn_the_concepts/blockchains/consensus/#proof-of-burn-pob","title":"Proof of Burn (PoB)","text":"

With PoB, validators burn coins by sending them to an address from which they are irretrievably lost, as opposed to spending money on expensive hardware equipment. Validators obtain the right to mine on the network based on a random selection procedure by sending the coins to an unreachable address. Burning coins here entails a long-term commitment on the part of validators in return for a temporary loss. Miners may burn either the native money of the Blockchain application or the currency of an alternate chain, such as bitcoin, depending on how the PoB is implemented. The more coins they burn, the better are their chances of being selected to mine the next block.

"},{"location":"learn_the_concepts/blockchains/consensus/#proof-of-capacity-poc","title":"Proof of Capacity (PoC)","text":"

PoC, often referred to as Proof-of-Space (PoSpace) is a mining technique that is based on the amount of hard disk space that a miner has available. Here, miners generate a list of all the possible hashes beforehand in a process called plotting. Such plots are then stored on hard drives. The more storage capacity a miner has, the more possible solutions available. The more solutions, the higher the chances of possessing the correct combination of hashes and thus the higher the possibility of winning the reward.

Info

PoC makes it possible for the typical individual to take part in the network because it does not need expensive or specialized equipment. As a result, this is decentralized and less energy-intensive alternative to some of the more widely used consensus mechanisms. However and there are concerns about its susceptibility to malware attacks.

"},{"location":"learn_the_concepts/blockchains/consensus/#proof-of-activity-poa","title":"Proof of Activity (PoA)","text":"

It is a consensus approach that combines PoW and PoS. Similar to PoW, the mining process starts here with miners vying to use the most powerful processing power to solve a complex mathematical problem. The system then turns to resemble PoS when the block has been mined, with the successfully created block header being sent to the PoA network. The new block is then randomly validated by a number of validators who sign off on the hash. The more crypto a validator possesses, similar to PoS, the better their chances are of being chosen. The block is introduced to the blockchain network and made available to record transactions once each selected validator has signed it. The miner and validators then split the block rewards.

"},{"location":"learn_the_concepts/blockchains/consensus/#proof-of-authority-poa","title":"Proof of Authority (PoA)","text":"

This mechanism chooses its validators based on their track record. Validators in PoA do not stake coins. Instead, in order to have the ability to validate blocks, they must risk their reputations. The majority of blockchain systems, in contrast to this, often do not need participants to disclose their identities. This approach is far less resource-intensive than some of its predecessors, especially PoW, as it requires essentially little computational power.

"},{"location":"learn_the_concepts/blockchains/decentralized_applications/","title":"Decentralized Applications","text":"

Decentralized applications (DApps) are digital applications or programmes that exist and run on a blockchain or peer-to-peer (i.e. P2P) network of computers. DApps, which might resemble ordinary mobile applications on your phone in appearance but have a distinct backend system, as these are not subject to the control of a single authority. DApps function by using smart contracts on a distributed network as opposed to a centralized system. These features make DApps more open, decentralized, and secure as a result. A smart contract functions as a collection of predetermined rules that computer code enforces. The tasks specified in the contract will be carried out by all network nodes when and if specific conditions are satisfied.

DApps share the following features:

  • Open-sourceness: This allows anybody to access, verify, use, copy, and edit their source code. The vast majority of the DApp tokens are not controlled by a single entity. Users are also able to suggest and vote on DApp improvements.

  • Decentralization and Cryptography: To protect data, the DApp stores all of its data on a public, decentralized blockchain that is maintained by a number of users (or nodes).

  • Tokenization: A cryptographic token is required to access DApps. They can use native tokens created using a consensus mechanism like Proof of Work (PoW) or Proof of Stake (PoS), or they can adopt cryptocurrencies like ETH. Additionally, contributors like miners and stakers can be rewarded with this token.

Info

DApps are present on the Ethereum network and on other blockchains, such as BNB Smart Chain (BSC), Solana (SOL), Polygon (MATIC), Avalanche (AVAX), EOS.

To interact with a DApp, you first need a compatible browser extension wallet like MetaMask, or Trust Wallet.

"},{"location":"learn_the_concepts/blockchains/decentralized_applications/#benefits-and-limitations-of-dapps","title":"Benefits and Limitations of DApps","text":"

While DApps and conventional apps may have similar-looking user interfaces, DApps have some advantages over their centralized alternatives. Data is kept on centralized servers by web applications. The entire network of the app might be brought down by a single hacked server, rendering it inoperable for a while or ever. Data leaks or theft may occur in centralized systems as well, endangering businesses and consumers.

DApps, in contrast, are created via decentralized distributed networks. DApps have no single point of failure, making them less susceptible to assaults and making it exceedingly challenging for bad actors to take over the network. The P2P network can also guarantee that the DApp keeps running with little downtime even if a few machines or a section of the network go wrong. Users may exercise more control over the data they disclose because of DApps' decentralized nature. Users do not need to reveal their real-world identities in order to communicate with a DApp because no corporations are in charge of their personal data. Instead, individuals may connect to DApps using a crypto wallet and have complete control over the data they disclose.

Another advantage of DApps is that by utilizing smart contracts, developers may quickly include cryptocurrency into their core functions.

Example

DApps on Ethereum may accept ETH as payment without integrating external payment services.

When it comes to illustrate the limitations of DApps, we first need to highlight the fact that DApps have the potential to play a significant role in a world without censorship. However, decentralized apps are still in their infancy, and the market has yet to find solutions to problems like scalability, code revisions, and a small user base. DApps might overburden the networks they run on since they demand a lot of computational power to function.

Another difficult task is modifying a DApp. A DApp does require continual updates to repair problems, update the user interface, and introduce new functions in order to improve user experience and security.

Info

It is challenging to change the backend code of a DApp once it has been put on the blockchain, though. Any modifications or enhancements would need to be approved by a majority of the network's nodes, which would take a while to put into practise.

It is challenging for one DApp to stand out and draw a lot of users when there are so many others available. A DApp has to have a network effect in order to function properly. This is, the more users a DApp has, the more efficient it becomes at offering services. The DApp may be made more secure and shielded from hackers tampering with the open-source code by having more users.

"},{"location":"learn_the_concepts/blockchains/decentralized_applications/#use-cases","title":"Use Cases","text":""},{"location":"learn_the_concepts/blockchains/decentralized_applications/#gamefi","title":"GameFi","text":"

GameFi DApps are becoming more and more well-liked. The majority of gaming DApps give players complete control over their in-game assets, in contrast to conventional video games. They also provide users the chance to earn money from these things outside the game.

Example

One of the most successful DApp in this sector is the play-to-earn game Axie Infinity on the Ethereum blockchain. Axie Infinity offers NFT-based game avatars, virtual worlds, and gaming-related merchandise. Players can trade with other players on NFT markets, move them to other Ethereum addresses, or keep them in their digital wallets. Players can compete with one another inside the ecosystem to amass ERC-20 tokens that can be exchanged. Generally speaking, players may earn more in-game incentives the longer they play.

"},{"location":"learn_the_concepts/blockchains/decentralized_applications/#dexs-decentralized-exchanges","title":"DEXs - Decentralized Exchanges","text":"

The intermediaries in traditional finance are financial institutions. Everyone may access financial services with DApps without the need for a centralized authority and have total control over their assets. Low-income people can also profit from DeFi since it gives them access to a wide range of financial services at a considerable cost savings.

The most common financial services offered by decentralized apps are borrowing and lending. Instant transaction settlement, little-to-no credit checks, and the usage of digital assets as collateral are all features of DeFi DApps. Users in DApp lending markets have greater freedom. Another essential example of financial DApps is decentralized exchanges (DEXs). These platforms make peer-to-peer trading easier by getting rid of middlemen like centralized cryptocurrency exchanges. Users are not required to give up control of their funds. With the aid of smart contracts, people trade with another user directly rather than putting their assets into an exchange. Orders are carried out directly between the users' wallets and on the blockchain. In comparison to centralized exchanges, DEXs often charge cheaper trading costs since they require less maintenance.

Example

SushiSwap, PancakeSwap, and Uniswap are a few of the well-known DEXs.

"},{"location":"learn_the_concepts/blockchains/decentralized_applications/#entertainment","title":"Entertainment","text":"

DApps enable the transformation of entertainment daily activities into digital experiences that can also produce financial incentives.

Example

For instance, Audius, a decentralized music streaming network built on blockchain, eliminates the middlemen present in the conventional music industry to connect artists and listeners directly. By creating permanent records of their labor on the blockchain, it enables music curators to better monetise their material.

DApps are addressing problems that users of social media platforms encounter. Twitter and Facebook, two centralized social media behemoths, are frequently attacked for editing content and treating user data carelessly.

Example

An example on decentralized social media platform is given by Steemit. In there, the community can interact freely and express their opinions with fewer limitations and censorship while also having more control over their personal data.

"},{"location":"learn_the_concepts/blockchains/decentralized_applications/#governance","title":"Governance","text":"

DApps can enable users to participate more actively in the administration of online organizations by establishing a decision-making process that is more focused on the community. Users with governance tokens of a certain blockchain project can make suggestions for the community to vote on and vote on other people's ideas anonymously with the use of smart contracts.

Note

Decentralized Autonomous Organizations (DAOs) are one of the decentralized governance models. DAOs are completely autonomous DApps that employ smart contracts to decide for themselves without the need for a central authority. There is no hierarchy there. Instead, economic processes bring the organization's interests into line with those of specific DAO members.

"},{"location":"learn_the_concepts/blockchains/oracles/","title":"Oracles","text":"

Blockchains get their most significant positive features by being purposefully segregated from external systems. However, off-chain data cannot be accessed by blockchains or smart contracts directly, and these need such data to perform any contractual agreement between any party on the blockchain itself.

Info

This is known as the core of the oracle dilemma. In fact, blockchains are unable to draw data from or push data out to any external systems as part of their built-in functionality. A blockchain's isolation is precisely what makes it so safe and dependable since the network just needs to come to consensus on a very simple set of questions using information that has already been written in the ledger.

A blockchain needs an additional piece of infrastructure, an oracle, to connect the blockchain to the off-chain services in a secure manner. A blockchain oracle is a safe piece of middleware that makes it easier for blockchains to communicate with any off-chain system, such as data providers, online APIs, business backends, cloud providers, IoT devices, e-signatures, payment systems, other blockchains, and more. This is, a blockchain Oracle is the element that links the blockchain to the external system, making it possible for smart contracts to operate on the basis of inputs and outputs from the real world.

Note

The oracle system must work concurrently on and off the blockchain in order to perform these tasks. The on-chain component is used to connect to the blockchain to listen for requests, broadcast messages, submit proofs, extract blockchain data, and perhaps even carry out computation on the blockchain. The off-chain component is used to execute requests, retrieve and prepare external data, transport blockchain data to other systems, and carry out off-chain computation for improved smart contract scalability, privacy, security, and other features.

Blockchain oracles can help to increase the range of possibilities for smart contracts to work, thus, oracles are essential components of the blockchain ecosystem. Smart contracts would not even be particularly useful without blockchain oracles as they would only have access to data from their own networks since the vast majority of smart contract use cases, including DeFi, depend on knowledge of real world data and off-chain occurrences.

"},{"location":"learn_the_concepts/blockchains/oracles/#types-of-blockchain-oracles","title":"Types of blockchain oracles","text":"

Blockchain oracles may be categorized based on the following characteristics:

  • Data source: does the data come from hardware or software as its source?
  • Information direction: is the flow of data going inward or outward?
  • Trust: are these oracles decentralized or centralized?

Given this, we can identify the following types of oracles:

  • Software oracles: these communicate with internet data sources and provide the data to the blockchain. Any internet data source is a possible source for this information (e.g. online databases, servers, websites, ...). Exchange rates, pricing of digital assets, and real-time flight information are just a few of the types of data that software oracles frequently give.

  • Hardware oracle: these oracles are made to gather data from the real world and provide it to smart contracts. Electronic sensors, barcode scanners, and other information gathering tools may transmit this data. In essence, a hardware oracle converts actual occurrences into digital values that smart contracts can comprehend.

  • Inbound and outbound oracles: outbound oracles transfer information from smart contracts to the outside world, whereas inbound oracles give information from external sources to smart contracts.

  • Centralized oracles: a centralized oracle is the only source of data for the smart contract and is managed by a single organization. The primary issue with centralized oracles is that they have a single point of failure, which reduces the contracts' resistance to weaknesses and intrusions.

  • Decentralized oracles: public blockchains and decentralized oracles goal is to reduce counterparty risk. By not depending on a single source of truth, decentralized oracles improve the accuracy of the data supplied to smart contracts.

  • Specialized oracles: these oracles are created so that a single smart contract can use them. This means that a proportionately large number of contract-specific oracles must be created if one plans to implement numerous smart contracts. These oracles are said to be exceedingly time and money consuming. This strategy can prove to be quite unworkable for businesses that need to pull data from a number of sources. On the other hand, developers have a lot of freedom to customize contract-specific oracles to meet unique needs because they may be created from the ground up to service a particular use case.

  • Human oracles: oracles can occasionally be people who have specific expertise in a certain area. They are able to gather information from numerous sources, investigate its veracity, and convert it into smart contracts. Since human oracles may use cryptography to confirm their identity, it is unlikely that a fraudster will pretend to be them while supplying tainted information.

  • Cross-chain oracles: these are another kind of oracle that can read and write data between several blockchains. Using data from one blockchain to start an activity on another or connecting assets across chains, so they may be utilized outside the native blockchain they were issued on, cross-chain oracles provide interoperability for moving both data and assets between blockchains.

Info

Given the variety of oracle services, selecting between oracle service providers often comes down to reputation. Reputation frameworks offer transparency into each oracle network's and each oracle node operator's precision and dependability. Then, users may decide for themselves which oracles they wish to use to support their smart contracts.

"},{"location":"learn_the_concepts/blockchains/oracles/#blockchain-oracles-use-cases","title":"Blockchain oracles: Use Cases","text":""},{"location":"learn_the_concepts/blockchains/oracles/#decentralized-finance-defi","title":"Decentralized Finance (DeFi)","text":"

Oracles are necessary for a significant section of the decentralized finance (i.e. DeFi) ecosystem to access financial information regarding assets and markets. Decentralized money markets, for instance, employ price oracles to assess users' borrowing capacity and evaluate if their holdings are under collateralized and at risk of liquidation. Similarly, Automated Market Makers (AMMs) employ price oracles to assist concentrate liquidity at the current market price to increase capital efficiency. Synthetic asset platforms use price oracles to link the value of tokens to real-world assets.

"},{"location":"learn_the_concepts/blockchains/oracles/#dynamic-nfts-and-gaming","title":"Dynamic NFTs and Gaming","text":"

Oracles also make it possible for smart contracts to be used in non-financial use cases, such as NFTs and dynamic NFTs. Non-fungible Tokens (i.e. NFTs) that can alter in appearance, value, or distribution based on external factors like the time of day or the weather are examples of dynamic NFTs. Oracles may also produce verified randomness, which projects can utilize to give NFTs randomized characteristics or to choose random winners in high-demand NFT drops. Verifiable randomness is also used in on-chain gaming applications to produce more captivating and surprising gameplay elements, such as the emergence of random treasure boxes or random matchmaking during a tournament.

"},{"location":"learn_the_concepts/blockchains/oracles/#businesses","title":"Businesses","text":"

Businesses may link their backend systems to any blockchain network using the safe blockchain middleware provided by business cross-chain oracles. Enterprise systems may read from and write to any blockchain in this way, and they can also use complicated logic to decide how to deploy assets and data among chains and with counterparties that are connected to the same oracle network. As a consequence, institutions no longer need to invest time and development resources in integrating with each individual blockchain. Instead, they can immediately join blockchains that are highly desired by their counterparties and quickly provide support for smart contract services that their customers require.

"},{"location":"learn_the_concepts/blockchains/oracles/#insurance","title":"Insurance","text":"

Insurance smart contracts provide access to physical sensors, online APIs, satellite images, and legal data by using input oracles to confirm the presence of insurable events throughout claims processing. Using other blockchains or conventional payment networks, output oracles can give insurance smart contracts a mechanism to pay out on claims.

"},{"location":"learn_the_concepts/blockchains/smart_contracts/","title":"Smart Contracts","text":""},{"location":"learn_the_concepts/blockchains/smart_contracts/#what-is-a-smart-contract","title":"What is a Smart Contract?","text":"

A smart contract (i.e. automated self-enforcing contract) is a piece of computer software that initiates a certain activity when the circumstances are satisfied. In other words, smart contracts are essentially lines of code that run when certain criteria are satisfied, performing a certain purpose.

Info

Smart contracts enable trustworthy transactions and agreements to be made between dispersed, anonymous participants, without the need for a centralized authority, a legal system, or an external enforcement mechanism.

A smart contract may describe the transaction's mechanics and serve as the final arbitrator of the terms. Because of this, smart contracts have evolved into the foundation of a complete ecosystem of decentralized applications (dApps) and are now a key area of focus for blockchain development as a whole.

When predefined circumstances have been verified to have been satisfied, a network of computers will carry out the actions. These can entail paying out money to the right people, registering a car, sending out notices, or writing a ticket. When the transaction is finished, the blockchain is then updated.

Info

As a result, the transaction cannot be modified, and only parties to whom permission has been granted can view the outcome. Smart contacts, therefore, allow intermediaries such as banks and central authorities to be bypassed, as the contract is written onto the blockchain, and once written on the blockchain, these are irreversible. This means they are ideal for automating and decentralizing transactions and deals, and indeed are really the key to making dApps work on the blockchain.

Example

Smart contracts are adaptable to be used in almost any sector where money, digital assets, or any type of digital information has to be moved between parties. Their application is also being investigated in the healthcare industry and to enforce intellectual property agreements.

"},{"location":"learn_the_concepts/blockchains/smart_contracts/#benefits","title":"Benefits","text":"
  1. Speed, Efficiency, and Precision: the contract is promptly carried out if a condition is satisfied. Smart contracts are digital and automated, so there is no paperwork to complete or time wasted fixing mistakes that frequently occur when documents are filled out manually.

  2. Transparency and Trust: there is no need to wonder whether information has been changed for one participant's personal gain because there is no third party engaged and participants exchange encrypted records of transactions. Smart contracts are written in a way that does make disputes extremely rare, indeed they are designed to avoid them entirely.

  3. Security: because the blockchain transaction records are encrypted, these are incredibly difficult to hack. Additionally, hackers would need to alter the entire chain in order to change a single record on a distributed ledger since each record is linked to the records that came before and after it.

"},{"location":"learn_the_concepts/blockchains/smart_contracts/#drawbacks","title":"Drawbacks","text":"
  1. Legal status: smart contracts lack any legal status. This may be largely caused because the technology is so new, and most legal jurisdictions have not yet updated their guidance on smart contracts and legislation if required is usually not yet in place.

  2. Difficult to Change: once a record has been written to the blockchain it is impossible to change or delete it. Therefore, if a smart contract has been issued in some way incorrectly, then changing it is almost impossible. You would be able to issue another contract that effectively would reverse the effect of the incorrect contract, but that would not necessarily be easy.

  3. Need for Precision: smart contracts do not work unless they are written in an extremely precise way. In fact, there can not be any ambiguity or vagueness in their terms.

"},{"location":"learn_the_concepts/blockchains/smart_contracts/#use-cases","title":"Use Cases","text":""},{"location":"learn_the_concepts/blockchains/smart_contracts/#finance","title":"Finance","text":"

Thanks to the irreversible, transparent, and trustless properties of blockchain and smart contract technology, DeFi dApps provide whole new sorts of goods and decentralized business models that may be of great use and utility for consumers, in addition to parallel services to the banking and financial services sector like lending, borrowing, trading, and a variety of other financial activities.

Note

DApps have the ability to lower the barriers to entry into the financial services industry for individuals all over the world because of the improved transparency provided by smart contracts (combined with 24/7 functioning, and cheaper prices). The implications of smart-contract-powered dApps on the financial industry are already apparent, despite the DeFi sector's relative youth given the amount of creative dApps that are currently offering users value and usefulness.

"},{"location":"learn_the_concepts/blockchains/smart_contracts/#gaming-and-nfts","title":"Gaming and NFTs","text":"

Blockchain and smart contract technologies in gaming can help gamers more effectively realize the value and usefulness of in-game purchases and asset purchases. Non-fungible tokens (NFTs), which are distinctive digital assets that stand in for in-game content, are frequently the engine behind blockchain technology in the gaming industry. Smart contracts are crucial to NFTs. The blockchain networks that support NFTs provide player ownership, proved scarcity, interoperability, and immutability, while these tokens are unique, uncommon, and indivisible. These features of blockchain in gaming might promote widespread adoption and a more equal value model when taken as a whole.

Info

The worldwide gaming market is a $100 billion ecosystem that is expanding swiftly, yet the way wealth is produced and dispersed within the market may often be unfair. Games are created and released by developers, and users pay to access and participate in such games. As a result, gamers continue to pay money to gain access to in-game resources and gaming options, perpetuating a one-way flow of value.

"},{"location":"learn_the_concepts/blockchains/smart_contracts/#healthcare","title":"Healthcare","text":"

Another sector that has started utilizing blockchain technology for safe, trustless, and transparent data sharing is healthcare. The incorporation of smart contracts and full dApps created to address important healthcare pain points like interoperability, identity, and authentication issues may help strengthen the connection between blockchain technology and healthcare.

"},{"location":"learn_the_concepts/blockchains/smart_contracts/#identity-management","title":"Identity Management","text":"

To increase security and lower the likelihood of data mismanagement or a breach, Digital Identifier (DIDs) smart contracts built on distributed ledger technologies offer people complete ownership over their data and let them share the content of their data as they see fit.

Note

For identity management, smart contracts help to ensure and facilitate: identity protection, the information the user wants to share, and KYC verification.

"},{"location":"learn_the_concepts/blockchains/smart_contracts/#machine-learning-and-artificial-intelligence","title":"Machine Learning and Artificial Intelligence","text":"

The capacity of blockchain technology and related smart contract technology to simplify difficult computing operations like those involved in machine learning and artificial intelligence is one of the most interesting uses of these technologies (AI). There is potential to develop AI-powered smart contracts by fusing the data-intensive processing of AI with the decentralized security and immutability of blockchain technology. Applications for smart contracts will need to develop into increasingly complicated systems as they are used by more and more sectors of the economy.

"},{"location":"learn_the_concepts/blockchains/smart_contracts/#real-estate","title":"Real Estate","text":"

By fusing blockchain and real estate transactions, smart contracts are increasing fractional ownership of assets and decreasing the entry barrier for investing for many. Particularly, there have been a number of successful attempts to tokenize real estate assets. By adding blockchain into real estate deals, smart contract technology may also remodel the paperwork and transaction procedures. When a piece of property is tokenized, much of the required record-keeping can take place via associated smart contracts, which can save the parties time and money.

"},{"location":"learn_the_concepts/blockchains/smart_contracts/#supply-chain-management","title":"Supply Chain Management","text":"

A fantastic use case for blockchain smart contracts is supply chain management. The supply chain may be significantly enhanced by the use of smart contracts.

Note

Smart contracts may be used, for instance, to track things in the supply chain completely transparently. A company may employ supply networks that are enabled by smart contracts to track its inventory more precisely. Additionally, it enhances corporate operations in other areas that are directly related to the supply chain. Additionally, adopting smart contracts leads to less verification work, improved traceability, and fewer frauds and thefts. However, in order for it to be functional, the institutions must upgrade their supply chain with new hardware equipments.

Smart contracts and dApps are poised to continue revolutionizing the world of digital agreements. Numerous businesses will benefit greatly from smart contract technology in the future. Researchers and developers are increasingly keen to take use of smart contract technology to meet the demands of the expanding Internet of Things (IoT). While IoT devices are currently given security and transparency by blockchain technology in general, the benefits of smart contracts might make this integration much more seamless.

"},{"location":"learn_the_concepts/blockchains/transaction_fees/","title":"Transaction Fees","text":"

Any transaction involving the transfer of cryptocurrency will incur fees, whether you are purchasing or withdrawing funds from an exchange or sending or receiving payments in cryptocurrency. Various fee types include:

  • Transaction or miner fees: these vary depending on how many transactions are awaiting their inclusion in the current block and are intended to motivate miners and validators to validate cryptocurrency transactions.

  • Service or network fees: these are applied by third-party providers that enable transactions (e.g. cryptocurrency exchange). These fees are made in addition to any network-generated fee paid to miners.

Transaction fees serve two crucial functions:

  1. Compensate miners or validators assisting and confirming the transactions.

  2. Defend the network against spam assaults: this is because transaction fees lead to a decrease in spam on the network, and large-scale spam assaults become more expensive and more difficult to execute.

Info

Transaction fees serve as a basic but efficient spam filter.

Note

Transaction fees might be modest or substantial. The fees you pay might also be influenced by market forces. It is important to highlight that the amount of fees you pay impacts your transaction's priority for inclusion in the following validated block. In fact, users who wish to execute their transaction more quickly can even increase the transaction cost to enhance the likelihood that their transaction will be added to the following block of transactions on the blockchain.

The confirmation procedure moves along more quickly the more money that is spent.

Miners are in charge of adding transactions to the blockchain and are required for confirming and securing these transactions on each network. These payments make the effort of miners and validators profitable. Despite the fact that each blockchain is distinct, they all have a limited quantity of transactions that may fit into a single block.

Example

The Bitcoin network only permits a total of 2,800 transactions per block.

Depending on how many transactions are awaiting their inclusion, miner fees may change. During periods of high network traffic, miners prioritize the validation of new transactions based on the fee paid by the user. Hence, the user who wants to complete the transaction more quickly can even increase the transaction fee to boost its chances of being included in the next completed block.

"},{"location":"learn_the_concepts/blockchains/transaction_fees/#how-to-reduce-transaction-fees","title":"How to reduce transaction fees","text":"

There are several options available for reducing fees. First of all, you may lower these fees by deciding when you want to complete your transaction. Due to the fact that the majority of cryptocurrency users worldwide are concentrated in the U. S., blockchain networks typically see the highest levels of activity during times when people there are awake. Additionally, traffic is lower during the weekends, particularly on Saturdays. Additionally, the fees you pay depend on how quickly you want your transaction to be validated. If you have a high priority transaction and want it to be confirmed faster, you should expect a higher miner fee. If your transaction is not urgent, then a slower verification time means a lower transaction fee.

Info

On the Ethereum network, transaction fees are referred to as gas. Like other blockchains, the gas costs can go up or down depending on demand. Miners are more likely to give your transaction priority if you pay more for gas. The gas limit specifies the maximum price to be paid for a specific transaction and it should also be taken into account when proceeding with a transaction.

"},{"location":"learn_the_concepts/blockchains/transaction_fees/#transaction-fees-on-the-fetchai-network","title":"Transaction fees on the Fetch.ai network","text":"

On the Fetch.ai network, the gas mechanism is made simple and gives you an option to pick high, medium, or low fees, in addition to allowing you to manually set the amount you wish to pay. Fetch.ai does operate some validators on the network, which further gives it influence over the gas fee levels. Even though some constraints are set by Fetch.ai on gas fees, the decentralized nature of the network guarantees that transaction fees are not set by any central authority, and the actual level of gas is set by each of the validators on the Fetch network. Fetch.ai has the philosophy that having high gas fees does no good for anyone, and because of this, the Fetch team wants to encourage everyone to use the network, and trade and transact using $FET tokens.

Low fees encourages higher network traffic, and this in turn means more transactions and activity and therefore more fees being paid in general. However, $FET that are traded and transferred on other networks are subject to the gas fees that those networks charge and Fetch has no control whatsoever over those fees. The fees gathered on the Fetch network are then distributed among the stakeholders (i.e. validators and community stakeholders). Gas fees on the Fetch network are extremely very low when compared to other networks. As a general comparison, Juno Network, another ecosystem blockchain within the Cosmos ecosystem has fees that on average are $0.05 per transfer, whereas Fetch.ai transfers cost $0.000000000000001.

These gas fees are calculated in an extremely fair way, with the fee being directly proportional to the computing power needed to complete the process.

Example

A simple purchase transaction takes less computing power than activating a smart contract, therefore the associated fee will be lower.

There is full transparency too, as it is always clear what the gas fee will be before making a transaction, and so there are no hidden surprises.

"},{"location":"learn_the_concepts/blockchains/validators/","title":"Validators","text":"

The block validation procedure is one of the essential elements that makes blockchain functioning possible. A network node known as a blockchain validator assists in processing and validating transaction blocks on the platform so that they may be added to the blockchain's permanent ledger.

Info

On PoS blockchains, these nodes are referred to as validators, whereas, on PoW blockchain networks, these are known as miners. In essence, validator nodes take on the responsibility of validating, voting on, and keeping a record of transactions by taking part in the consensus process.

Block validation is a procedure that both of these blockchain variants may use. Staking, the method of block validation utilized on PoS blockchains, would be a more appropriate alternative to mine when referring to blockchain networks.

The process starts with a user sending the transaction on the blockchain, which is then queued on the network for further confirmation. The block is then verified by validator nodes by batching individual transactions into it. The amount of transactions that can be included in a block is governed differently by each blockchain. The block is then processed by validators which add it to the blockchain as a permanent record. On some blockchains, validators have the option to select which transactions to group into a block.

Info

This choice is made depending on the validator's preferences, usually based on the transaction costs involved, and is not necessarily made in chronological sequence. Transactions with extremely little or no fees, however, are more likely to be disregarded by validators and, as a result, can hang around in the unconfirmed state for a very long time. The transaction is often removed from the network if it is not put into a block for validation after a certain time period.

The sender of cryptocurrency assets tacks on the fees to every blockchain transaction as a reward for validators. Senders have the option to set their own rates and even transmit transactions completely free of charge.

"},{"location":"learn_the_concepts/blockchains/what_is_a_blockchain/","title":"What is a Blockchain?","text":"

A blockchain is a series of data records that functions as a distributed, replicated digital ledger of transactions across a network of computer systems.

Info

On blockchains, the records of transactions are compiled into blocks which are linked together to form a chain. A blockchain consists of a stable chain of blocks, and in the context of cryptocurrencies, each one of these blocks stores a list of previously confirmed transactions.

Transactions take place inside a P2P global network, thus, blockchains are considered borderless and immune to censorship. A blockchain network serves as a decentralized ledger since it is maintained by several computers located all over the world. This implies that each participant, namely node, keeps a copy of the blockchain data and interacts with other participants to make sure that everyone is aware of the same information stored in the block.

Note

Each block inside the blockchain contains a hash (i.e. a digital fingerprint or unique identifier), timestamped batches of recent valid transactions, and the hash of the previous block. The previous block hash links the blocks together, preventing existing blocks from being altered or new blocks inserted between existing ones. An additional feature of a blockchain is that all previous records are stored within any current record, so, there is always full history of previous transactions.

"},{"location":"learn_the_concepts/blockchains/what_is_a_blockchain/#blockchains-use-cases","title":"Blockchains: Use Cases","text":"

Currently, the blockchain technology is mostly used to track cryptocurrency transactions. Examples of other uses are:

  1. Monitoring of Supply Chains: businesses may easily identify inefficiencies in their supply chains using blockchain, as well as find products in real time and monitor their quality as they move from producers to retailers.

  2. Data sharing: blockchain could act as an intermediary to securely store and move enterprise data among industries.

  3. Copyright and royalties protection: blockchain technology has the potential to be utilized to build a decentralized database that guarantees the preservation of music rights and rewards musicians with transparent and real-time royalties.

  4. Internet of Things (IoT) network management: blockchain has the potential to regulate IoT networks by identifying connected devices, tracking their behavior, assessing their dependability, and automatically determining the dependability of new devices that are linked to the network, such as smartphones and vehicles.

  5. Healthcare: the healthcare industry may benefit greatly from the use of blockchain. Blockchains are being used by healthcare payers and providers to handle clinical trial data and electronic medical records while upholding regulatory compliance.

"},{"location":"learn_the_concepts/blockchains/what_is_a_blockchain/#who-can-participate-in-a-blockchain-network","title":"Who can participate in a blockchain network?","text":"
  1. Blockchain users: participants joining the blockchain network and conducting transactions with other network participants.

  2. Regulators: users with special permissions to oversee the transactions happening within the network.

  3. Blockchain Network Operators: individuals who have special permissions and authority to define, create, manage, and monitor the blockchain network.

  4. Certificate Authorities: individuals who issue and manage the different types of certificates required to run a permissioned blockchain.

"},{"location":"learn_the_concepts/blockchains/what_is_a_blockchain/#benefits","title":"Benefits","text":"
  1. Security: blockchains are secure, in the sense that the data is cryptographically encrypted, and their structure makes it difficult and immediately noticeable if someone tries to change the data in a blockchain.

  2. Resiliency: the same information is stored in many places on the blockchain. This implies high resiliency as, if any part of the network was to fail then the information stored on the blockchain is still available in full.

  3. Trust: blockchains are decentralized systems and thus benefit from having no central authority controlling them. Due to their structure, and depending on the mechanism the blockchain's participants use to arrive at a consensus on what the state of the chain is, there is no need for the blockchain's users to trust the other parties. Guarantees are provided by the system itself.

"},{"location":"learn_the_concepts/blockchains/what_is_a_blockchain/#drawbacks","title":"Drawbacks","text":"
  1. Complexity: Setting up a blockchain is not a simple task, there are networks of nodes to set up, the participatory framework established, and numerous components to bring together before you can even begin to store any information on the blockchain.

  2. Speed: It takes longer to add a record to a block and add a block to the chain than it does to simply add a record into a traditional database table. This is a challenge for blockchain-based payment systems, as blockchains typically handle fewer transactions per second than conventional payment systems.

  3. Scaling issues: As blockchains grow, they get more complex leading to issues, such as network congestion.

  4. Participation is required: Blockchains are decentralized systems, so they do need the active participation of members, for example they need to vote on governance proposals affecting the chain and be engaged in order for the blockchain to successfully operate.

"},{"location":"learn_the_concepts/blockchains/what_is_a_blockchain/#types-of-blockchain","title":"Types of Blockchain","text":"

The architecture of blockchain systems varies greatly, especially in terms of the consensus protocol employed to validate network data. The security, usability, and sustainability of the underlying blockchain are affected differently by their own design. Different blockchain networks operate quite differently from one another. We can identify the following:

  • Public blockchains are entirely decentralised, permissionless, and open to everybody. All nodes are meant to have equal access and abilities within the blockchain. These are censorship-resistant and offer broad ecosystems for the development of apps and platforms.

    Example

    Examples of public blockchains include Bitcoin and Ethereum. The majority of public blockchain networks use processes known as Proof-of-Work (PoW) or Proof-of-Stake (PoS) to provide consensus.

  • Private blockchains are blockchains within which permissions are managed by a single company. It is the central authority which decides who can be a node. The central authority may not always accord each node an equal right to execute certain responsibilities. This makes private blockchains partially decentralised. Private (or permissioned) blockchains can be structured in various ways to prioritize speed, security, and scalability.

  • Consortium blockchains are permissioned blockchains governed by a group of entities, rather than a single one as private blockchains. These blockchains are more decentralised than private blockchains, which increases their security. But creating consortiums may be a difficult process since it calls for collaboration between several groups, which poses logistical problems and a possible antitrust risk. However, these may offer faster transaction processing times and are easier to modify, but are restrictive with limited usage outside the private consortium.

  • Hybrid blockchains are blockchains which are managed by a single entity but at the same time have some supervision provided by the public blockchain, which is necessary to carry out specific transaction validations.

Note

Varying consensus techniques have different effects on accessibility, security, and sustainability, and not all blockchains are created equally. In fact, the most suitable type of blockchain design needs to be fitting the actual use case it wants to serve.

"},{"location":"ledger-subquery/introduction/","title":"Introduction","text":"

The ledger-subquery is a SubQuery-based indexer for the Fetch ledger. This indexer provides a Graphql API for querying tracked entities. For a list of tracked entities, see the schema.graphql file.

To learn more about how to run or change this SubQuery Project to get your own custom GraphQL API for your app, visit the SubQuery Academy for documentation.

"},{"location":"ledger-subquery/introduction/#endpoints-playground-uis","title":"Endpoints / Playground UIs","text":"

The graphql API endpoints also serve a playground UI to browsers for convenience. This UI is useful for rapid experimentation and iteration of queries as well as just getting some results, features include:

  • real-time query results
  • query editor:
  • auto-complete & validation via schema introspection
  • can store multiple, named queries
  • supports graphql variables
  • local persistence of query editor contents
  • schema reference
  • graphql docs reference
Network API / Playground URL Fetchhub (mainnet) https://subquery.fetch.ai Dorado (testnet) https://subquery-dorado.fetch.ai"},{"location":"ledger-subquery/introduction/#architecture","title":"Architecture","text":""},{"location":"ledger-subquery/introduction/#component-diagram","title":"Component Diagram","text":""},{"location":"ledger-subquery/introduction/#querying","title":"Querying","text":"

The graphql API relies heavily on postgraphile (as a library) to resolve graphql requests.

Postgraphile plugins also play a critical role; in particular, the connection-filter and pg-aggregates plugins.

"},{"location":"ledger-subquery/introduction/#pagination","title":"Pagination","text":"

The graphql API implements the connections specification for pagination (see: GraphQL pagination docs for more).

Tip

It is recommended to prefer using pagination operators by default (e.g. first: <limit>) to avoid unnecessary delays in query responses.

"},{"location":"ledger-subquery/introduction/#filtering","title":"Filtering","text":"

Filtering is facilitated by postgraphile and its plugins. For specifics on supported operators and how to use them, please refer to their documentation:

  • connection-filter plugin
  • operators
  • query examples
"},{"location":"ledger-subquery/introduction/#examples","title":"Examples","text":"

Filtering NativeTransfers for a given sender address:

query nativeTransfersFromAddress {\nnativeTransfers(first: 5, filter: {\nfromAddress: {\nequalTo: \"fetch1t3qet68dr0qkmrjtq89lrx837qa2t05265qy6s\"\n}\n}) {\nnodes {\ntoAddress\namounts\n}\n}\n}\n

Filtering for Messages from a given sender address:

query messagesFromAddress {\nmessages (first: 5, filter:  {\ntransaction: {\nsignerAddress: {\nequalTo: \"fetch1t3qet68dr0qkmrjtq89lrx837qa2t05265qy6s\"\n}\n}\n}) {\nnodes {\ntransaction {\nsignerAddress\n}\n}\n}\n}\n

Filtering on Eventss within a given timeframe and with a given type:

query transferEventsDuring {\nevents(first: 5, filter:  {\nblock: {\ntimestamp: {\ngreaterThanOrEqualTo: \"2022-09-15T01:44:13.719\",\nlessThanOrEqualTo: \"2022-09-19T02:15:28.632\"\n}\n},\ntype: {equalTo: \"transfer\"},\n}) {\nnodes {\nattributes {\nnodes {\nkey\nvalue\n}\n}\n}\n}\n}\n
"},{"location":"ledger-subquery/introduction/#order-by-sorting","title":"Order by / Sorting","text":"

Each entity, by default, can be sorted by any of its respective fields. Additional support for ordering by certain fields on related entities is facilitated by custom ordering plugins generated from makeAddPgTableOrderByPlugin (see: postgraphile-docs).

"},{"location":"ledger-subquery/introduction/#block-height","title":"Block height","text":"

Any entity which relates to Block can be ordered by a related block's height field:

query contractExecByBlockHeight {\ncontractExecutionMessage (orderBy: EXECUTE_CONTRACT_MESSAGES_BY_BLOCK_HEIGHT_ASC) {\nnodes {\nid,\n...\nBlock {\nheight\n}\n}\n}\n}\n

"},{"location":"ledger-subquery/introduction/#contract-code-id","title":"Contract Code ID","text":"

The contract entity can be sorted by codeId through the storeMessage and instantiateMessage relations.

query contractsByRelatedCodeID {\ncontracts (orderBy: CONTRACTS_BY_STORE_CONTRACT_MESSAGES_CODE_ID_ASC) {\n#  or CONTRACTS_BY_INSTANTIATE_CONTRACT_MESSAGES_CODE_ID_ASC\nnodes {\nid,\n...\nstoreMessage {\ncodeId\n}\n}\n}\n}\n

"},{"location":"ledger-subquery/introduction/#order-direction","title":"Order direction","text":"

Each of these custom orders are implemented in both directions, ascending and descending. These directions are accessed through the ending characters of the order enum, by choosing either _ASC and _DESC.

"},{"location":"ledger-subquery/introduction/#aggregation","title":"Aggregation","text":"

Aggregation is facilitated by the pg-aggregates plugin. Features include:

  • calculating aggregates
  • grouped aggregates
  • applying conditions to grouped aggregates
  • ordering by relational aggregates
  • filtering by the results of aggregates on related connections
"},{"location":"ledger-subquery/introduction/#tests-as-examples","title":"Tests as examples","text":"

Additional examples of queries and use cases can be found in the end-to-end test suite.

"},{"location":"ledger-subquery/introduction/#entities","title":"Entities","text":"

Entities tracked by the indexer exist at varying levels of abstraction. \"Lower-level\" entities include the primitives (i.e. blocks, transactions, messages, and events), upon which \"higher-level\" entities are constructed (e.g. LegacyBridgeSwaps).

Some entities are derived from objects which do not correspond to any network state change (e.g. failed transactions and their messages). In the case of failed transactions, it is desirable to index the associated data for end-user reference. This notion may also apply to other objects but should be considered carefully to avoid storing invalid or useless data.

"},{"location":"ledger-subquery/introduction/#primitive-entities","title":"Primitive entities","text":"

(see: schema.graphql)

  • blocks
  • transactions
  • messages
  • events
  • event attributes
"},{"location":"ledger-subquery/introduction/#entity-relationship-diagrams","title":"Entity relationship diagrams","text":""},{"location":"ledger-subquery/introduction/#versioning","title":"Versioning","text":"

The versions of both the GraphQL API and the Indexer itself can be retrieved simply using the following query on the GraphQL playground.

"},{"location":"ledger-subquery/introduction/#example","title":"Example:","text":"
query ReleaseVersionTest {\n_metadata {\nqueryNodeVersion\nindexerNodeVersion\n}\n}\n

Each of these version numbers are stored as the value to the key \"version\" within their relevant module package.json file. These files can be found in the docker/node-cosmos/ and subql/packages/query/ directories for the Indexer and GraphQL versions, respectively.

// The Indexer version number, taken from \"docker/node-cosmos/package.json\"\n{ \"name\": \"@subql/node-cosmos\",\n\"version\": \"1.0.0\",\n...\n}\n

"},{"location":"ledger-subquery/introduction/#_metadata-entity","title":"\"_metadata\" Entity","text":"

The _metadata entity has further utility beyond the scope of the example query given prior. Using any of the relevant fields from the type definition below, internal states and config information can be retrieved with ease.

type _Metadata {\n        lastProcessedHeight: Int\n        lastProcessedTimestamp: Date\n        targetHeight: Int\n        chain: String\n        specName: String\n        genesisHash: String\n        indexerHealthy: Boolean\n        indexerNodeVersion: String\n        queryNodeVersion: String\n        rowCountEstimate: [TableEstimate]\n        dynamicDatasources: String\n      }\n

"},{"location":"ledger-subquery/introduction/#example_1","title":"Example:","text":"

If a developer was curious about the chain-id or whether the Indexer has passed any health checks, using indexerHealthy, these values can be returned within the playground or otherwise connected projects.

query ReleaseVersionTest {\n_metadata {\nchain\nindexerHealthy\n}\n}\n

"},{"location":"ledger_v2/","title":"Mainnet is here","text":"

The fetchhub mainnet forms the core of the Fetch.ai ecosystem. In these pages, you will find all information to setup your client and connect on the network and cli-introduction pages.

This documentation covers some of the things you need to know in order to prepare for and develop on this network.

"},{"location":"ledger_v2/#test-networks","title":"Test Networks","text":"

The starting point for most will be our test network, since it can provide you with test tokens with no value, that you can safely experiment with.

Head over the Command line client section for guidance on how to install and configure the fetchd client.

"},{"location":"ledger_v2/archived-networks/","title":"Mainnet Archive","text":""},{"location":"ledger_v2/archived-networks/#mainnet-archives","title":"Mainnet Archives","text":"

Archived data from previous versions of the fetchhub mainnet.

"},{"location":"ledger_v2/archived-networks/#fetchhub-3-archive","title":"Fetchhub-3 archive","text":"Parameter Value Chain ID fetchhub-3 Block range 4,504,601 --> 5,300,200 Date range 08/02/2022 --> 05/04/2022 Denomination afet Decimals 18 (1fet = 1000000000000000000afet) Version v0.9.1 RPC Endpoint https://rpc-fetchhub3-archive.fetch.ai:443 GRPC Endpoint https://grpc-fetchhub3-archive.fetch.ai:443 REST Endpoint https://rest-fetchhub3-archive.fetch.ai:443 Block Explorer https://explore-fetchhub3-archive.fetch.ai Token Faucet N/A Seed Node(s) N/A Snapshots https://storage.googleapis.com/fetch-ai-mainnet-snapshots/fetchhub-3-archive.tgz"},{"location":"ledger_v2/archived-networks/#fetchhub-2-archive","title":"Fetchhub-2 archive","text":"Parameter Value Chain ID fetchhub-2 Block range 2,436,701 --> 4,504,600 Date range 15/09/2021 --> 08/02/2022 Denomination afet Decimals 18 (1fet = 1000000000000000000afet) Version v0.8.7 RPC Endpoint https://rpc-fetchhub2-archive.fetch.ai:443 GRPC Endpoint https://grpc-fetchhub2-archive.fetch.ai:443 REST Endpoint https://rest-fetchhub2-archive.fetch.ai:443 Block Explorer https://explore-fetchhub2-archive.fetch.ai Token Faucet N/A Seed Node(s) N/A Snapshots https://storage.googleapis.com/fetch-ai-mainnet-snapshots/fetchhub-2-archive.tgz"},{"location":"ledger_v2/archived-networks/#fetchhub-1-archive","title":"Fetchhub-1 archive","text":"Parameter Value Chain ID fetchhub-1 Block range 1 --> 2,436,700 Date range 31/03/2021 --> 15/09/2021 Denomination afet Decimals 18 (1fet = 1000000000000000000afet) Version v0.7.4 RPC Endpoint https://rpc-fetchhub1-archive.fetch.ai:443 GRPC Endpoint N/A REST Endpoint https://rest-fetchhub1-archive.fetch.ai:443 Block Explorer https://explore-fetchhub1-archive.fetch.ai Token Faucet N/A Seed Node(s) N/A Snapshots https://storage.googleapis.com/fetch-ai-mainnet-snapshots/fetchhub-1-archive.tgz"},{"location":"ledger_v2/block-explorer/","title":"Block Explorer","text":"

Each of the networks has a dedicated block explorer web site associated with it. This is a useful tool for monitoring network activity.

"},{"location":"ledger_v2/block-explorer/#logging-in-with-the-ledger-nano","title":"Logging in with the Ledger Nano","text":"

Return to the block explorer landing page and click on the key button in the top right corner. You'll then be prompted to \"Sign in With Ledger\". You must accept this request on your ledger nano device. After completing this process, the key button will be replaced by a person icon with a link to your personal address page, which keeps track of the activity that you have performed on the test-net.

"},{"location":"ledger_v2/block-explorer/#getting-testnet-tokens-from-the-faucet","title":"Getting Testnet Tokens from the Faucet","text":"

For networks that support it, you can obtain tokens for your account by copying the address and pasting it into the token faucet. Then, return to the main page, press the \"Get Funds\" button and paste your address in the pop-up. Afterwards you can return to your address page (via the person icon) and should observe that you have been allocated 1 TESTFET.

"},{"location":"ledger_v2/block-explorer/#transferring-tokens-to-another-address","title":"Transferring Tokens to another Address","text":"

After receiving tokens, you can send these to another address using the purple Transfer button on your address page. This will trigger a pop-up that prompts you to specify the destination address and the amount you wish to transfer. After filling in this information, you will be asked to sign the transaction using your ledger nano. The confirmation that the transaction has been broadcast gives two links that can be used to check that the transaction has been executed on the blockchain using either the transaction hash or your account page.

Note:

The transaction format includes a memo field that can be used to check the transaction information on the ledger nano display.

"},{"location":"ledger_v2/block-explorer/#delegating-stake-to-a-validator","title":"Delegating Stake to a Validator","text":"

You can delegate your test-net tokens to a validator who is operating the network by clicking on the Validators tab, and selecting one of the validators that you wish to delegate stake towards. In the Voting Power panel there is an option to DELEGATE tokens. Pressing this button will trigger a pop-up that prompts you to select a delegation amount and then sign the transaction with your Ledger Nano device.

After delegating tokens, buttons labelled with REDELEGATE and UNDELEGATE will appear. The delegation of tokens to a validator provides you with a reward for helping to secure the network. It is also possible to delegate your tokens to a different validator using a REDELEGATE transaction. You can return any bonded tokens to your address by submitting an UNDELEGATE request, which will trigger the tokens to be returned after 21 days have elapsed. The rewards that you receive from delegating tokens to a validator are shown in the account page. These can be sent to your address by sending a WITHDRAW transaction.

"},{"location":"ledger_v2/building/","title":"Building the Ledger","text":""},{"location":"ledger_v2/building/#prerequisites","title":"Prerequisites","text":"
  • Go 1.18+ (installation instructions available here)
  • Packages: make, gcc (on Ubuntu, install them with sudo apt-get update && sudo apt-get install -y make gcc)
"},{"location":"ledger_v2/building/#building-the-code","title":"Building the code","text":"

Download the latest released version from github and build the project using the following commands:

git clone https://github.com/fetchai/fetchd.git && cd fetchd\n

Then build the code with the command:

make build\n

This will generate the ./build/fetchd binary.

For non-developer users we recommend that the user installs the binaries into their system. This can be done with the following command:

make install\n

This will install the binaries in the directory specified by your $GOBIN environment variable (default to ~/go/bin).

which fetchd\n

This should return a path such as ~/go/bin/fetchd (might be different depending on your actual go installation).

If you get no output, or an error such as which: no fetchd in ..., possible cause can either be that make install failed with some errors or that your go binary folder (default: ~/go/bin) is not in your PATH.

To add the ~/go/bin folder to your PATH, add this line at the end of your ~/.bashrc:

export PATH=$PATH:~/go/bin\n

and reload it with:

source ~/.bashrc\n

You can also verify that you are running the correct version

fetchd version\n

This should print a version number that must be compatible with the network you're connecting to (see the network page for the list of supported versions per network).

"},{"location":"ledger_v2/building/#faq","title":"FAQ","text":"
  • Error: failed to parse log level (main:info,state:info,:error): Unknown Level String: 'main:info,state:info,:error', defaulting to NoLevel

This means you had a pre-stargate version of fetchd (<= v0.7.x), and just installed a stargate version (>= v0.8.x), you'll need to remove the previous configuration files with:

rm ~/.fetchd/config/app.toml ~/.fetchd/config/config.toml\n
"},{"location":"ledger_v2/cli-bls/","title":"BLS signatures","text":"

The BLS algorithm can be selected when creating new keys and signing transactions. BLS supported keys are particularly useful for the increasing the efficiency of multi-signature transactions at the cost of simplicity in verification.

It affords the users a shorter and yet still robust grouping of each signature from party members without the sacrifice of security on each multi-signature transaction.

"},{"location":"ledger_v2/cli-bls/#creating-bls-keys","title":"Creating BLS keys","text":"

Creating BLS keys is straightforward in comparison to normal key instantiation with the addition of one extra parameter to the command. This example will show the additional flag required in comparison to a key with the standard algorithm; with 'bls12381' in place of the default 'secp256k1'.

"},{"location":"ledger_v2/cli-bls/#example","title":"Example","text":"
# Create a normal key\nfetchd keys add Ron\n\n# Create a key capable of BLS signed transactions\nfetchd keys add Tom_BLS --algo bls12381\n\n# 'Ron' can be assumed to define implicitly --algo secp256k1 by default\n
"},{"location":"ledger_v2/cli-bls/#bls-transactions-and-signatures","title":"BLS Transactions and signatures","text":"

After creating this BLS key, transactions can be carried out between two keys using the different algorithms.

"},{"location":"ledger_v2/cli-bls/#example_1","title":"Example","text":"

Ensure that the 'Ron' key has some funds before performing this example.

# Perform a normal transfer of funds from Ron to Tom_BLS\nfetchd tx bank send <address_of_Ron> <address_of_Tom_BLS> 1000test\n\n# This should provide a breakdown of the transaction parameters, including the gas fees\n# Keep note of these fees\n\n# Check that these funds have been transferred to Tom_BLS\nfetchd query bank balaces <address_of_Tom_BLS>\n\n# Perform a BLS signed transaction from Tom_BLS to Ron\nfetchd tx bank send <address_of_Tom_BLS> <address_of_Ron> 1000test\n\n# Compare the difference between information printed from each transaction and observe\n# the difference in gas costs (?)\n\n# Now assure funds were successfully transferred back to Ron through a BLS signed transaction \nfetchd query bank balaces <address_of_Tom_BLS>\nfetchd query bank balaces <address_of_Ron>\n
"},{"location":"ledger_v2/cli-governance/","title":"Governance Proposals","text":"

In order to change any attribute of a network, a governance proposal must be submitted. This could be a simple poll, a software update or a governing parameter change.

"},{"location":"ledger_v2/cli-governance/#parameter-change","title":"Parameter change","text":"

This is an example of the process in which network parameters may be changed through the use of a governance proposal.

The values within this code can be changed in order to alter the minimum deposited fund threshold for a proposal to enter the voting phase, and the length of the deposit stage in which the minimum deposit threshold must be met.

# A JSON file containing the following code should be created to instantiate the proposal.\n# The two variables of interest are the \"amount\" which is set from 10000000stake to 1000stake\n# and the \"max_deposit_period\" which is changed from the default value to 7200000000000\n# equal to 2 hours, instead of the standard 2 days (in nanoseconds).\n\n{\n  \"title\": \"Staking Param Change\",\n  \"description\": \"Update max validators\",\n  \"changes\": [\n    {\n      \"subspace\": \"staking\",\n      \"key\": \"MaxValidators\",\n      \"value\": 105\n    }\n  ],\n  \"deposit\": \"1000000000000000000atestfet\"\n}\n
# Create initial proposal by uploading the JSON file\n# this is signed by a key 'proposer' that provides a portion of the current threshold deposit\nfetchd tx gov submit-proposal --proposal ~/json_path/proposal.json --from proposer\n\n# In order to later refer to this proposal, the proposal_id can be determined\nfetchd query gov proposals\n

"},{"location":"ledger_v2/cli-governance/#proposal-deposit-phase","title":"Proposal deposit phase","text":"

The characteristics of the deposit phase are described by a set of network governance parameters, where the deposit period is two days from the initial proposal deposit until expiration, and a minimum threshold of 10000000denom as default. The minimum threshold must be met during this deposit period in order to proceed to the voting phase. The proposer may provide all of this threshold, or just some. In which case, supporters of the proposal may donate additional funding towards the goal of meeting the threshold.

At any point of the deposit stage, the deposit pot can be queried.

# To get the proposal ID, use the txhash obtained when the proposal was submitted and run the following command:\nfetchd query tx <txhash>\n\n# This command returns a text representation of the current total deposit value of a proposal\nfetchd query gov deposits <proposal_id>\n\n# Other users may contribute to funding the proposal using\nfetchd tx gov deposit <proposal_id> <deposit_amount> --from contributer\n

This documentation provides a more detailed explanation of the deposit funding period.

"},{"location":"ledger_v2/cli-governance/#proposal-voting-and-querying","title":"Proposal voting and querying","text":"

After the deposit period has passed, there are two outcomes: either the current minimum threshold is met, or the value is not met and the funds are returned. In the first case this proposal is submitted and to be voted on, returning a tally at the end of the voting period.

In order to submit a vote on a proposal that has passed into the voting phase, all staked users except the proposer may do so using this command.

# Submit a vote from a key 'voter' with the desired outcome of the voter\nfetchd tx gov vote <proposal_id> <yes|no|no_with_veto|abstain> --from voter\n

The current voting turnout and tally can be queried, which displays a list of all voters and their choice.

# The current voting statistics can be printed using\nfetchd query gov votes <proposal_id>\n

"},{"location":"ledger_v2/cli-governance/#example-output","title":"Example output","text":"
votes:\n- option: VOTE_OPTION_YES\n  proposal_id: \"1\"\n  voter: fetch1dmehhhvul8y7slqs3zu2z3fede9kzlnyupd9rr\n- option: VOTE_OPTION_NO\n  proposal_id: \"1\"\n  voter: fetch1064endj5ne5e868asnf0encctwlga4y2jf3h28\n- option: VOTE_OPTION_YES\n  proposal_id: \"1\"\n  voter: fetch1k3ee923osju93jm03fkfmewnal39fjdbakje1x\n
"},{"location":"ledger_v2/cli-governance/#voting-outcome","title":"Voting outcome","text":"

After the voting period has ended, the results are used to determine the next step of the proposal. The potential outcomes include:

  • Majority yes vote
    • The proposal passes through and the users act according to the proposal type - e.g. A Software update proposal passes, and users begin uptake of the new version
  • Majority no vote

    • The funds deposited to pass into the voting stage are returned, and there is no governance change
  • Majority no_with_veto vote

    • This outcome is indicative of a proposal which may undermine the current governance system, e.g. a proposal to set the deposit threshold or voting period to an absurd value
    • All funds deposited in the proposal are to be burned subject to this outcome, and there is no governance change
"},{"location":"ledger_v2/cli-introduction/","title":"CLI - Introduction","text":"

The command line client provides all of the capabilities for interacting with the fetch ledger such as creating addresses, sending transactions and the governance capabilities. Before starting with the command line client you need to follow the installation instructions here

"},{"location":"ledger_v2/cli-introduction/#connecting-to-a-network","title":"Connecting to a network","text":"

While some users will want to connect a node to the network and sync the entire blockchain, for many however, it is quicker and easier to connect directly to existing publically available nodes.

"},{"location":"ledger_v2/cli-introduction/#connecting-to-fetchhub-mainnet","title":"Connecting to fetchhub mainnet","text":"

To connect to the mainnet run the following configuration steps:

fetchd config chain-id fetchhub-4\nfetchd config node https://rpc-fetchhub.fetch.ai:443\n
"},{"location":"ledger_v2/cli-introduction/#connecting-to-dorado-network","title":"Connecting to dorado network","text":"

To connect to the dorado network run the following configuration steps:

fetchd config chain-id dorado-1\nfetchd config node https://rpc-dorado.fetch.ai:443\n

Checkout the Network Information page for more detailed information on the available networks.

"},{"location":"ledger_v2/cli-keys/","title":"CLI - Managing Keys","text":"

Managing your keys is an essential part of working with the Ledger, since all interactions are authenticated with these keys.

"},{"location":"ledger_v2/cli-keys/#adding-keys","title":"Adding keys","text":"

To create a new local key you need run the following command:

fetchd keys add <your_key_name>\n

Note

These keys are stored locally on your system. By default, these keys will be stored in the OS level keychain, however, in general these keys are considered less secure than using a hardware device

After running the command fetchd will print out a summary of the new key. An example of this output is shown below:

- name: test\n  type: local\n  address: fetch142tawq2rj397mctc3jtw9dfzf03ns0ze4swat0\n  pubkey: fetchpub1addwnpepqvtmze0ekffynnjx9n85g6sexzl49ze2vpgc2f52fteyyghjtvvqw682nkx\n  mnemonic: \"\"\n  threshold: 0\n  pubkeys: []\n

This will be followed by a 24-word mnemonic that can be used to re-generate the private key and address for the account (keep this safe, if ever used to control main-net tokens).

"},{"location":"ledger_v2/cli-keys/#looking-up-an-address","title":"Looking up an address","text":"

A common operation that you will want to do is to lookup the address for a specified key. This can be done quickly using the following command:

fetchd keys show -a <name of key>\n

An example of the expected output is shown below:

fetch142tawq2rj397mctc3jtw9dfzf03ns0ze4swat0\n

A less common operation, but still useful, would be to lookup the public key for a specified key. The can be achieved with the following command:

fetchd keys show -p <name of the key>\n

An example of the expected output is shown below:

fetchpub1addwnpepqvtmze0ekffynnjx9n85g6sexzl49ze2vpgc2f52fteyyghjtvvqw682nkx\n
"},{"location":"ledger_v2/cli-keys/#listing-keys","title":"Listing keys","text":"

To lookup more detailed information for all keys on your system use the following command:

fetchd keys list\n

This will output all of your keys information in a yaml format that is similar to the one generated when you first created the key.

- name: test\ntype: local\naddress: fetch142tawq2rj397mctc3jtw9dfzf03ns0ze4swat0\n  pubkey: fetchpub1addwnpepqvtmze0ekffynnjx9n85g6sexzl49ze2vpgc2f52fteyyghjtvvqw682nkx\n  mnemonic: \"\"\nthreshold: 0\npubkeys: []\n
"},{"location":"ledger_v2/cli-keys/#recovering-a-key","title":"Recovering a key","text":"

You can import a key from a 24-word mnemonic by running:

fetchd keys add <name> --recover\n> Enter your bip39 mnemonic\n<type or paste your mnemonic>\n
You'll be prompted to enter the mnemonic phrase, and it will then print the matching address and key details as when creating a new key.

"},{"location":"ledger_v2/cli-keys/#hardware-wallets","title":"Hardware Wallets","text":""},{"location":"ledger_v2/cli-keys/#setup","title":"Setup","text":"

We recommend hardware wallets as a solution for managing private keys. The Fetch ledger is compatible with Ledger Nano hardware wallets. To use your Ledger Nano you will need to complete the following steps:

  1. Set-up your wallet by creating a PIN and passphrase, which must be stored securely to enable recovery if the device is lost or damaged.
  2. Connect your device to your PC and update the firmware to the latest version using the Ledger Live application.
  3. Install the Cosmos application using the software manager (Manager > Cosmos > Install).
"},{"location":"ledger_v2/cli-keys/#adding-a-new-key","title":"Adding a new key","text":"

In order to use the hardware wallet address with the cli, the user must first add it via fetchd. This process only records the public information about the key.

To import the key first plug in the device and enter the device pin. Once you have unlocked the device navigate to the Cosmos app on the device and open it.

To add the key use the following command:

fetchd keys add <name for the key> --ledger --index 0\n

Note

The --ledger flag tells the command line tool to talk to the ledger device and the --index flag selects which HD index should be used.

When running this command, the Ledger device will prompt you to verify the generated address. Once you have done this you will get an output in the following form:

- name: hw1\n  type: ledger\n  address: fetch1xqqftqp8ranv2taxsx8h594xprfw3qxl7j3ra2\n  pubkey: fetchpub1addwnpepq2dulyd9mly3xqnvfgdsjkqlqzsxldpdhd6cnpm67sx90zhfw2ragk9my5h\n  mnemonic: \"\"\nthreshold: 0\npubkeys: []\n
"},{"location":"ledger_v2/cli-multisig/","title":"Multisig keys","text":"

This feature of fetchd allows users to securely control keys in a number of configurations. Using a threshold number K of maximum N keys, a user or group of users can set the minimum number of keys required to sign a transaction. Some examples of these configurations allow some useful features such as the choice of a spare key, where only one key is required to sign (K=1) but there are two keys available to do so. Another more complex example configuration is set out below.

"},{"location":"ledger_v2/cli-multisig/#creating-a-multisig-key","title":"Creating a multisig key","text":"

The following represents the syntax and argument layout of the fetchd command to create a multisig key.

# Create a simple multisig key with a threshold of 1 as default\nfetchd keys add <multisig_key_name> --multisig <list_of_key_names>\n\n# Creating a multisig key with a higher threshold, K\nfetchd keys add <multisig_key_name> --multisig <list_of_key_names> --multisig-threshold <threshold integer K>\n
"},{"location":"ledger_v2/cli-multisig/#example-instantiation-of-a-multisig-key","title":"Example instantiation of a multisig key","text":"

This example represents a shared multisig key that could be used within a business amongst three account holders - where at least two of three (K=2) must sign off on each transaction.

# Create the three keys owned by the separate account holders\nfetchd keys add fred\nfetchd keys add ted\nfetchd keys add ned\n\n# Create the multisig key from keys above\nfetchd keys add business_key --multisig fred,ted,ned --multisig-threshold 2\n

You will need the address of the business_key later in the example. Here just a reminder how to get it:

fetchd keys show -a business_key\n
"},{"location":"ledger_v2/cli-multisig/#signing-and-broadcasting-multisig-transactions","title":"Signing and broadcasting multisig transactions","text":"

Transactions must be signed and broadcast before they are carried out.

In order to sign a multisig transaction, the transaction itself must not be immediately broadcast; but instead, the keyholders must each sign until a minimum threshold K signatures are present.

For this example we will be performing the transaction on the Dorado network and therefore will be using atestfet as the denomination, and a gas price of 1000000000atestfet (this should be changed depending on the actual currency and network used).

"},{"location":"ledger_v2/cli-multisig/#multisig-transaction-example","title":"Multisig transaction example","text":"
# Create a key to represent a vendor that the business must pay\nfetchd keys add vendor\n\n# Generate a transaction as an output file to be signed by\n# the keyholders, 'ted' and 'fred' in this example\nfetchd tx bank send <business_key address> <vendor address> 1000atestfet --gas 90000 --gas-prices 1000000000atestfet --generate-only > transfer.json\n\n# you'll get \"account <address of business_key> not found\" error for missing funds\n# add funds to <address of business_key> using block explorer or by eg\ncurl -X POST -H 'Content-Type: application/json' -d '{\"address\":\"<address of business_key>\"}' https://faucet-dorado.fetch.ai/api/v3/claims\n\n# This transaction file (transfer.json) is then made available for\n# the first keyholder to sign, 'fred'\nfetchd tx sign transfer.json --chain-id dorado-1 --from fred --multisig <address of business_key> > transfer_fredsigned.json\n\n# This is repeated for 'ted'\nfetchd tx sign transfer.json --chain-id dorado-1 --from ted --multisig <address of business_key> > transfer_tedsigned.json\n\n# These two files are then collated together and used as inputs to the\n# multisign command to create a fully signed transaction\nfetchd tx multisign transfer.json business_key transfer_fredsigned.json transfer_tedsigned.json > signed_transfer.json\n\n# Now that the transaction is fully signed, it may be broadcast\nfetchd tx broadcast signed_transfer.json\n\n# Now display the result of the transaction and confirm that the vendor has\n# received payment\nfetchd query bank balances <address of vendor>\n

It is important to note that this method of signing transactions can apply to all types of transaction.

"},{"location":"ledger_v2/cli-multisig/#other-multisig-transaction-examples","title":"Other multisig transaction examples","text":"
# In order to create a staking transaction using a multisig key\n# the same process as above can be used with the output file of this command\nfetchd tx staking delegate <fetchvaloper address> 10000atestfet --from <address of business_key> --gas 200000 --gas-prices 1000000000atestfet --generate-only > stake.json\n\n# The following command can also be used to create a withdrawal transaction for the\n# rewards from staking when using a multisig key - this too must be signed as before\nfetchd tx distribution withdraw-all-rewards --from <address of business_key> --gas 150000 --gas-prices 1000000000atestfet --generate-only > withdrawal.json\n
"},{"location":"ledger_v2/cli-tokens/","title":"CLI - Managing Tokens","text":""},{"location":"ledger_v2/cli-tokens/#querying-your-balance","title":"Querying your balance","text":"

Once fetchd is configured for the desired network. The user can query their balance using the following command:

fetchd query bank balances fetch1akvyhle79nts4rwn075t85xrwmp5ysuqynxcn4\n

If the address exists on the network then the user will expect to see an output in the following form:

balances:\n- amount: \"8000000000000000000\"\n  denom: atestfet\npagination:\n  next_key: null\n  total: \"0\"\n
"},{"location":"ledger_v2/cli-tokens/#sending-funds","title":"Sending funds","text":"

Before sending funds, make sure the sender address has tokens available by querying your balance as shown above. Checkout the Token Faucet page for more information on how to add test tokens to your address.

To send funds from one address to another address then you would use the tx send subcommand. As shown below:

fetchd tx bank send <from address or key name> <target address> <amount>\n

In a more concrete example if the user wanted to send 100atestfet from main key/address to fetch106vm9q6ezu9va7v7e0cvq0nedc54egjm692fcp then the following command would be used.

fetchd tx bank send main fetch106vm9q6ezu9va7v7e0cvq0nedc54egjm692fcp 100atestfet\n

When you run the command you will get a similar output and prompt. The user can check the details of the transfer and then press 'y' to confirm the transfer.

{\"body\":{\"messages\":[{\"@type\":\"/cosmos.bank.v1beta1.MsgSend\",\"from_address\":\"fetch12cjntwl32dry7fxck8qlgxq6na3fk5juwjdyy3\",\"to_address\":\"fetch1hph8kd54gl6qk0hy5rl08qw9gcr4vltmk3w02v\",\"amount\":[{\"denom\":\"atestfet\",\"amount\":\"100\"}]}],\"memo\":\"\",\"timeout_height\":\"0\",\"extension_options\":[],\"non_critical_extension_options\":[]},\"auth_info\":{\"signer_infos\":[],\"fee\":{\"amount\":[],\"gas_limit\":\"200000\",\"payer\":\"\",\"granter\":\"\"}},\"signatures\":[]}\n\nconfirm transaction before signing and broadcasting [y/N]: y\n

Once the transfer has been made a summary is presented to the user. An example is shown below:

code: 0\ncodespace: \"\"\ndata: \"\"\ngas_used: \"0\"\ngas_wanted: \"0\"\nheight: \"0\"\ninfo: \"\"\nlogs: []\nraw_log: '[]'\ntimestamp: \"\"\ntx: null\ntxhash: 77C7382A0B1B9FE39257A6C16C7E3169A875CB3A87F2CE9D947D7C1335B53E76\n

On failure, the response will have a non zero code, as well as some logs under the raw_log key:

code: 4\ncodespace: sdk\ndata: \"\"\ngas_used: \"0\"\ngas_wanted: \"0\"\nheight: \"0\"\ninfo: \"\"\nlogs: []\nraw_log: 'signature verification failed; please verify account number (5815) and chain-id\n  (dorado-1): unauthorized'\ntimestamp: \"\"\ntx: null\ntxhash: 23701B052B423D63EB4AC94773B5B8227B03A576692A57999E92F2554F2372D4\n
"},{"location":"ledger_v2/delegator-guide-cli/","title":"CLI - Delegator guide","text":""},{"location":"ledger_v2/delegator-guide-cli/#querying-the-state","title":"Querying the state","text":""},{"location":"ledger_v2/delegator-guide-cli/#querying-the-current-staking-holdings-of-the-validators","title":"Querying the current staking holdings of the validators","text":"

The following command can be used to retrieve the current staking holdings of all validators:

fetchd query staking validators\n

On dorado network, this will produce an output similar to the following, describing the status of all the existing validators:

- |\n  operatoraddress: fetchvaloper1z72rph6l5j6ex83n4urputykawcqg6t98xul2w\n  conspubkey: fetchvalconspub1zcjduepq3urw6c6u0zvqmde4vr4gmy56nnq57shdhg56jynpu8n3s74hrm0q0mzqrx\n  jailed: false\n  status: 2\n  tokens: \"1000000000000000000000\"\n  delegatorshares: \"1000000000000000000000.000000000000000000\"\n  description:\n    moniker: validator5\n    identity: \"\"\n    website: \"\"\n    security_contact: \"\"\n    details: \"\"\n  unbondingheight: 0\n  unbondingcompletiontime: 1970-01-01T00:00:00Z\n  commission:\n    commission_rates:\n      rate: \"0.050000000000000000\"\n      max_rate: \"0.100000000000000000\"\n      max_change_rate: \"0.010000000000000000\"\n    update_time: 2021-02-12T12:41:25.579730119Z\n  minselfdelegation: \"1000000000000000000000\"\n  producingblocks: true\n- |\n  operatoraddress: fetchvaloper1ysc8n5uspv4698nyk8u75lx98uu92zt7m3udw8\n  conspubkey: fetchvalconspub1zcjduepqmxr8gmcs6pwuxpsma264ax59wxtxd3vchrcv2c06deq9986kwt3s0wsk6n\n  jailed: false\n  status: 2\n  tokens: \"1000000000000000000000\"\n  delegatorshares: \"1000000000000000000000.000000000000000000\"\n  description:\n    moniker: validator2\n    identity: \"\"\n    website: \"\"\n    security_contact: \"\"\n    details: \"\"\n  unbondingheight: 0\n  unbondingcompletiontime: 1970-01-01T00:00:00Z\n  commission:\n    commission_rates:\n      rate: \"0.050000000000000000\"\n      max_rate: \"0.100000000000000000\"\n      max_change_rate: \"0.010000000000000000\"\n    update_time: 2021-02-03T13:00:00Z\n  minselfdelegation: \"1000000000000000000000\"\n  producingblocks: true\n...\n

To obtain the same information for a single validator, use the following command, providing the operatoraddress of the validator.

fetchd query staking validator fetchvaloper1z72rph6l5j6ex83n4urputykawcqg6t98xul2w\n

A delegator will be particularly interested in the following keys:

  • commission/commission_rates/rate: The commission rate on revenue charged to any delegator by the validator.
  • commission/commission_rates/max_change_rate: The maximum daily increase of the validator's commission. This parameter cannot be changed by the validator operator.
  • commission/commission_rates/max_rate: The maximum commission rate this validator can charge. This parameter cannot be changed by the validator operator.
  • minselfdelegation: Minimum amount of atestfet the validator need to have bonded at all time. If the validator's self-bonded stake falls below this limit, their entire staking pool (i.e. all its delegators) will unbond. This parameter exists as a safeguard for delegators. Indeed, when a validator misbehaves, part of their total stake gets slashed. This includes the validator's self-delegateds stake as well as their delegators' stake. Thus, a validator with a high amount of self-delegated atestfet has more skin-in-the-game than a validator with a low amount. The minimum self-bond amount parameter guarantees to delegators that a validator will never fall below a certain amount of self-bonded stake, thereby ensuring a minimum level of skin-in-the-game. This parameter can only be increased by the validator operator.
"},{"location":"ledger_v2/delegator-guide-cli/#query-the-delegations-made-to-a-validator","title":"Query the delegations made to a validator","text":"

From a validator address, we can retrieve the list of delegations it received:

fetchd query staking delegations-to fetchvaloper1z72rph6l5j6ex83n4urputykawcqg6t98xul2w\n

Here is a sample of delegations validator2 received on dorado:

- delegation:\n    delegator_address: fetch1z72rph6l5j6ex83n4urputykawcqg6t9zzruef\n    validator_address: fetchvaloper1z72rph6l5j6ex83n4urputykawcqg6t98xul2w\n    shares: \"1000000000000000000000.000000000000000000\"\n  balance:\n    denom: atestfet\n    amount: \"1000000000000000000000\"\n- delegation:\n    delegator_address: fetch15fn3meky8ktfry3qm73xkpjckzw4dazxpfx34m\n    validator_address: fetchvaloper1z72rph6l5j6ex83n4urputykawcqg6t98xul2w\n    shares: \"100000.000000000000000000\"\n  balance:\n    denom: atestfet\n    amount: \"100000\"\n
"},{"location":"ledger_v2/delegator-guide-cli/#query-the-redelegations","title":"Query the redelegations","text":"

Delegators can choose to redelegate the tokens they already delegated from one validator to another. Redelegation takes effect immediately, without any waiting period. However, the tokens can't be redelegated until the initial redelegation transaction has completed its 21 day completion time (the unlocking time is indicated by the redelegationentry/completion_time field in the outputs below).

To obtain the list of redelegations made from a validator, use the following command:

fetchd query staking redelegations-from fetchvaloper1z72rph6l5j6ex83n4urputykawcqg6t98xul2w\n

This produces an output similar to the following, where delegator fetch15fn3meky8ktfry3qm73xkpjckzw4dazxpfx34m issued 2 redelegations from fetchvaloper1z72rph6l5j6ex83n4urputykawcqg6t98xul2w to fetchvaloper122veneudkzyalay6gusvrhhpp0560mparpanvu:

fetchd query staking redelegations-from fetchvaloper1z72rph6l5j6ex83n4urputykawcqg6t98xul2w\n- redelegation:\n    delegator_address: fetch15fn3meky8ktfry3qm73xkpjckzw4dazxpfx34m\n    validator_src_address: fetchvaloper1z72rph6l5j6ex83n4urputykawcqg6t98xul2w\n    validator_dst_address: fetchvaloper122veneudkzyalay6gusvrhhpp0560mparpanvu\n    entries: []\n  entries:\n  - redelegationentry:\n      creation_height: 291037\n      completion_time: 2021-03-24T14:24:38.973444629Z\n      initial_balance: \"50000\"\n      shares_dst: \"50000.000000000000000000\"\n    balance: \"50000\"\n  - redelegationentry:\n      creation_height: 291133\n      completion_time: 2021-03-24T14:33:43.425472866Z\n      initial_balance: \"10000\"\n      shares_dst: \"10000.000000000000000000\"\n    balance: \"10000\"\n

Similarly, the list of redelegations issued by a delegator can be obtained with the following:

fetchd query staking redelegations fetch15fn3meky8ktfry3qm73xkpjckzw4dazxpfx34m\n
"},{"location":"ledger_v2/delegator-guide-cli/#query-the-user-rewards","title":"Query the user rewards","text":"

After having delegated some tokens to a validator, the user is eligible to a share of the rewards the validator collects.

To retrieve all the outstanding rewards for an address, issue the following command:

fetchd query distribution rewards fetch15fn3meky8ktfry3qm73xkpjckzw4dazxpfx34m\n

This address having delegated tokens to 2 validators on dorado, produces the following output:

rewards:\n- validator_address: fetchvaloper1z72rph6l5j6ex83n4urputykawcqg6t98xul2w\n  reward:\n  - denom: atestfet\n    amount: \"0.000000000000200000\"\n- validator_address: fetchvaloper1ysc8n5uspv4698nyk8u75lx98uu92zt7m3udw8\n  reward:\n  - denom: atestfet\n    amount: \"0.000000000001000000\"\ntotal:\n- denom: atestfet\n  amount: \"0.000000000001200000\"\n

Rewards can also be filtered for a given validator, like validator5 here:

fetchd query distribution rewards fetch15fn3meky8ktfry3qm73xkpjckzw4dazxpfx34m fetchvaloper1z72rph6l5j6ex83n4urputykawcqg6t98xul2w\n

We now get only the reward from this validator:

- denom: atestfet\n  amount: \"0.000000000000200000\"\n
"},{"location":"ledger_v2/delegator-guide-cli/#delegator-operations","title":"Delegator operations","text":""},{"location":"ledger_v2/delegator-guide-cli/#delegating-tokens","title":"Delegating tokens","text":"

To delegate 1000000 atestfet tokens to the fetchvaloper1z72rph6l5j6ex83n4urputykawcqg6t98xul2w validator from the account myKey, the following command can be used:

fetchd tx staking delegate fetchvaloper1z72rph6l5j6ex83n4urputykawcqg6t98xul2w 1000000atestfet --from myKey\n

This will prompt for confirmation before issuing a transaction. After the transaction gets processed, it should appear under the delegations of the fetchvaloper1z72rph6l5j6ex83n4urputykawcqg6t98xul2w validator.

Note

Once delegated, tokens can only be redelegated to another validator, or unbond in order to be returned to their original account. It's important to note that those two operations take 21 days to complete, period in which the involved tokens will be unavailable.

"},{"location":"ledger_v2/delegator-guide-cli/#redelegating-tokens","title":"Redelegating tokens","text":"

Redelegating tokens allows to transfer already delegated tokens from one validator to another.

From the above example where we delegated 1000000 atestfet to fetchvaloper1z72rph6l5j6ex83n4urputykawcqg6t98xul2w, we can now redelegate parts or all of those tokens to another validator. For example, we redelegate 400000 atestfet from fetchvaloper1z72rph6l5j6ex83n4urputykawcqg6t98xul2w to fetchvaloper122veneudkzyalay6gusvrhhpp0560mparpanvu with the following command:

fetchd tx staking redelegate fetchvaloper1z72rph6l5j6ex83n4urputykawcqg6t98xul2w fetchvaloper122veneudkzyalay6gusvrhhpp0560mparpanvu 400000atestfet --from myKey\n

This will prompt for confirmation and issue a new transaction once accepted. From here, inspecting the delegations from our account, we'll see that our delegated tokens are now:

  • 600000atestfet to validator fetchvaloper1z72rph6l5j6ex83n4urputykawcqg6t98xul2w (our initial 1000000 minus the 400000 redelegated)
  • 400000atestfet to validator fetchvaloper122veneudkzyalay6gusvrhhpp0560mparpanvu

Now those 400000 atestfet we redelegated can't be redelegated anymore for 21 days (the exact date can be found by querying the redelegation transaction, under the completion_time key). Note that it's still possible to unbond those tokens if needed.

"},{"location":"ledger_v2/delegator-guide-cli/#unbonding-tokens","title":"Unbonding tokens","text":"

At any time, we can transfer parts or all of our delegated tokens back to our account:

fetchd tx staking unbond fetchvaloper1z72rph6l5j6ex83n4urputykawcqg6t98xul2w 300000atestfet --from myKey\n

Once again, this will prompt for confirmation and issue a transaction, initiating the transfer of 300000 atestfet from our stake on fetchvaloper1z72rph6l5j6ex83n4urputykawcqg6t98xul2w validator back to our account. Those tokens will then be available after a 21 day period (the exact date can be found by querying the redelegation transaction, under the completion_time key).

"},{"location":"ledger_v2/delegator-guide-cli/#withdrawing-rewards","title":"Withdrawing rewards","text":"

In order to transfer rewards to the wallet, the following command can be used:

fetchd tx distribution withdraw-rewards fetchvaloper1z72rph6l5j6ex83n4urputykawcqg6t98xul2w --from myKey\n

It requires the validator address from where the reward is withdrawn, and the name of the account private key having delegated tokens to the validator.

When having delegated tokens to multiple validators, all rewards can be claimed in a single command:

fetchd tx distribution withdraw-all-rewards --from myKey\n

The rewards then appears on the account as soon as the transaction is processed.

"},{"location":"ledger_v2/faucet/","title":"Token Faucet","text":"

For our test networks, we have a simple token faucet implemented to allow users of the network to get started quickly. You can send it an account address, and it will transfer some test token on it.

Token Faucets are network specific, depending on the network type they may or may not be deployed. Please check the networks page for specific details.

The Token Faucet itself is available from the network block explorer (GET FUNDS button on the homepage).

Enter your fetch... address in the popup and click Add funds button. Wait a few blocks for the transaction to be processed, and you should see it appear along with some funds on your account.

"},{"location":"ledger_v2/faucet/#add-funds-to-wallet-using-faucet-apis","title":"Add funds to Wallet using faucet APIs:","text":"

You can also request and get testnet tokens in your wallet using the APIs.

"},{"location":"ledger_v2/faucet/#get-some-atestfet","title":"Get some atestfet","text":"
curl -X POST -H 'Content-Type: application/json' -d '{\"address\":\"<address>\"}' https://faucet-dorado.fetch.ai/api/v3/claims\n
"},{"location":"ledger_v2/faucet/#get-some-nanomobx","title":"Get some nanomobx","text":"
curl -X POST -H 'Content-Type: application/json' -d '{\"address\":\"<address>\"}' https://faucet-mobx-dorado.fetch.ai/api/v3/claims\n
"},{"location":"ledger_v2/faucet/#get-some-ulrn","title":"Get some ulrn","text":"
curl -X POST -H 'Content-Type: application/json' -d '{\"address\":\"<address>\"}' https://faucet-lrn-dorado.fetch.ai/api/v3/claims\n
"},{"location":"ledger_v2/faucet/#sample-response-for-fund-request-to-faucet","title":"Sample response for fund request to faucet","text":"
{\"status\":\"ok\",\"uuid\":\"<uuid>\",\"target\":\"<address>\"}\n
"},{"location":"ledger_v2/faucet/#check-the-wallet-balance","title":"Check the wallet balance","text":"
fetchd query bank balances <address>\n
balances:\n- amount: \"<balance>\"\n  denom: atestfet\npagination:\n  next_key: null\n  total: \"0\"\n
"},{"location":"ledger_v2/governance/","title":"Governance","text":"

In order to be able to take part in the governance you either need to be running a full validator node or you need to have have delegated stake to an existing validator

"},{"location":"ledger_v2/governance/#stake-delegation","title":"Stake Delegation","text":"

In order to delegate stake to a validator the following command should be used:

fetchd tx staking delegate <VALOPER_ADDRESS> <AMOUNT> --from <KEY_NAME>\n

Where the <VALOPER_ADDRESS> begins with the prefix fetchvaloper1... and the <AMOUNT> field contains the currency denomination. For example:

fetchd tx staking delegate fetchvaloper1cct4fhhksplu9m9wjljuthjqhjj93z0s97p3g7 1000atestfet --from agent\n
"},{"location":"ledger_v2/governance/#proposals-overview","title":"Proposals Overview","text":"

There are three types of proposal:

  • Text Proposals: These are the most basic type of proposal. They can be used to get the opinion from participants of the network on a given topic.
  • Parameter Proposals: These proposals are used to update the value of an existing software parameter of the network.
  • Software Upgrade Proposals: These are used to propose an upgrade of the fetchd software, particularly in cases where the software changes might not necessary be backwards compatible or in some way present a major update to the network.
"},{"location":"ledger_v2/governance/#the-proposal-process","title":"The Proposal Process","text":"

Any FET holder can submit a proposal. In order for the proposal to be open for voting, it needs to come with a deposit that is greater than a parameter called minDeposit. The deposit need not be provided in its entirety by the submitter. If the initial proposer's deposit is not sufficient, the proposal enters the deposit period status. Then, any FET holder can increase the deposit by sending a depositTx transaction to the network.

Once the deposit reaches minDeposit, the proposal enters the voting period, which lasts 2 weeks. Any bonded FET holder can then cast a vote on this proposal. The user has the following options for voting:

  • Yes
  • No
  • NoWithVeto
  • Abstain

At the end of the voting period, the proposal is accepted if there are more than 50% Yes votes (excluding Abstain votes) and less than 33.33% of NoWithVeto votes (excluding Abstain votes).

"},{"location":"ledger_v2/governance/#generating-proposals","title":"Generating Proposals","text":"

When creating a proposal, the user will create a proposal JSON file with all the relevant information. An example of a text proposal is shown below:

{\n\"title\": \"Switch to semantic commit messages for fetchd\",\n\"description\": \"This proposal is advocating a switch to sematic commit messages\\nYou can find the full discussion here: https://github.com/fetchai/fetchd/issues/231\",\n\"type\": \"Text\",\n\"deposit\": \"10000000000000000000atestfet\"\n}\n

It is always recommended that the description of a text proposal has a link to a Github issue with the full proposal text along with the discussions about it.

Once the user has created the JSON file, to generate the text propsal on chain run the following command:

fetchd tx gov submit-proposal --proposal proposal.json --from <name of signing key>

"},{"location":"ledger_v2/governance/#increasing-the-deposit-for-a-proposal","title":"Increasing the deposit for a proposal","text":"

If a user wants to increase the deposit of a proposal they would run the following command:

fetchd tx gov deposit <proposalID> 100atestfet --from <key name>

For example:

fetchd tx gov deposit 2 100atestfet --from validator

To get the proposalID, use the txhash obtained when the proposal was submitted and run the following command:

fetchd query tx <txhash>

"},{"location":"ledger_v2/governance/#listing-current-proposals","title":"Listing current proposals","text":"

Current proposals are visible from the block explorer and using the CLI.

To get the list of current proposals and their corresponding proposal-ids the run the following command:

fetchd query gov proposals

"},{"location":"ledger_v2/governance/#voting-on-a-proposal","title":"Voting on a proposal","text":"

To vote for a proposal run the following command

fetchd tx gov vote <proposalID> <option> --from <delegatorKeyName>

For example:

fetchd tx gov vote 5 yes --from validator

Note

When using CLI commands make sure that your CLI is pointing at the correct network. See the CLI introduction documentation for more details

"},{"location":"ledger_v2/joining-a-testnet/","title":"Joining a testnet","text":"

In order to join the test network you will need to have the correct version of the fetchd ledger available on your system.

"},{"location":"ledger_v2/joining-a-testnet/#using-a-local-version","title":"Using a local version","text":"

Assuming that you have followed the installation guide. You should now have fetchd successfully installed in your path. You can check this with the following command:

fetchd version\n

This should print a version number that must be compatible with the network you're connecting to (see the network page for the list of supported versions per network).

"},{"location":"ledger_v2/joining-a-testnet/#configuring-the-client-fetchd","title":"Configuring the client fetchd","text":"

In general to configure the CLI to point at a given network it needs as a minimum the following configuration values

fetchd config chain-id <chain-id>\nfetchd config node <rpc url>\n
"},{"location":"ledger_v2/joining-a-testnet/#dorado-example","title":"Dorado example","text":"

In the case of the Dorado network this would be as follows:

fetchd config chain-id dorado-1\nfetchd config node https://rpc-dorado.fetch.ai:443\n
"},{"location":"ledger_v2/joining-a-testnet/#configuring-the-server-fetchd","title":"Configuring the server fetchd","text":"

Initialize fetchd by running command. This setups a default / empty genesis configuration.

fetchd init <moniker-name> --chain-id <chain id>\n

This will initialize default configuration files under the FETCHD_HOME folder, which default to ~/.fetchd/.

Execute the following command to download the latest genesis file:

curl <rpc url>/genesis | jq '.result.genesis' > ~/.fetchd/config/genesis.json\n

Finally connect fetchd to the network by getting it to connect to a seed node for the given network.

fetchd start --p2p.seeds=<network seed peers>\n

Dorado Example

Less abstractly then, if you wants to connect to the Dorado test net for example, you would need to run the following steps:

# init\nfetchd init my-first-fetch-node --chain-id dorado-1\n\n# genesis\ncurl https://rpc-dorado.fetch.ai:443 | jq '.result.genesis' > ~/.fetchd/config/genesis.json\n# ...or, if that's too large to download from the rpc interface as a single file...\ncurl https://storage.googleapis.com/fetch-ai-testnet-genesis/genesis-dorado-827201.json --output ~/.fetchd/config/genesis.json\n\n# start\nfetchd start --p2p.seeds=eb9b9717975b49a57e62ea93aa4480e091ae0660@connect-dorado.fetch.ai:36556,46d2f86a255ece3daf244e2ca11d5be0f16cb633@connect-dorado.fetch.ai:36557,066fc564979b1f3173615f101b62448ac7e00eb1@connect-dorado.fetch.ai:36558\n

Your local node will then start to synchronise itself with the network, replaying all blocks and transactions up to the current block. Depending on the age of the network and your hard disk speed, this could take a while. Consider using chain snapshots to speed up this process.

To know when your node as finished syncing, you can query it's status from its RPC API:

curl -s 127.0.0.1:26657/status |  jq '.result.sync_info.catching_up'\ntrue # this will print \"false\" once your node is up to date\n
"},{"location":"ledger_v2/live-networks/","title":"Networks","text":""},{"location":"ledger_v2/live-networks/#mainnet","title":"Mainnet","text":"

The chain identifier of our production network is fetchhub-4.

Parameter Value Chain ID fetchhub-4 Block range 5,300,201 --> Date range 05/04/2022 --> Denomination afet Decimals 18 (1fet = 1000000000000000000afet) Version v0.10.3 up to block 6295500 v0.10.4 up to block 7305500 v0.10.5 for blocks > 7305500 RPC Endpoint https://rpc-fetchhub.fetch.ai:443 GRPC Endpoint https://grpc-fetchhub.fetch.ai:443 REST Endpoint https://rest-fetchhub.fetch.ai:443 Block Explorer https://explore-fetchhub.fetch.ai Token Faucet N/A Genesis curl https://raw.githubusercontent.com/fetchai/genesis-fetchhub/fetchhub-4/fetchhub-4/data/genesis_migrated_5300200.json --output ~/.fetchd/config/genesis.json Seed Node(s) 17693da418c15c95d629994a320e2c4f51a8069b@connect-fetchhub.fetch.ai:36456,a575c681c2861fe945f77cb3aba0357da294f1f2@connect-fetchhub.fetch.ai:36457,d7cda986c9f59ab9e05058a803c3d0300d15d8da@connect-fetchhub.fetch.ai:36458 Snapshots https://storage.googleapis.com/fetch-ai-mainnet-snapshots/fetchhub-4-pruned.tgz https://storage.googleapis.com/fetch-ai-mainnet-snapshots/fetchhub-4-full.tgz https://storage.googleapis.com/fetch-ai-mainnet-snapshots/fetchhub-4-archive.tgz"},{"location":"ledger_v2/live-networks/#test-nets","title":"Test Nets","text":""},{"location":"ledger_v2/live-networks/#dorado","title":"Dorado","text":"

This network is running the same major version of fetchd as our mainnet (fetchhub-4), possibly at a more recent minor version.

It is stable for deploying smart contracts and testing IBC.

Parameter Value Chain ID dorado-1 Denomination atestfet Decimals 18 (1testfet = 1000000000000000000atestfet) Min Gas Prices 1000000000atestfet Version v0.10.3 up to block 947800 v0.10.4 for blocks > 947800 and < 2198000 v0.10.5 for blocks > 2198000 RPC Endpoint https://rpc-dorado.fetch.ai:443 GRPC Endpoint https://grpc-dorado.fetch.ai:443 REST Endpoint https://rest-dorado.fetch.ai:443 Block Explorer https://explore-dorado.fetch.ai/ Ledger Explorer https://browse-dorado.fetch.ai/ Token Faucet Use block explorer Genesis curl https://storage.googleapis.com/fetch-ai-testnet-genesis/genesis-dorado-827201.json --output ~/.fetchd/config/genesis.json Seed Node(s) eb9b9717975b49a57e62ea93aa4480e091ae0660@connect-dorado.fetch.ai:36556,46d2f86a255ece3daf244e2ca11d5be0f16cb633@connect-dorado.fetch.ai:36557,066fc564979b1f3173615f101b62448ac7e00eb1@connect-dorado.fetch.ai:36558 Snapshots https://storage.googleapis.com/fetch-ai-testnet-snapshots/dorado-pruned.tgz https://storage.googleapis.com/fetch-ai-testnet-snapshots/dorado-full.tgz https://storage.googleapis.com/fetch-ai-testnet-snapshots/dorado-archive.tgz"},{"location":"ledger_v2/single-node-network/","title":"Running a Single Node Network","text":"

Especially for things like contract development, it can be very useful to be able to run a single node network for testing. This document will outline the steps that are required in order to configure a fetchd network of 1 node.

"},{"location":"ledger_v2/single-node-network/#network-setup","title":"Network Setup","text":"

These steps only need to be done once in order to setup the local network.

Step 1 - Build the ledger from source

Follow the build instructions in order to compile the latest version of the ledger.

Step 2 - Remove any existing networks

Since we are starting a new network we need to remove any local files that we have in our system from a previous network

rm -Rf ~/.fetchd

Step 3 - Create an initial genesis

Create the initial genesis file (~/.fetchd/config/genesis.json) with the following command:

fetchd init --chain-id localnet-1 my-local-node-name

  • localnet-1 is the chain id
  • my-local-node-name is the moniker for the node

If you want to make any updates to the genesis, it is a good opportunity to make these updates now.

Step 4 - Create your validator key

In the following steps we will need to create the public/private keypair for our node.

To create a new key called \"validator\" use the following command.

fetchd keys add validator

  • validator is the name of the key in the keyring

For more information checkout the complete documentation on keys.

Step 5 - Adding the validator to the network

To set the initial state for the network use the following command. This allocates 100000000000000000000 stake tokens to the validator which can be bonded.

fetchd add-genesis-account validator 100000000000000000000stake

stake is the default test token denomination in the cosmos ecosystem, but you could use afet, BTC etc.

Step 6 - Generating a validator transaction

To get your validator to sign the genesis block (and to agree that this is the correct genesis starting point) use the following command.

fetchd gentx validator 100000000000000000000stake --chain-id localnet-1

  • validator here is the name that you have given to the key

Step 7 - Building the complete genesis

To build final genesis configuration for the network run the following command

fetchd collect-gentxs

After running this command the network is successfully configured and you have computed the final genesis configuration for the network.

"},{"location":"ledger_v2/single-node-network/#running-the-local-node","title":"Running the local node","text":"

To run the network use the following command.

fetchd start

"},{"location":"ledger_v2/single-node-network/#resetting-the-network","title":"Resetting the network","text":"

Often you will want to clear out all the data from the network and start again. To do that in a local network simply run the following command:

fetchd tendermint unsafe-reset-all

This resets the chain back to genesis, you DO NOT need to perform the network setup steps again. After running this command you can simply run the fetchd start command again.

"},{"location":"ledger_v2/snapshots/","title":"Chain State Snapshots","text":"

As blockchains get longer, the process of syncing from the genesis block begins to take many hours, or even days to complete. In circumstances where a faster sync is required, various snapshots of the fetchd chain state data are available for download, to more quickly bootstrap a node.

Snapshots are available for both mainnet and the most recent testnet. The URLs can be obtained from the network page. We aim to update snapshots on a daily basis.

The example below uses the pruned mainnet snapshot, but can be adapted as required for full or archive nodes.

"},{"location":"ledger_v2/snapshots/#using-a-snapshot","title":"Using a snapshot","text":""},{"location":"ledger_v2/snapshots/#stop-your-node","title":"Stop your node","text":"

If you are already running fetchd, it is important that you stop it before proceeding. Instructions for this are highly installation dependent and beyond the scope of this document, but could be as simple as a Ctrl-C. If you have not already initialised your node, follow the instructions for joining a testnet (modifying for mainnet as appropriate), then return to this page before starting fetchd.

"},{"location":"ledger_v2/snapshots/#reset-your-node","title":"Reset your node","text":"

WARNING: This will irreversibly erase your node's state database. Ensure you take whatever backups you deem appropriate before proceeding.

If using fetchd <= 0.10.3 fetchd unsafe-reset-all

If using fetchd >= 0.10.4 fetchd tendermint unsafe-reset-all

"},{"location":"ledger_v2/snapshots/#download-and-install-the-snapshot","title":"Download and install the snapshot","text":"

Many options here! The example below assumes a bash-like environment, uses a single connection for downloading, confirms the md5sum of the downloaded data against that of the original, and does not land the original compressed data to disk. This is a good starting point, but depending on your local environment you may wish to make adaptations that eg sacrifice disk space and extra md5sum complexity for the benefit of parallel downloads with aria2. Entirely up to you... let us know how you get on!

# (optional) show the timestamp of the latest available snapshot\necho \"Latest available snapshot timestamp : $(curl -s -I  https://storage.googleapis.com/fetch-ai-mainnet-snapshots/fetchhub-4-pruned.tgz | grep last-modified | cut -f3- -d' ')\"\n# download, decompress and extract state database\ncurl -v https://storage.googleapis.com/fetch-ai-mainnet-snapshots/fetchhub-4-pruned.tgz -o- 2>headers.out | tee >(md5sum > md5sum.out) | gunzip -c | tar -xvf - --directory=~/.fetchd\n\n# (optional, but recommended) compare source md5 checksum provided in the headers by google, with the one calculated locally\n[[ $(grep 'x-goog-hash: md5' headers.out | sed -z 's/^.*md5=\\(.*\\)/\\1/g' | tr -d '\\r' | base64 -d | od -An -vtx1 | tr -d ' \\n') == $(awk '{ print $1 }' md5sum.out) ]] && echo \"OK - md5sum match\" || echo \"ERROR - md5sum MISMATCH\"\n# (optional) show the creation date of the downloaded snapshot\necho \"Downloaded snapshot timestamp: $(grep last-modified headers.out | cut -f3- -d' ')\"\n
"},{"location":"ledger_v2/snapshots/#restart-your-node","title":"Restart your node","text":"

Again, this entirely depends on your local installation, but a simple example for mainnet might be...

fetchd start --p2p.seeds 17693da418c15c95d629994a320e2c4f51a8069b@connect-fetchhub.fetch.ai:36456,a575c681c2861fe945f77cb3aba0357da294f1f2@connect-fetchhub.fetch.ai:36457,d7cda986c9f59ab9e05058a803c3d0300d15d8da@connect-fetchhub.fetch.ai:36458`.\n
"},{"location":"ledger_v2/state-sync/","title":"State-sync","text":"

State sync is a feature which allows you to quickly bootstrap a new node by allowing it to pull a state snapshot taken by other nodes.

The state sync feature is only available from fetchd v0.10.6 and later. Prior versions needed to either sync from scratch or restore a chain snapshot, which both could take hours before having the node fully synced.

With state sync, it now takes only a few minutes before having an operational node.

"},{"location":"ledger_v2/state-sync/#configuring-the-new-node","title":"Configuring the new node","text":"

In order to instruct the node to sync itself using a state sync snapshot, it need some configuration in the ~/.fetchd/config/config.toml file. Open this file in an editor and lookup for the statesync section. By default, it should looks like this:

#######################################################\n###         State Sync Configuration Options        ###\n#######################################################\n[statesync]\n# State sync rapidly bootstraps a new node by discovering, fetching, and restoring a state machine\n# snapshot from peers instead of fetching and replaying historical blocks. Requires some peers in\n# the network to take and serve state machine snapshots. State sync is not attempted if the node\n# has any local state (LastBlockHeight > 0). The node will have a truncated block history,\n# starting from the height of the snapshot.\nenable = false\n# RPC servers (comma-separated) for light client verification of the synced state machine and\n# retrieval of state data for node bootstrapping. Also needs a trusted height and corresponding\n# header hash obtained from a trusted source, and a period during which validators can be trusted.\n#\n# For Cosmos SDK-based chains, trust_period should usually be about 2/3 of the unbonding time (~2\n# weeks) during which they can be financially punished (slashed) for misbehavior.\nrpc_servers = \"\"\ntrust_height = 0\ntrust_hash = \"\"\ntrust_period = \"168h0m0s\"\n# Time to spend discovering snapshots before initiating a restore.\ndiscovery_time = \"15s\"\n# Temporary directory for state sync snapshot chunks, defaults to the OS tempdir (typically /tmp).\n# Will create a new, randomly named directory within, and remove it when done.\ntemp_dir = \"\"\n# The timeout duration before re-requesting a chunk, possibly from a different\n# peer (default: 1 minute).\nchunk_request_timeout = \"10s\"\n# The number of concurrent chunk fetchers to run (default: 1).\nchunk_fetchers = \"4\"\n

A few changes are needed:

  • First, set enable = true to activate the state sync engine.
  • Then, at least 2 rpc servers must be provided. A good place to find some is the cosmos chain registry. Servers must be comma separated without space (ie: rpc_servers = \"https://rpc-fetchhub.fetch.ai:443,https://fetch-rpc.polkachu.com:443\"). These servers will be used to verify the snapshots, so make sure you trust them enough for this.
  • a recent trust_height and trust_hash are needed. Recent means it must be contained in the trust_period (168 hours, or ~1 week old by default). These can be obtained from a RPC server you trust to provide you correct data (and the 2nd RPC server from rpc_servers will be charged of confirming that the data are correct).
  • And last, set chunk_request_timeout to 60s (the 10s default value seems too short and can lead to \"context deadline exceeded\" timeout errors when verifying the hashes)

To retrieve the correct value for a fetch.ai RPC server, and the current network height, use:

curl https://rpc-fetchhub.fetch.ai:443/block | jq -r '{\"trusted_hash\": .result.block_id.hash, \"trusted_height\": .result.block.header.height}'\n{\n\"trusted_hash\": \"...some hash...\",\n  \"trusted_height\": \"...some height...\"\n}\n

and set the trusted_hash and trusted_height values in the config file.

Once this is set, make sure you have the correct genesis by downloading it from the RPC node:

curl https://raw.githubusercontent.com/fetchai/genesis-fetchhub/fetchhub-4/fetchhub-4/data/genesis_migrated_5300200.json --output ~/.fetchd/config/genesis.json\n

and start the node using the seeds from the chain-registry:

fetchd start --p2p.seeds=\"17693da418c15c95d629994a320e2c4f51a8069b@connect-fetchhub.fetch.ai:36456,a575c681c2861fe945f77cb3aba0357da294f1f2@connect-fetchhub.fetch.ai:36457,d7cda986c9f59ab9e05058a803c3d0300d15d8da@connect-fetchhub.fetch.ai:36458\"\n

After the node initialized, it will start searching for available snapshots, and it should print log messages similar to:

8:22AM INF Discovered new snapshot format=1 hash=\"\ufffd \u076b/\ufffd\ufffd\\r\ufffdF#C(pD\ufffd<\ufffd\ufffd\\x066\ufffd\ufffd\\x1f\ufffd\ufffd\\x1f<i\ufffd\u075d\" height=2000 module=statesync\n8:22AM INF Discovered new snapshot format=1 hash=\"F\ufffd=\\x05\ufffdGh\ufffd{\ufffd|\ufffd\ufffd\ufffd\ufffd\ufffd,\ufffdQ'\ufffd=]\\x1a\ufffd$\ufffdb\ufffd\u05bfQ\" height=1900 module=statesync\n

The node will select the one with the height value the closest to the tip of the chain, and it will then start restoring the state, and finish syncing the few blocks remaining.

If it fails to verify any blocks or hash when restoring, it will attempt to restore the next available snapshot, and, if no more are available, will fallback in discovery mode until an usable snapshot is available.

"},{"location":"ledger_v2/state-sync/#configure-an-existing-node-to-provide-snapshots","title":"Configure an existing node to provide snapshots","text":"

In order to provide new nodes snapshots they can start from, existing nodes need to be configured to create these snapshots. This requires changes in the ~/.fetchd/config/app.toml file, in the state-sync section.

###############################################################################\n###                        State Sync Configuration                         ###\n###############################################################################\n\n# State sync snapshots allow other nodes to rapidly join the network without replaying historical\n# blocks, instead downloading and applying a snapshot of the application state at a given height.\n[state-sync]\n\n# snapshot-interval specifies the block interval at which local state sync snapshots are\n# taken (0 to disable). Must be a multiple of pruning-keep-every.\nsnapshot-interval = 0\n\n# snapshot-keep-recent specifies the number of recent snapshots to keep and serve (0 to keep all).\nsnapshot-keep-recent = 2\n

Here snapshot-interval must be set to a number of blocks between each snapshot creation and it must be a multiple of your node prunning settings (default is 100, so valid values are 100, 1000, 700...). The number of snapshots to keep can be set with snapshot-keep-recent.

"},{"location":"ledger_v2/versions/","title":"Versions","text":"

There are multiple versions of the fetchd software with differing levels of features and maturity. The following table outlines the rough overview of these versions

Version Maturity Description v0.2.x Deprecated This is a stable version of the network to support agent development v0.3.x Deprecated Builds upon our stable release and adds support for the random beacon consensus module v0.4.x Deprecated Builds upon the random beacon consensus and adds support for aggregated signatures v0.5.x Deprecated Extension of v0.4.x v0.6.x Deprecated Extension of v0.5.x v0.7.x Deprecated Pre stargate fetchhub mainnet version v0.8.x Deprecated Mainline version of the network used for Stargate fetchhub mainnet v0.9.x Deprecated Mainline version of the network used for Carpicorn fetchhub mainnet v0.10.x Stable Mainline version of the network used for Dorado fetchhub mainnet"},{"location":"ledger_v2/versions/#upgrade-history","title":"Upgrade history","text":"

For node operators, the full upgrade history, documentations and procedures are available at: https://github.com/fetchai/genesis-fetchhub

"},{"location":"ledger_v2/validators/overview/","title":"Validators Overview","text":""},{"location":"ledger_v2/validators/overview/#introduction","title":"Introduction","text":"

The Fetch.ai Ledger relies on a set of validators that are responsible for committing new blocks in the blockchain. These validators participate in the consensus protocol by broadcasting votes which contain cryptographic signatures signed by each validator's private key.

Validator candidates can bond their own FET and have FET delegated, or staked, to them by token holders. The validators are determined by who has the most stake delegated to them. The top N validator candidates with the most stake will become the active validators.

Validators and their delegators will earn FET as block provisions and tokens as transaction fees through execution of the consensus protocol. Transaction fees will be paid in FET.

If validators double sign, are frequently offline or do not participate in governance, their staked FET (including FET of users that delegated to them) can be slashed. The penalty depends on the severity of the violation.

"},{"location":"ledger_v2/validators/overview/#hardware","title":"Hardware","text":"

The hardware resources for running a validator node largely depend on the network load. As a recommended configuration we suggest the following requirements

  • 2 x CPU, either Intel or AMD, with the SSE4.1, SSE4.2 and AVX flags (use lscpu to verify)
  • 8 GB RAM
  • 500 GB SSD
  • 100 Mbit/s always-on internet connection
  • Linux OS (Ubuntu 18.04 or 20.04 recommended) / MacOS

Uptime in incredibly important for being a validator. It is expected that validators will have appriopriate redundancies for compute, power, connectivity etc. While the blockchain itself it highly replicated it is also expected that validators will perform local storage backups in order to minimise validator down time.

"},{"location":"ledger_v2/validators/overview/#set-up-a-website","title":"Set Up a Website","text":"

Set up a dedicated validator's website and signal your intention to become a validator on our Discord server. This is important since delegators will want to have information about the entity they are delegating their FET to.

Strictly speaking this is not necessary, however, it is recommended. As a validator on the network you will want to get other community users to delegate stake to your validator. The more combined stake that a validate has then the great share of the block rewards they will take.

"},{"location":"ledger_v2/validators/overview/#seek-legal-advice","title":"Seek Legal Advice","text":"

Seek legal advice if you intend to run a Validator.

"},{"location":"ledger_v2/validators/overview/#community","title":"Community","text":"

We highly recommdend to check out the validator community on the discord channel for more information and to see that latest announcements about becoming a validator.

  • Discord
"},{"location":"ledger_v2/validators/security/","title":"Security","text":""},{"location":"ledger_v2/validators/security/#validator-security","title":"Validator Security","text":"

Each validator candidate is encouraged to run its operations independently, as diverse setups increase the resilience of the network. Validator candidates should commence their setup phase now in order to be on time for launch.

"},{"location":"ledger_v2/validators/security/#key-management-hsm","title":"Key Management - HSM","text":"

It is mission critical that an attacker cannot steal a validator's key. If this is possible, it puts the entire stake delegated to the compromised validator at risk. Hardware security modules are an important strategy for mitigating this risk. HSM modules must support ed25519 signatures.

"},{"location":"ledger_v2/validators/security/#sentry-nodes-ddos-protection","title":"Sentry Nodes (DDOS Protection)","text":"

Validators are responsible for ensuring that the network can sustain denial of service attacks.

One recommended way to mitigate these risks is for validators is to carefully structure their network topology in a so-called sentry node architecture.

Validator nodes should only connect to full-nodes they trust because they operate them themselves or are run by other validators they know socially. This architecture shifts the burden of denial-of-service from the validator's node directly to its sentry nodes, and may require new sentry nodes be spun up or activated to mitigate attacks on existing ones.

Sentry nodes can be quickly spun up or change their IP addresses. Because the links to the sentry nodes are in private IP space, an internet based attacked cannot disturb them directly. This will ensure validator block proposals and votes always make it to the rest of the network.

To setup your sentry node architecture you can follow the instructions below:

Validators nodes should edit their config.toml:

# Comma separated list of nodes to keep persistent connections to\n# Do not add private peers to this list if you don't want them advertised\npersistent_peers =[list of sentry nodes]\n# Set true to enable the peer-exchange reactor\npex = false\n

Sentry Nodes should edit their config.toml:

# Comma separated list of peer IDs to keep private (will not be gossiped to other peers)\n# Example ID: 3e16af0cead27979e1fc3dac57d03df3c7a77acc@3.87.179.235:26656\nprivate_peer_ids = \"node_ids_of_private_peers\"\n
"},{"location":"ledger_v2/validators/setup/","title":"Setting up a Validator Node","text":"

This guide assumes that you have successfuly installed, configured and connected your validator to the desired network.

"},{"location":"ledger_v2/validators/setup/#creating-a-validator","title":"Creating a validator","text":"

To create a validator on the network you will need to send a transaction to the network bonding / staking your FET tokens. This process registers you as a validator and if you are one of the chosen validators you will start to produce blocks.

fetchd tx staking create-validator \\\n--amount=<the amount to bond> \\\n--pubkey=$(fetchd tendermint show-validator) \\\n--moniker=\"choose a moniker\" \\\n--chain-id=<chain_id> \\\n--commission-rate=\"0.10\" \\\n--commission-max-rate=\"0.20\" \\\n--commission-max-change-rate=\"0.01\" \\\n--min-self-delegation=\"<the min self delegation>\" \\\n--gas auto --gas-adjustment 1.5 --gas-prices \"<network gas prices>\" \\\n--from=<key_name>\n

Dorado Example

Before trying to create a validator you should verify that you have some tokens available beforehand. The easiest way to do this is via the CLI.

Here is an sample of a typical command line command that will register the node as running the validator.

fetchd tx staking create-validator \\\n--amount=1000000000000000000atestfet \\\n--pubkey=$(fetchd tendermint show-validator) \\\n--moniker=\"my-test-validator\" \\\n--chain-id=dorado-1 \\\n--commission-rate=\"0.10\" \\\n--commission-max-rate=\"0.20\" \\\n--commission-max-change-rate=\"0.01\" \\\n--min-self-delegation=\"1000000000000000000\" \\\n--gas auto --gas-adjustment 1.5 --gas-prices 1000000000atestfet \\\n--from=test-key\n
"},{"location":"ledger_v2/validators/setup/#editing-a-validator","title":"Editing a validator","text":"

Over time it is possible that validators will want to adjust various settings about their nodes. This can be simple things like the associated website for a validator or more consequential actions like altering the commission rate.

In either case, should a validator choose to make this update they would send an \"edit-validator\" transaction to the network. These can be created in a similar way to the \"create-validator\" transactions as shown below:

fetchd tx staking edit-validator\n  --moniker=\"choose a moniker\" \\\n--website=\"https://fetch.ai\" \\\n--details=\"To infinity and beyond!\" \\\n--chain-id=<chain_id> \\\n--commission-rate=\"0.10\" \\\n--from=<key_name>\n
"},{"location":"ledger_v2/validators/setup/#unbonding-a-validator","title":"Unbonding a validator","text":"

When / if a validator wants to stop being a validator for any reason, they can unbond some or all of their staked tokens. This is done with the following command.

fetchd tx staking unbond \\\n<validator operator address> \\\n<amount to remove> \\\n--from <key name>\n

An example of the command is given in the following example:

fetchd tx staking unbond \\\nfetchvaloper1jqqwdch3jmzlmj4tjfn67s3sqm9elkd3wrpspf \\\n1000000000000000000000atestfet \\\n--gas auto --gas-adjustment 1.5 --gas-prices 1000000000atestfet \\\n--from test-key\n
"},{"location":"native_and_erc20/how_to_convert_fet/","title":"How to convert","text":"

On this page, you can find instructions on how to convert ERC-20 and native FETs to each other.

"},{"location":"native_and_erc20/how_to_convert_fet/#native-to-erc-20-fet","title":"Native to ERC-20 FET","text":"
  1. Ensure you have the Fetch wallet installed and that the native FETs you want to convert are in this wallet.

  2. Ensure you have the Metamask wallet installed and that your Ethereum account is set up on it. This is where the ERC-20 FETs will go to.

  3. Go to the Token Bridge.

  4. Unlock the Metamask wallet if instructed to do so.

  5. Ensure the Native to ERC-20 tab is selected.

  6. Hit Connect Browser Wallet and allow the token bridge to connect to your Fetch wallet.

  7. The fields will be automatically populated with the addresses on the two wallets. Ensure these are correct.

  8. Under Amount, enter the amount of tokens you wish to convert.

  9. Hit Transfer.

"},{"location":"native_and_erc20/how_to_convert_fet/#erc-20-to-native-fet","title":"ERC-20 to Native FET","text":"
  1. Ensure you have the Metamask wallet installed and that the ERC-20 FETs you want to convert are in this wallet.

  2. Ensure you have the Fetch wallet installed and that your Fetch.ai account is set up on it. This is where the native FETs will go to.

  3. Go to the Token Bridge.

  4. Unlock the Metamask wallet if instructed to do so.

  5. Ensure the ERC-20 to Native tab is selected.

  6. The Ethereum address should be automatically filled in with the address of the active account on your Metamask wallet. Ensure that it is correct.

  7. Insert your Fetch wallet address in the Native Address section.

  8. Under Amount, enter the amount of tokens you wish to convert.

  9. Hit Transfer and allow the Token Bridge to connect with your wallet.

"},{"location":"native_and_erc20/native_and_erc20_fet/","title":"Native and ERC-20 FET","text":"

The Fetch.ai token (FET) is a utility token and the key medium of exchange on the Fetch.ai network. FET can be used to pay for services in the Fetch ecosystem and network transaction fees. Users can also choose to stake FET to participate in securing the network via its Proof-of-Stake (PoS) consensus mechanism and earn rewards in return for contributing to validator nodes.

The Fetch.ai team initially developed the FET utility token on an ERC-20 contract on the Ethereum network while finishing the work on the Fetch.ai native main network (main-net). This helped developers across the world get their hands on the FET tokens earlier and kickstart the process of developing innovative solutions within the Fetch.ai ecosystem. FET, as an ERC-20 token on the Ethereum contract has never been Fetch.ai's end game because the Ethereum chain does not offer the degree of scalability needed by the kinds of applications the fetch.ai ecosystem aims for.

With the launch of the Fetch.ai Main-net, the native FET became available. This meant that users operating on the Fetch.ai network no longer needed to hold any token (e.g. ETH or BTC) associated with any other network. It is only the native FET tokens that fuel the Fetch.ai ecosystem and its applications.

ERC-20 FET tokens are still in circulation and currently co-exist with native FET tokens and can be transferred from one to the other easily on the Fetch.ai network. All of the ERC-20 FET tokens will ultimately become native FET tokens, but in the foreseeable future both will live side by side. FET tokens can be purchased from different centralized or decentralized exchanges. However, certain exchanges may buy or sell one type or the other.

"},{"location":"native_and_erc20/reconciliation/","title":"Reconciliation service","text":"

During the stake migration from ETH to the Fetch Main-net on September 14th, 2021, the staked funds were all migrated from the Ethereum staking contract to the Fetch.ai Main-net. However, some users decided to wait for reconciliation to access their funds after the auto-migration was complete.

The reconciliation service has been built to restore access of the migrated funds for these users.

Warning

The reconciliation service is specifically for those users who opted to not use the guides to gain access to their staked funds during the stake migration. If you are unable to access your funds for any other reason, for example you were hacked, then the reconciliation service is not applicable to you.

"},{"location":"native_and_erc20/reconciliation/#instructions","title":"Instructions","text":"

First, make sure the following conditions are satisfied:

  • Your Fetch wallet is set up and you have access to it.

    Info

    The address of the account on your Fetch wallet must have recorded at least one transaction on the network. If you have never made a transaction using this address before, the reconciliation tool and the network have no way of knowing your wallet exists and will not allow you to complete the final submission.

  • You have at least 1 Native FET token in your wallet to pay the transaction fee associated with the reconciliation tool. You can buy FET from centralized or decentralized exchanges.

  • You have the Metamask wallet set up and configured with the address you used in the original staking platform.

If the above conditions are satisfied, head over to the reconciliation service and follow the instructions.

Info

The reconciliation service is activated at certain times so users can register with it. For economic safety, the fund transfers only happen during network upgrades.

If the above conditions are satisfied, head over to the reconciliation service and follow the instructions.

Once your registered transaction is successful and your reconciliation request is deemed valid, the funds will be automatically released to the main-net address on your Fetch wallet.

"},{"location":"soef/simple-oef-usage/","title":"SOEF Connection","text":"

You can use the SOEF in the agent framework by using the SOEF connection as a package in your agent project.

Note

Please consult the relevant guide for details.

"},{"location":"soef/simple-oef/","title":"Simple-OEF: Agent Search and Discovery","text":"

This documentation has been produced for the Simple-OEF version 0.3.4.

"},{"location":"soef/simple-oef/#concepts","title":"Concepts","text":"

The Simple-OEF, or soef, is a search and discovery mechanism for autonomous economic agents. Agents register with the soef and are then able to conduct searches around them to find other agents that may be able to help. It is a relatively simple implementation focussing on functionality, performance and ease-of-use. As it develops, it will evolve into a full-scale decentralised, multi-dimensional digital world.

The work-flow is:

  • Find relevant agents on the soef,
  • Communicate using the Agent Framework's peer-to-peer network,
  • Negotiate and then transact on the ledger in order to exchange value for tokens

When an agent registers with the soef, it is issued with a unique reference which is quoted in all subsequent transactions. This way, the soef knows who its talking to. The soef is transaction based, so it does not need a permanent connection to be maintained in order to work with it. If it does not hear from an agent for a period of time, that agent will be timed out and automatically unregistered. This period of time is typically about one hour, but you can see the soef's configuration at:

https://s-oef.fetch.ai:443\" target=\"_blank\">https://s-oef.fetch.ai:443

Agents identify themselves in a number of ways. These include their address, their given name, their classification and their genus. They can also describe how they \"look\" in other ways, and specify the services that they provide.

In order to register, agents must provide a valid address and a given name. The address can be for the Fetch.ai native ledger, the Fetch.ai Cosmos ledger or the Ethereum ledger. It is this that uniquely identifies them, and addresses cannot be duplicated or shared. The given name can be anything and it is not used for search filtering. Typically, it can be thought of as a debugging aid or a context. Names could be Alice, Bob or Jim, as well as they could be a flight number, train identity or reference code. They appear in find results, but are not used to find by.

"},{"location":"soef/simple-oef/#describing-an-agent","title":"Describing an Agent","text":"

Agents describe themselves in three ways:

  1. Identity: their address and ledger type along with their given name
  2. Personality Pieces: how they look
  3. Service Keys: what they do, sell or want.

We cover all of these in this next section. It's important to understand the difference between personality pieces and service keys, as agents only have one appearance, but they can provide many services. Search results can be filtered by a number of both, and wildcards are permitted where relevant.

"},{"location":"soef/simple-oef/#personality-pieces","title":"Personality Pieces","text":"

Agents can have a number of personality pieces. These describe how an agent appears, where it is, and other properties such as heading, supported protocols and types of transactions. All personality pieces are optional, but the more you set, the easier it is for searchers to narrow you down accurately.

Piece Description genus Coarse type of agent, includes things such as vehicle, building, iot. See the genus table below. classification An agent's classification, typically in the form mobility.railway.train. See note below on classifications. No fixed classifications are specified. Classifications can contain alphanumeric characters, the period, underscore and colon (_.:). architecture Agent's architecture. See the architecture table below. Introduced in version 0.1.20. The vast majority of agents should set this to agentframework. dynamics.moving Boolean, indicates if the agent is moving or not. dynamics.heading Indicates the heading of the agent, in radians, with 0.0 pointing due north. dynamics.altitude Altitude of the agent in metres from MSL dynamics.position Indicates the GPS co-ordinates of the agent as latitude and longitude. action.buyer Boolean, indicates whether the agent wishes to buy information, i.e., is an agent that requires value from another agent. action.seller Boolean, indicates whether the agent sells information, i.e., provides value. Value provided can be zero-cost."},{"location":"soef/simple-oef/#genus-list","title":"Genus List","text":"

A genus is a coarse agent class. It is the roughest description of what an agent is, and an easy way of filtering large groups of agents out of searches. The supported genus list is:

Name Description test Agent is a test agent, and should be generally ignored. vehicle Moving objects such as trains, planes and automobiles avatar An agent that represents a human being service An agent that provides a service iot An agent that represents an Internet of Things device data An agent that represents data furniture Small fixed location items such as signs, mobile masts building Large fixed location item such as house, railway station, school buyer Indicates the agent is a buyer only and does not have value to deliver viewer The agent is a view in the world, acting as a \"camera\" to view content financial Financial agent: service, exchange, autonomous market maker, etc.

The best way to use genus is to pick the best fit choice. If there isn't one for you, then do not specify it. If you feel that a high-level genus is missing, please make the suggestion in our Developer Discord (see here).

"},{"location":"soef/simple-oef/#architectures","title":"Architectures","text":"

An architecture is a clue to other agents to describe how the agent is built. The vast majority of agents will be built using the Fetch Agent Framework, but in some cases, such as light-weight IoT devices or test/debugging, agents are built otherwise. Architecture offers a way of describing or filtering, as agents with a similar architecture are more likely to be able to communicate with each other in a meaningful way.

Architecture Description custom Custom agent architecture agentframework Built using the Fetch Agent Framework"},{"location":"soef/simple-oef/#a-note-on-classifications","title":"A Note on Classifications","text":"

There is currently no fixed set of guidelines as to how classifications are used. It is expected that agent builders will converge on a set of standards, and as those become clearer, they will be documented as \"by convention\" classification uses. Here are some examples of classifications in use:

mobility.railway.station\nmobility.railway.train\nmobility.road.taxi\ninfrastructure.road.sign\n

When filtering by classifications, the * wildcard can be used to, for example, capture all mobility related agents with a wildcard of mobility.*.

"},{"location":"soef/simple-oef/#service-keys","title":"Service Keys","text":"

Agents can have a number of service keys. Service keys are simple key/value pairs that describe the list of services that the agent provides. Whilst personality pieces can be thought of as how an agent looks, service keys are what an agent has or does. Service keys are user defined and as with personality pieces, currently have no convention for formatting. They are at the agent builder's discretion. As this changes, the documentation will be updated. However, for buyer agents, three suggested keys are:

buying_genus\nbuying_architecture\nbuying_classifications\ndata_type\nsi_unit\n

This allows searches to look for potential buyers of classifications, genus or with a compatible architecture.

"},{"location":"soef/simple-oef/#finding-agents","title":"Finding Agents","text":"

The soef is designed for geographic searches where agents are able to find other agents near to them that are able to provide them with the value that they want, or who might wish to have the value they provide. However, it also allows for positionless searches on a single node. Future versions of the soef will support searches across nodes, and dimensional reduction-based fuzzy searches.

Geographic searches are performed using the find_around_me operation. This allows searches that:

  • Are within a certain range in KM
  • Optionally must be positioned within an angle of a heading
  • That have a specified set of personality pieces (with wildcards where applicable)
  • That have a specified set of service keys (with wildcards)
  • Where chain identifiers match

Positionless searches are performed using the find_on_this_node operation. This allows searches that:

  • That have a specified set of personality pieces (with wildcards where applicable)
  • That have a specified set of service keys (with wildcards)
  • Where chain identifiers match

At least one filter must be supplied in positionless searches. Positionless searches are not boundless, they are capped at a specific number. The tighter the filters, the less likely that you will be capped.

Some limits apply to the maximum number of filters, range and returned results. This may vary from soef instance to soef instance. You can see (and parse if required) these by getting the soef status at:

https://s-oef.fetch.ai:443\" target=\"_blank\">https://s-oef.fetch.ai:443

The soef returns XML that includes information about all found agents. An example of that, unparsed, looks like this:

<response>\n<success>1</success>\n<total>1</total>\n<capped>0</capped>\n<results>\n<agent name=\"TrainNumber1234\" genus=\"vehicle\" classification=\"mobility.railway.train\" user_context=\"18:00 to Berlin\">\n<identities>\n<identity chain_identifier=\"fetchai\">2h6fi8oCkMz9GCpL7EUYMHjzgdRFGmDP5V4Ls97jZpzjg523yY</identity>\n</identities>\n<range_in_km>55.7363</range_in_km>\n<location accuracy=\"3\">\n<latitude>52.5</latitude>\n<longitude>0.2</longitude>\n</location>\n</agent>\n</results>\n</response>\n

The <location> block is only returned if the agent has set itself to disclose its position in a find. Likewise, the user_context=\"\" is only returned if enabled. Normally, the default is not to, and agents will then only return the <range_in_km> item. This is because agents may deliver their precise location as part of the value that they deliver, and therefore it would need to be negotiated and potentially paid for. However, sometimes, it is desirable for agents to always deliver their position when found but specify the accuracy. Because of this, the soef supports four levels of accuracy:

Level Accuracy none Default do not disclose position, range only. low Rounded to nearest 11km medium Rounded to nearest 1.1km high Rounded to nearest 110 metres maximum No rounding: supplied in maximum available detail"},{"location":"soef/simple-oef/#technical-details","title":"Technical Details","text":"

For the majority of use cases, the soef will be used from the Agent Framework. As a result, talking to it directly will not be needed. There are some occasions where interacting with the soef directly may be required, and this section documents the API functionality.

Until version 1.0 and main-net version 2 (expected in early 2021), some of the security and paid-for-services are not implemented and where they are, they generally not enforced. Digital signatures for the sign-on process and unique identity recovery will be implemented, as will encryption on sensitive data transport, for example. Thus the API is likely to change substantially in the coming months, particularly the initial registration process. It is not recommended that you invest in substantial code that talks to the soef directly until after 1.0, and it is always preferred to go through the Agent Framework.

"},{"location":"soef/simple-oef/#registration","title":"Registration","text":"

Agents register at the /register page on the soef. They are expected to provide four pieces of information:

  1. An API key
  2. A chain identifier, which can be either fetchai_v1 for the Fetch native network (testnet or mainnet), fetchai_v2_* for the Fetch version 2 network or ethereum for the Ethereum network. See the \"Chain identifiers\" table below for a complete list of supported chain identifiers.
  3. An address, which must be a valid address for the specified chain identifier
  4. A \"given name\" (see \"Concepts\", above), which can be anything from Alice to Bob, or a flight number, or any other user-given context. It must not exceed 128 characters.

If registration is successful, the soef will return a result like this:

<response>\n<encrypted>0</encrypted>\n<token>0A709D1ED170A3E96C4AC9D014BCAE30</token>\n<page_address>\noef_AEC97453A80FFFF5F11E612594585F611D1728FFCD74BBF4FE915BBBB052\n  </page_address>\n</response>\n

This indicates success and that the agent is now in the Lobby. The lobby is a temporary holding pen where newly registered agents wait until the negotiation is complete. If an agent does not respond and complete its registration within 60 seconds, it is removed from the lobby and registration is cancelled.

The <page_address> is the unique URL for the new agent. This must be quoted in all subsequent interactions and is how the soef identifies that specific agent. To complete registration, use the unique URL and specify the parameters:

  • token= with the token that was returned above and
  • command=acknowledge

If this works, you will receive a success response:

<response>\n<success>1</success>\n</response>\n

At this point, your agent is now fully registered and can then communicate with the soef.

Agents that do not contact the soef at least once over a specified interval will be automatically unregistered. The typical setting for this is 60 minutes.

"},{"location":"soef/simple-oef/#chain-identifiers","title":"Chain Identifiers","text":"

The soef supports a selection of chain identifiers designed to allow agents to distinguish networks in searches, but also to identify the type of address used for verification purposes.

Chain identifier Network fetchai_v1 Version 1 Fetch.ai network (testnet or mainnet). Versions prior to 0.2 of the soef used fetchai for this, which is retained for compatibility. fetchai_v2_testnet_stable Version 2 Fetch.ai stable testnet, also known as \"Agentland\". Versions prior to 0.2 of the soef used fetchai_cosmos which is retained for compatibility, but deprecated. fetchai_v2_testnet_incentivised Current incentivised testnet. Fetch.ai are running a high-reward sequence of testnets in Q4 2020 and Q1 2021 leading to V2 mainnet. fetchai_v2_misc Miscellaneous v2 network. These are temporary or transient testnets where there is a desire to separate the chain ID from other v2 networks. fetchai_v2_mainnet Fetch.ai v2 mainnet. Not yet active."},{"location":"soef/simple-oef/#commands","title":"Commands","text":"

The soef has a number of commands that can be used to set or update personality pieces, manage service keys, unregister, find other agents and other operations. These commands are specified using the agent's unique URL and a command= parameter. There may then be other required and optional parameters for that particular command.

Command Details unregister Unregisters the agent from the soef. The unique URL is invalidated and the agent will no longer appear in searches. No parameters. ping Say hello. This is for agents that have been idle for a long period of time and wish to maintain their connection. No parameters. set_personality_piece Sets or updates a personality piece. Specify the piece (see personality piece table above) and the value. For personality pieces with multiple values, such as dynamics.position, separate them with the pipe character |. set_service_key Sets or updates a service key. Specify the key and the value to assign to it. remove_service_key Removes an existing service key. Specify the key. set_find_position_disclosure_accuracy Sets the find disclosure accuracy. See the table in \"Finding Agents\", above, for the accepted values for the parameter accuracy. find_around_me Geographic finding of agents around me. This allows various filters, such as personality pieces and service keys, to be specified. See below, as this is more complex. find_on_this_node Positionless finding of agents on this node. Various filters such as personality pieces and service keys can narrow the search. See below for more information. set_position This is a direct internal mapping to set_personality_piece with a piece of dynamics.position. It existed in the earliest versions of the soef and remains as a short-cut. It expects longitude and latitude as parameters. set_declared_name This allows an agent's declared name to be changed after registration. It takes one parameter, name, to specify the replacement name. Names cannot exceed 128 characters and must not contain illegal characters. set_user_context Sets an optional user-context for an agent to what is specified in the value parameter. This can be optionally disclosed in find_around_me if enabled. See set_disclose_user_context, below. The user context must not contain illegal characters and is limited to 160 maximum. set_disclose_user_context If the disclose parameter is set to true, the optional user context is disclosed if it has been set. Default is false."},{"location":"soef/simple-oef/#find-commands-in-detail","title":"Find Commands in Detail","text":"

find_around_me and find_on_this_node are the big commands. Ultimately, they will cost a small amount of tokens to use, depending on the size of the request, as it involves the most computing time. This provides an incentive for soef operators to maintain soef nodes that correspond to subject areas, geographic areas or both. The command has a number of parameters specifying the filtering required. For find_around_me, the range_in_km is required, whereas narrowing down agents to be within a certain angle of a direction is optional. This cannot exceed a certain range, typically between 50 and 75km. This, and other configuration items, are available on the soef's configuration page. There are other parameters that are optional, although for find_on_this_node at least one ppfilter or skfilter must be specified. The parameters are:

Parameter Use range_in_km Range in kilometres to include agents in results. of_heading Optional: if a pizza-slice type search is required, this is the direction, in degrees, with 0.0 being north. within Optional: if a pizza-slice search, this is the angle in degrees from the of_heading that is allowed. If either of_heading or within are specified, both must be specified. Example: of_heading set to 90.0 and within set to 30 would exclude any agents that are not within 30 degrees of direct east of the me agent. chains_must_match Boolean. Must be true or false. Default is false. If specified, this ensures that any agents returned in the search will have the same chain identifier as you. ppfilter Specify a personality piece filter. Multiple ppfilters can be specified. Example use is: ppfilter=dynamics.moving,true. Wildcards can be used where relevant, e.g.: ppfilter=classification,mobility* will match all classifications that start with mobility, whereas ppfilter=classification,*mobility* will match all classifications with mobility anywhere in it. skfilter Specify a service key filter. Multiple skfilters can be specified. Example use is: skfilter=fruit,peach which will require any returned results to have a service key of fruit and a value of peach. Wildcards can be specified, so skfilter=fruit,pea* will match any agent with a service key of fruit that starts pea, so pear and peach would match."},{"location":"soef/simple-oef/#sk-filters-filter-modes","title":"SK Filters: filter modes","text":"

The skfilter parameter for find_around_me also supports a mode. Four modes are supported:

Mode string Description PS Key must be present, and success is required PF Key must be present, and failure is required OS Only match if present, and success is required OF Only match if present, and failure is required

For example:

command=find_around_me&range_in_km=50&skfilter=type,fruit,PS&skfilter=size,large,OF\n

In this example, the key type must be present, and it must match to fruit. If the size key is present, and it is set to large, then do not match. I.e., return everything that's a fruit within 50km except where the size is large.

"},{"location":"soef/simple-oef/#further-information","title":"Further Information","text":"

You can find further information, or talk to us, in the #agents channel on our official developer Discord server, which you can access here.

We welcome your feedback and strive to deliver the best decentralised search and discovery service for agents that is possible. There are many upcoming features, including the operation incentive mechanisms, additional security and encryption, active searches (where results happen without find_around_me being issued), non-geographic searches across one and many soef nodes and dimensional-reduction based approximate searches.

[Docs: issue 15, 0.3.4, 28-Dec-2020, TWS]

"},{"location":"uAgents/","title":"Introduction","text":"

The \u03bcAgents (micro-Agents) project is a fast and lightweight framework that makes it easy to build agents for all kinds of decentralised use cases.

"},{"location":"uAgents/#why-use-agents","title":"Why use \u03bcAgents?","text":"

Here are a few of the reasons to build with \u03bcAgents. They are:

  • easy to learn: follow our quick start guides to install the Python package and create an agent in just a few minutes.
  • customizable: create any type of agent you can think of and put into code.
  • connected: on startup, each agent automatically joins the fast growing network of \u03bcAgents by registering on the Almanac, a smart contract deployed on the Fetch.ai blockchain.
  • secure: \u03bcAgent messages and wallets are cryptographically secured, so their identities and assets are protected.
  • platform and language independent: though initially launched as a Python library, the exchange protocol is defined in terms of standard data types, and since it is a lightweight framework, expect packages to appear in other languages very soon.
"},{"location":"uAgents/addresses/","title":"Agent addresses","text":"

You can print your agent's addresses in the following way:

from uagents import Agent\nalice = Agent(name=\"alice\")\nprint(\"uAgent address: \", alice.address)\nprint(\"Fetch network address: \", alice.wallet.address())\n

Your agent will have two types of addresses:

  • uAgent address: represents the main \u03bcAgent identifier. Other \u03bcAgents can use this to query the agent's information in the Almanac contract.

  • Fetch address: provides the agent with the capabilities for interacting with the Fetch ledger such as registering in the Almanac contract.

"},{"location":"uAgents/agent-protocols/","title":"Agent protocols","text":"

The \u03bcAgents framework supports capturing related message types and handlers in protocols. Agents who include the same protocol will be able to communicate with each other.

A protocol is built similar to an agent, but it has no identity and cannot be run. It contains only the message types and handlers that define some component of agent functionality.

To show how this works, we will use a simple restaurant table booking request as an example. We first need to define the type of messages that the handler will receive and send. Here we define BookTableRequest which will contain the requested table number and BookTableResponse which will inform the user if that table is available.

from uagents import Context, Model, Protocol\nclass BookTableRequest(Model):\ntable_number: int\nclass BookTableResponse(Model):\nsuccess: bool\n

Now we define the booking protocol as book_proto and we define the desired logic to determine if the BookTableResponse will be successful or not.

book_proto = Protocol()\n@book_proto.on_message(model=BookTableRequest, replies={BookTableResponse})\nasync def handle_book_request(ctx: Context, sender: str, msg: BookTableRequest):\nif ctx.storage.has(str(msg.table_number)):\nsuccess = False\nelse:\nsuccess = True\nctx.storage.set(str(msg.table_number), sender)\n# send the response\nawait ctx.send(sender, BookTableResponse(success=success))\n

We will create a folder named protocols and save this file in it as book.py. We can then import it from the agent script:

from protocols.book import book_proto\n

Then, if your agent is called restaurant you can include the protocol in this way:

restaurant.include(book_proto)\n
"},{"location":"uAgents/almanac-endpoint/","title":"Endpoint weighting","text":"

When an agent registers in the almanac contract, it must specify the service endpoints that they provide along with a weight parameter for each endpoint. Then, when any agent tries to communicate with your agent, the service endpoint will be chosen using a weighted random selection.

You will have two format options when defining your agent's endpoints:

"},{"location":"uAgents/almanac-endpoint/#list-format","title":"List format","text":"

Define your agent's endpoints as a list of strings, the weights will be automatically assigned a value of 1.

agent = Agent(\nname=\"alice\",\nport=8000,\nseed=\"agent secret phrase\",\nendpoint=[\"http://127.0.0.1:8000/submit\",\"http://127.0.0.1:8001/submit\"]\n)\n
"},{"location":"uAgents/almanac-endpoint/#dict-format","title":"Dict format","text":"

Define your agent's endpoints in a Dict format specifying the weight for each endpoint, if the weight parameter is not specified, it will be assigned a value of 1.

agent = Agent(\nname=\"alice\",\nport=8000,\nseed=\"agent recovery seed phrase\",\nendpoint={\n\"http://127.0.0.1:8000/submit\": {\"weight\": 2},\n\"http://127.0.0.1:8001/submit\": {}, # weight value = 1\n},\n)\n
"},{"location":"uAgents/almanac-overview/","title":"Almanac Contract","text":"

Agents of the system will be registered in the Almanac contract. Users can query a particular agent's information directly from the contract to communicate.

A central part of the system is that registrations are strictly time (in blocks) limited. This primarily helps with the liveness problem when dealing with a large ecosystem of agents. In order to keep the registration information up to date, the agent will need to intermittently re-register their information with the Almanac contract.

When an agent\u2019s information times out, queries for that agent will no longer return the registered information.

With each registration, the agent will need to prove ownership of the agent address by signing a sequence number with their \u03bcAgent private key and submitting the signature for verification on the contract. This sequence number should increment with each successful registration and should also be queryable. This will be performed automatically.

"},{"location":"uAgents/almanac-registration/","title":"Registration","text":"

Agent registration in the almanac-contract is a key part for remote agents communication.

To be found by other \u03bcAgents, each \u03bcAgent needs to register (paying a small fee) in the almanac contract using their agent address. Therefore, your agents need to have funds available in their Fetch address. When using the testnet, you can use the function fund_agent_if_low to fund your agent:

from uagents.setup import fund_agent_if_low\nfrom uagents import Agent\nagent = Agent(name=\"alice\", seed=\"agent1 secret phrase\")\nfund_agent_if_low(agent.wallet.address())\n
This function will check if you have enough tokens to register in the almanac-contract. If not it will add tokens to your Fetch address. Make sure to add a seed to your agent so you don't have to fund different addresses each time you run your agent.

\u03bcAgents can communicate by querying the almanac-contract and retrieving an HTTP endpoint from the recipient \u03bcAgent. Therefore, we need to specify the service endpoints when defining an agent:

agent = Agent(\nname=\"alice\",\nport=8000,\nseed=\"agent1 secret phrase\",\nendpoint=[\"http://127.0.0.1:8000/submit\"],\n)\n

Here we defined a local http address, but you could also define a remote address to allow agent communication over different machines through the internet.

"},{"location":"uAgents/booking-demo/","title":"Restaurant Booking Demo","text":"

To showcase how easy it is to create \u03bcAgents for a particular application, here is an example of how to create a custom message type using the Model class.

from uagents import Model\nclass TableStatus(Model):\nseats: int\ntime_start: int\ntime_end: int\n

For this example, we will create a restaurant booking service with two agents: a restaurant with tables available and a user requesting table availability.

"},{"location":"uAgents/booking-demo/#restaurant-setup","title":"Restaurant Setup","text":"

We can create a restaurant agent with its corresponding http endpoint. We will also make sure that the agent is funded so it is able to register in the Almanac contract.

from uagents import Agent\nfrom uagents.setup import fund_agent_if_low\nrestaurant = Agent(\nname=\"restaurant\",\nport=8001,\nseed=\"restaurant secret phrase\",\nendpoint=[\"http://127.0.0.1:8001/submit\"],\n)\nfund_agent_if_low(restaurant.wallet.address())\n
The protocols query_proto and book_proto are built from message handlers in the same way as agents. See query protocol and book protocol for the details and logic behind these protocols, but for now we will simply import them. You will need to add these files inside a protocols folder in the same directory you are running your agent. See agent protocols for more information. Next we build the restaurant agent from these protocols and set the table availability information.

from protocols.book import book_proto\nfrom protocols.query import query_proto, TableStatus\n# build the restaurant agent from stock protocols\nrestaurant.include(query_proto)\nrestaurant.include(book_proto)\nTABLES = {\n1: TableStatus(seats=2, time_start=16, time_end=22),\n2: TableStatus(seats=4, time_start=19, time_end=21),\n3: TableStatus(seats=4, time_start=17, time_end=19),\n}\n
"},{"location":"uAgents/booking-demo/#storage","title":"Storage","text":"

We will now store the TABLES information in the restaurant agent and run it.

# set the table availability information in the restaurant protocols\nfor (number, status) in TABLES.items():\nrestaurant._storage.set(number, status.dict())\nif __name__ == \"__main__\":\nrestaurant.run()\n
The restaurant agent is now online and listing for messages.

"},{"location":"uAgents/booking-demo/#user-setup","title":"User Setup","text":"

We will first import the needed objects and protocols. We will also need the restaurant agent's address to be able to communicate with it.

from protocols.book import BookTableRequest, BookTableResponse\nfrom protocols.query import (\nQueryTableRequest,\nQueryTableResponse,\n)\nfrom uagents import Agent, Context\nfrom uagents.setup import fund_agent_if_low\nRESTAURANT_ADDRESS = \"agent1qw50wcs4nd723ya9j8mwxglnhs2kzzhh0et0yl34vr75hualsyqvqdzl990\"\nuser = Agent(\nname=\"user\",\nport=8000,\nseed=\"user secret phrase\",\nendpoint=[\"http://127.0.0.1:8000/submit\"],\n)\nfund_agent_if_low(user.wallet.address())\n

Now we create the table query to generate the QueryTableRequest using the restaurant address. If the request has not been completed before, we send the request to the restaurant agent.

table_query = QueryTableRequest(\nguests=3,\ntime_start=19,\nduration=2,\n)\n# This on_interval agent function performs a request on a defined period\n@user.on_interval(period=3.0, messages=QueryTableRequest)\nasync def interval(ctx: Context):\ncompleted = ctx.storage.get(\"completed\")\nif not completed:\nawait ctx.send(RESTAURANT_ADDRESS, table_query)\n

The function below activates when a message is received back from the restaurant agent. handle_query_response will evaluate if there is a table available, and if so, respond with a BookTableRequest to complete the reservation.

@user.on_message(QueryTableResponse, replies={BookTableRequest})\nasync def handle_query_response(ctx: Context, sender: str, msg: QueryTableResponse):\nif len(msg.tables) > 0:\nctx.logger.info(\"There is a free table, attempting to book one now\")\ntable_number = msg.tables[0]\nrequest = BookTableRequest(\ntable_number=table_number,\ntime_start=table_query.time_start,\nduration=table_query.duration,\n)\nawait ctx.send(sender, request)\nelse:\nctx.logger.info(\"No free tables - nothing more to do\")\nctx.storage.set(\"completed\", True)\n

Then, handle_book_response will handle messages from the restaurant agent on whether the reservation was successful or unsuccessful.

@user.on_message(BookTableResponse, replies=set())\nasync def handle_book_response(ctx: Context, _sender: str, msg: BookTableResponse):\nif msg.success:\nctx.logger.info(\"Table reservation was successful\")\nelse:\nctx.logger.info(\"Table reservation was UNSUCCESSFUL\")\nctx.storage.set(\"completed\", True)\nif __name__ == \"__main__\":\nuser.run()\n

Finally, run the restaurant agent and then the user agent from different terminals.

Run restaurant agent from one terminal

python restaurant.py\n

Run user agent from a second terminal

python user.py\n

You should see this printed on the user terminal:

INFO:root:Adding funds to agent...complete INFO:root:Registering Agent user... INFO:root:Registering Agent user...complete. Wallet address: fetchnfu3hd87323mw484ma3v3nz2v0q6uhds7d There is a free table, attempting to book one now Table reservation was successful

See the full example scripts at restaurant and user.

"},{"location":"uAgents/installation/","title":"Installation","text":""},{"location":"uAgents/installation/#system-requirements","title":"System requirements","text":"

The system requirements for the Python \u03bcAgents package are as follows, but libraries for more platforms and languages will be released soon.

System requirements

The Python \u03bcAgents pacakge runs on Ubuntu/Debian, MacOS, and Windows.

You need Python 3.8, 3.9 or 3.10 on your system.

"},{"location":"uAgents/installation/#install-from-pypi","title":"Install from PyPI","text":"

We recommend first creating a clean Python virtual environment, for example using poetry or pipenv.

poetrypipenv

Create and enter a new poetry virtual environment:

poetry init -n && poetry shell\n

Create and enter a new pipenv environment:

pipenv --python 3.10 && pipenv shell\n

Now install \u03bcAgents from the PyPI package registry:

pip install uagents\n

Alternatively, install from source code:

Download the latest released version from Github and navigate to the uAgents directory

git clone https://github.com/fetchai/uAgents.git\ncd uAgents\n

Install the required dependencies

poetry install\n

Open the virtual environment

poetry shell\n
"},{"location":"uAgents/interval-tasks/","title":"Interval tasks","text":"

\u03bcAgents can use interval tasks to periodically perform actions with some time interval.

We can use the on_interval decorator to repeat a task in a specified period. We also need to import Context to have access to the information that the agent needs to function. In this case, we will just define a say_hello function that will print out the agent name every 2 seconds.

from uagents import Agent, Context\nalice = Agent(name=\"alice\")\n@alice.on_interval(period=2.0)\nasync def say_hello(ctx: Context):\nctx.logger.info(f'hello, my name is {ctx.name}')\nif __name__ == \"__main__\":\nalice.run()\n

Run your agent

python agent.py\n

You should see the message printed out in the terminal every 2 seconds.

Hello my name is alice. Hello my name is alice. Hello my name is alice. Hello my name is alice.

For another interval task example see agent communication.

"},{"location":"uAgents/protocol/","title":"Exchange protocol","text":""},{"location":"uAgents/protocol/#overview","title":"Overview","text":"

The \u03bcAgents Exchange Protocol defines a simple standard by which the agents communicate.

In this protocol, agents can send messages enclosed in envelopes, which are then encoded and sent via HTTP to the endpoints of other agents.

We break down each of these concepts in more detail below.

"},{"location":"uAgents/protocol/#messages","title":"Messages","text":"

Messages consist of key-value pairs following the standard JSON format.

Here are a few examples:

{\"message\": \"hello\"}\n
{\"name\": \"alice\", \"age\": 26, \"languages\": [\"English\", \"Japanese\", \"Arabic\"]}\n
{\"item\": \"pretzel\", \"bid\": {\"amount\": 120, \"denomination\": \"GBP\"}}\n

Once created, messages are then enclosed in envelopes containing some important metadata.

"},{"location":"uAgents/protocol/#envelopes","title":"Envelopes","text":"

Envelopes have the following form and are quite similar to blockchain transactions:

@dataclass\nclass Envelope:\nsender: str:    # bech32-encoded public address\ntarget: str:    # bech32-encoded public address\nsession: str    # UUID\nprotocol: str   # protocol digest\npayload: bytes  # JSON type: base64 str\nexpires: int    # Unix timestamp in seconds\nsignature: str  # bech32-encoded signature\n
"},{"location":"uAgents/protocol/#semantics","title":"Semantics","text":"

The sender field exposes the address of the sender of the message.

The target field exposes the address of the recipient of the message.

The protocol contains the unique schema digest string for the message.

The payload field exposes the payload of the protocol. Its JSON representation should be a base64 encoded string.

The expires field contains the Unix timestamp in seconds at which the message is no longer valid.

The signature field contains the signature that is used to authenticate that the message has been sent from the sender agent.

Envelopes are then JSON encoded and sent to endpoints of other agents or services.

"},{"location":"uAgents/protocol/#endpoints","title":"Endpoints","text":"

The protocol supports only one standardised endpoint:

HTTP 1.1 POST /submit

and expects data which is broadly JSON compatible. The protocol currently supports MIME content type application/json.

"},{"location":"uAgents/remote-agents/","title":"Remote agents","text":"

\u03bcAgents can also interact remotely from different locations across the internet. All you need to know is the recipient agent's address to query it's information in the Almanac contract. See Addresses for more information about \u03bcAgent addresses.

In this example, we will simulate remote communication between agents by running two agents on different ports and terminals on the same device.

"},{"location":"uAgents/remote-agents/#alice","title":"Alice","text":"

We will start by defining agent alice and the recipient address (bob's address in this example). Then we will include a send function and a handler as we have learned in agent interactions:

from uagents.setup import fund_agent_if_low\nfrom uagents import Agent, Context, Model\nclass Message(Model):\nmessage: str\nRECIPIENT_ADDRESS = \"agent1q2kxet3vh0scsf0sm7y2erzz33cve6tv5uk63x64upw5g68kr0chkv7hw50\"\nalice = Agent(\nname=\"alice\",\nport=8000,\nseed=\"alice secret phrase\",\nendpoint=[\"http://127.0.0.1:8000/submit\"],\n)\nfund_agent_if_low(alice.wallet.address())\n@alice.on_interval(period=2.0)\nasync def send_message(ctx: Context):\nawait ctx.send(RECIPIENT_ADDRESS, Message(message=\"hello there bob\"))\n@alice.on_message(model=Message)\nasync def message_handler(ctx: Context, sender: str, msg: Message):\nctx.logger.info(f\"Received message from {sender}: {msg.message}\")\nif __name__ == \"__main__\":\nalice.run()\n
"},{"location":"uAgents/remote-agents/#bob","title":"Bob","text":"

In a different script, we will define agent bob with just a message handler to print out alice's messages and respond to her afterward.

from uagents.setup import fund_agent_if_low\nfrom uagents import Agent, Context, Model\nclass Message(Model):\nmessage: str\nbob = Agent(\nname=\"bob\",\nport=8001,\nseed=\"bob secret phrase\",\nendpoint=[\"http://127.0.0.1:8001/submit\"],\n)\nfund_agent_if_low(bob.wallet.address())\n@bob.on_message(model=Message)\nasync def message_handler(ctx: Context, sender: str, msg: Message):\nctx.logger.info(f\"Received message from {sender}: {msg.message}\")\n# send the response\nawait ctx.send(sender, Message(message=\"hello there alice\"))\nif __name__ == \"__main__\":\nbob.run()\n

Now, we first run bob and then alice from different terminals. They will register automatically in the Almanac contract using their funds. The received messages will print out in each terminal.

Run Bob and Alice

python bob.py\npython alice.py\n

In bob's terminal:

INFO:root:Adding funds to agent...complete INFO:root:Registering Agent bob... INFO:root:Registering Agent bob...complete. Wallet address: fetch1cr9ghmxrf943dmw484ma3v3nz2v0q6u9pynqdk [bob] From: agent1qdp9j2ev86k3h5acaayjm8tpx36zv4mjxn05pa2kwesspstzj697xy5vk2a hello there bob [bob] From: agent1qdp9j2ev86k3h5acaayjm8tpx36zv4mjxn05pa2kwesspstzj697xy5vk2a hello there bob [bob] From: agent1qdp9j2ev86k3h5acaayjm8tpx36zv4mjxn05pa2kwesspstzj697xy5vk2a hello there bob

In alice's terminal:

INFO:root:Adding funds to agent...complete INFO:root:Registering Agent alice... INFO:root:Registering Agent alice...complete. Wallet address: fetchnfu3hd87323mw484ma3v3nz2v0q6uhds7d [alice] From: agent5kdyfj2ev86k3h5acaa93kdnch8shv4mjxn05pa2kwesspstzj023jdus93j hello there alice [alice] From: agent5kdyfj2ev86k3h5acaa93kdnch8shv4mjxn05pa2kwesspstzj023jdus93j hello there alice [alice] From: agent5kdyfj2ev86k3h5acaa93kdnch8shv4mjxn05pa2kwesspstzj023jdus93j hello there alice

For a more complex example visit restaurant booking demo.

"},{"location":"uAgents/remote-agents/#the-agentverse-explorer","title":"The Agentverse Explorer","text":"

\u03bcAgents can also interact remotely using a mailbox server. For example, you can use The Agentverse Explorer to find other agents and register your own.

To register agents in the Agentverse mailbox, you need to sign in at The Agentverse Explorer. Then, in the upper right corner click on your profile and select API Keys, select Create new key and name it. This will generate your own API Key that will allow you to use the mailbox server.

Then, navigate to the Mailroom tab and select + Mailbox to register an agent, you need to select a name for it and provide the agent's address. Finally, you need to define the \u03bcAgent specifying the mailbox server and the API Key

# First generate a secure seed phrase (e.g. https://pypi.org/project/mnemonic/)\nSEED_PHRASE = \"put_your_seed_phrase_here\"\n# Copy the address shown below\nprint(f\"Your agent's address is: {Agent(seed=SEED_PHRASE).address}\")\n# Then sign up at https://agentverse.ai to get an API key and register your agent\nAPI_KEY = \"put_your_API_key_here\"\n# Now your agent is ready to join the agentverse!\nagent = Agent(\nname=\"alice\",\nseed=SEED_PHRASE,\nmailbox=f\"{API_KEY}@wss://agentverse.ai\",\n)\n

Now, you can recreate the example we showed at the begining of this section by also registering agent bob in The Agentverse Explorer.

"},{"location":"uAgents/run-agent/","title":"Running an agent","text":""},{"location":"uAgents/run-agent/#create-the-agent","title":"Create the agent","text":"

You can create your first \u03bcAgent by building a Python script with the following steps:

from uagents import Agent, Context\nalice = Agent(name=\"alice\", seed=\"alice recovery phrase\")\n

It is optional but useful to include a seed parameter when creating an agent to set fixed addresses. Otherwise, random addresses will be generated every time you run the agent.

"},{"location":"uAgents/run-agent/#give-the-agent-something-to-do","title":"Give the agent something to do","text":"

Let's start with a simple task of saying hello every 2 seconds:

@alice.on_interval(period=2.0)\nasync def say_hello(ctx: Context):\nctx.logger.info(f'hello, my name is {ctx.name}')\n
The Context object is a collection of data and functions related to the agent. In this case, we just use the agent's name.

"},{"location":"uAgents/run-agent/#run-the-agent","title":"Run the agent","text":"

You can now run your first \u03bcAgent!

from uagents import Agent, Context\nalice = Agent(name=\"alice\", seed=\"alice recovery phrase\")\n@alice.on_interval(period=2.0)\nasync def say_hello(ctx: Context):\nctx.logger.info(f'hello, my name is {ctx.name}')\nif __name__ == \"__main__\":\nalice.run()\n

Run your agent

python agent.py\n

After a few lines in the agent's logs, you should see the following text printed on your terminal:

hello, my name is alice. hello, my name is alice. hello, my name is alice. ..."},{"location":"uAgents/simple-interaction/","title":"Agent interactions","text":""},{"location":"uAgents/simple-interaction/#add-a-second-agent","title":"Add a second agent","text":"

To show \u03bcAgents interacting, we'll need to create a second agent. Importing the Bureau class will allow us to create a collection of agents and run them together in the same script. Then we can simply add agents alice and bob to the Bureau and run it.

from uagents import Agent, Context, Bureau\nalice = Agent(name=\"alice\", seed=\"alice recovery phrase\")\nbob = Agent(name=\"bob\", seed=\"bob recovery phrase\")\n@alice.on_interval(period=2.0)\nasync def say_hello(ctx: Context):\nctx.logger.info(f'Hello, my name is {ctx.name}')\n@bob.on_interval(period=2.0)\nasync def say_hello(ctx: Context):\nctx.logger.info(f'Hello, my name is {ctx.name}')\nbureau = Bureau()\nbureau.add(alice)\nbureau.add(bob)\nif __name__ == \"__main__\":\nbureau.run()\n

Yoy should observe alice and bob printing out their name in the terminal.

Run your agents

python simple-agents.py\n

You will see the message printed out every 2 seconds. You might see a message indicating insufficient funds to register, check out agent registration for more information.

Hello, my name is alice Hello, my name is bob Hello, my name is alice Hello, my name is bob"},{"location":"uAgents/simple-interaction/#agent-communication","title":"Agent communication","text":"

To allow our agents to communicate with each other we will need a message structure, and for that, we need to import Model to define a generic message.

from uagents import Model\nclass Message(Model):\ntext: str\n

We can use the send function from the Context class to send a message from alice to bob on an interval.

@alice.on_interval(period=2.0)\nasync def send_message(ctx: Context):\nmsg = f'hello there {bob.name} my name is {alice.name}'\nawait ctx.send(bob.address, Message(text=msg))\n

We also need to introduce a message handler for bob. We will do this inside the on_message decorator that will activate the message_handler once bob receives the message.

@bob.on_message(Message)\nasync def message_handler(ctx: Context, sender: str, msg: Message):\nctx.logger.info(f\"Received message from {sender}: {msg.text}\")\nctx.logger.info(msg)\n

Finally, we need to add both agents to the Bureau in order to run them from the same script.

from uagents import Agent, Context, Bureau, Model\nclass Message(Model):\ntext: str\nalice = Agent(name=\"alice\", seed=\"alice recovery phrase\")\nbob = Agent(name=\"bob\", seed=\"bob recovery phrase\")\n@alice.on_interval(period=2.0)\nasync def send_message(ctx: Context):\nmsg = f'hello there {bob.name} my name is {alice.name}'\nawait ctx.send(bob.address, Message(text=msg))\n@bob.on_message(model=Message)\nasync def message_handler(ctx: Context, sender: str, msg: Message):\nctx.logger.info(f\"Received message from {sender}: {msg.text}\")\nbureau = Bureau()\nbureau.add(alice)\nbureau.add(bob)\nif __name__ == \"__main__\":\nbureau.run()\n

When running the script above, you should see alice's message printed on the terminal:

Run your agents

python agent-communication.py\n
[bob]: message received from agent1qww3ju3h6kfcuqf54gkghvt2pqe8qp97a7nzm2vp8plfxflc0epzcjsv79t: hello there bob my name is alice [bob]: message received from agent1qww3ju3h6kfcuqf54gkghvt2pqe8qp97a7nzm2vp8plfxflc0epzcjsv79t: hello there bob my name is alice

You could also try to add a response from bob to alice, for that you would need to add a send message from bob after alice's message is received and a new message handler for alice to be able to manage and print out bob's message. For a slightly more complex example check out the next section remote agents.

"},{"location":"uAgents/storage/","title":"Storage","text":"

You can store information using the agent's local storage by simply running:

ctx.storage.set(\"key\", \"value\")\n
within a handler, where ctx is the agent's Context object.

This will save the information in a JSON file, you can retreive it a any time using:

 ctx.storage.get(\"key\")\n

See the restaurant booking demo for an example that makes use of the agent's storage to store table information.

"}]} \ No newline at end of file diff --git a/sitemap.xml b/sitemap.xml index 7f93e2177..a016400b5 100644 --- a/sitemap.xml +++ b/sitemap.xml @@ -2,1592 +2,1592 @@ https://docs.fetch.ai/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/bug_bounty/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/decentralisation/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/fund_form/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/CosmPy/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/CosmPy/auto-compounder/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/CosmPy/connect-to-network/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/CosmPy/deploy-a-contract/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/CosmPy/liquidity-pool/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/CosmPy/low-level-api/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/CosmPy/oracles/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/CosmPy/query-balance/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/CosmPy/send-tokens/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/CosmPy/stake-optimizer/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/CosmPy/staking/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/CosmPy/swap-automation/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/CosmPy/wallet-topup/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/CosmPy/wallets-and-keys/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/CosmPy/api/aerial/coins/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/CosmPy/api/aerial/config/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/CosmPy/api/aerial/exceptions/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/CosmPy/api/aerial/faucet/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/CosmPy/api/aerial/gas/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/CosmPy/api/aerial/tx/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/CosmPy/api/aerial/tx_helpers/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/CosmPy/api/aerial/urls/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/CosmPy/api/aerial/wallet/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/CosmPy/api/aerial/client/__init__/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/CosmPy/api/aerial/client/bank/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/CosmPy/api/aerial/client/distribution/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/CosmPy/api/aerial/client/staking/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/CosmPy/api/aerial/client/utils/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/CosmPy/api/aerial/contract/__init__/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/CosmPy/api/aerial/contract/cosmwasm/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/Jenesis/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/Jenesis/add-contracts/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/Jenesis/add-profile/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/Jenesis/compile-contracts/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/Jenesis/deploy-contracts/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/Jenesis/keys/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/Jenesis/use-contracts/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/12-factor/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/acn-internals/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/acn/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/aea-vs-mvc/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/aeas/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/agent-oriented-development/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/agent-vs-aea/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/aggregation-demo/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/application/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/aries-cloud-agent-demo/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/build-aea-programmatically/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/build-aea-step-by-step/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/car-park-skills/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/cli-commands/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/cli-vs-programmatic-aeas/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/config/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/connect-a-frontend/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/connection/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/contract/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/core-components-1/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/core-components-2/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/core-components/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/debug/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/decision-maker-transaction/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/decision-maker/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/defining-data-models/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/demos/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/deployment/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/design-principles/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/development-setup/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/diagram/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/ecosystem/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/erc1155-skills/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/generic-skills-step-by-step/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/generic-skills/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/generic-storage/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/glossary/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/gym-example/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/gym-skill/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/http-connection-and-skill/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/identity/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/install/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/interaction-protocol/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/known-limits/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/language-agnostic-definition/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/ledger-integration/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/limits/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/logging/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/message-routing/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/ml-skills/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/modes/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/multi-agent-manager/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/multiplexer-standalone/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/oracle-demo/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/orm-integration/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/p2p-connection/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/package-imports/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/performance-benchmark/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/por/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/prometheus/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/protocol-generator/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/protocol/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/query-language/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/questions-and-answers/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/quickstart/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/raspberry-set-up/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/runtime-cost/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/scaffolding/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/security/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/setup/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/simple-oef-usage/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/simple-oef/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/skill-guide/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/skill-testing/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/skill/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/standalone-transaction/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/step-one/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/tac-skills-contract/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/tac-skills/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/tac/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/thermometer-skills/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/trust/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/upgrading/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/wealth/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/weather-skills/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/abstract_agent/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/aea/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/aea_builder/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/agent/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/agent_loop/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/common/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/exceptions/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/launcher/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/multiplexer/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/runner/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/runtime/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/components/base/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/components/loader/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/components/utils/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/configurations/base/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/configurations/constants/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/configurations/data_types/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/configurations/loader/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/configurations/manager/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/configurations/pypi/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/configurations/utils/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/configurations/validation/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/connections/base/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/context/base/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/contracts/base/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/crypto/base/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/crypto/helpers/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/crypto/ledger_apis/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/crypto/plugin/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/crypto/wallet/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/crypto/registries/base/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/decision_maker/base/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/decision_maker/default/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/decision_maker/gop/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/error_handler/base/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/error_handler/default/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/helpers/async_friendly_queue/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/helpers/async_utils/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/helpers/base/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/helpers/constants/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/helpers/env_vars/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/helpers/exception_policy/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/helpers/exec_timeout/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/helpers/file_io/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/helpers/file_lock/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/helpers/http_requests/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/helpers/install_dependency/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/helpers/io/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/helpers/logging/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/helpers/multiple_executor/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/helpers/pipe/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/helpers/profiling/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/helpers/serializers/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/helpers/sym_link/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/helpers/win32/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/helpers/yaml_utils/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/helpers/acn/agent_record/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/helpers/acn/uri/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/helpers/ipfs/base/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/helpers/ipfs/utils/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/helpers/multiaddr/base/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/helpers/preference_representations/base/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/helpers/search/generic/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/helpers/search/models/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/helpers/storage/generic_storage/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/helpers/storage/backends/base/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/helpers/storage/backends/sqlite/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/helpers/transaction/base/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/identity/base/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/mail/base/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/manager/manager/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/manager/project/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/manager/utils/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/plugins/aea_cli_ipfs/core/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/plugins/aea_cli_ipfs/ipfs_utils/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/plugins/aea_ledger_cosmos/cosmos/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/plugins/aea_ledger_ethereum/ethereum/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/plugins/aea_ledger_fetchai/_cosmos/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/plugins/aea_ledger_fetchai/fetchai/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/protocols/base/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/protocols/default/custom_types/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/protocols/default/dialogues/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/protocols/default/message/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/protocols/default/serialization/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/protocols/dialogue/base/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/protocols/generator/base/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/protocols/generator/common/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/protocols/generator/extract_specification/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/protocols/generator/validate/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/protocols/signing/custom_types/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/protocols/signing/dialogues/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/protocols/signing/message/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/protocols/signing/serialization/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/protocols/state_update/dialogues/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/protocols/state_update/message/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/protocols/state_update/serialization/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/registries/base/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/registries/filter/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/registries/resources/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/skills/base/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/skills/behaviours/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/skills/tasks/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/test_tools/constants/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/test_tools/exceptions/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/test_tools/generic/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/test_tools/test_cases/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/test_tools/test_contract/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/aea-framework-documentation/api/test_tools/test_skill/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/archives/redelegate-for-decentralization/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/basics/staking/different_ways_to_stake_fet/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/basics/staking/how_to_stake/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/basics/staking/redelegation/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/basics/staking/what_is_staking/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/basics/wallet/getting_started/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/basics/wallet/how_to_use_wallet/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/colearn/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/colearn/about/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/colearn/demo/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/colearn/dev_notes/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/colearn/differential_privacy/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/colearn/examples/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/colearn/grpc_examples/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/colearn/grpc_tutorial/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/colearn/installation/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/colearn/intro_tutorial_keras/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/colearn/intro_tutorial_mli/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/colearn/intro_tutorial_pytorch/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/colearn/mli_factory/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/colearn/tasks/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/create-react-app/example_dapp/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/create-react-app/introduction/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/fetch-wallet/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/fetch-wallet/account_management/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/fetch-wallet/address_book/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/fetch-wallet/connections/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/fetch-wallet/deposit/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/fetch-wallet/migrate_erc20/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/fetch-wallet/send_tokens/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/fetch-wallet/stake/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/learn_the_concepts/agent-based_and_multi-agents_systems/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/learn_the_concepts/glossary/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/learn_the_concepts/peer_to_peer_systems/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/learn_the_concepts/blockchains/consensus/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/learn_the_concepts/blockchains/decentralized_applications/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/learn_the_concepts/blockchains/oracles/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/learn_the_concepts/blockchains/smart_contracts/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/learn_the_concepts/blockchains/transaction_fees/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/learn_the_concepts/blockchains/validators/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/learn_the_concepts/blockchains/what_is_a_blockchain/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/ledger-subquery/introduction/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/ledger_v2/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/ledger_v2/archived-networks/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/ledger_v2/block-explorer/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/ledger_v2/building/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/ledger_v2/cli-bls/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/ledger_v2/cli-governance/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/ledger_v2/cli-introduction/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/ledger_v2/cli-keys/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/ledger_v2/cli-multisig/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/ledger_v2/cli-tokens/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/ledger_v2/delegator-guide-cli/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/ledger_v2/faucet/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/ledger_v2/governance/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/ledger_v2/joining-a-testnet/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/ledger_v2/live-networks/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/ledger_v2/single-node-network/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/ledger_v2/snapshots/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/ledger_v2/state-sync/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/ledger_v2/versions/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/ledger_v2/validators/overview/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/ledger_v2/validators/security/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/ledger_v2/validators/setup/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/native_and_erc20/how_to_convert_fet/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/native_and_erc20/native_and_erc20_fet/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/native_and_erc20/reconciliation/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/soef/simple-oef-usage/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/soef/simple-oef/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/uAgents/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/uAgents/addresses/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/uAgents/agent-protocols/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/uAgents/almanac-endpoint/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/uAgents/almanac-overview/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/uAgents/almanac-registration/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/uAgents/booking-demo/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/uAgents/installation/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/uAgents/interval-tasks/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/uAgents/protocol/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/uAgents/remote-agents/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/uAgents/run-agent/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/uAgents/simple-interaction/ - 2023-05-25 + 2023-09-06 daily https://docs.fetch.ai/uAgents/storage/ - 2023-05-25 + 2023-09-06 daily \ No newline at end of file diff --git a/sitemap.xml.gz b/sitemap.xml.gz index 5066a8ce4..e2741fcbc 100644 Binary files a/sitemap.xml.gz and b/sitemap.xml.gz differ diff --git a/soef/simple-oef-usage/index.html b/soef/simple-oef-usage/index.html index 2a1a3a360..2a1965339 100644 --- a/soef/simple-oef-usage/index.html +++ b/soef/simple-oef-usage/index.html @@ -19,7 +19,7 @@ - + @@ -100,12 +100,16 @@ -
- - - Apply for the $150M Developer Fund - + + diff --git a/soef/simple-oef/index.html b/soef/simple-oef/index.html index 9fa8f56c8..fe0b04458 100644 --- a/soef/simple-oef/index.html +++ b/soef/simple-oef/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - + + diff --git a/uAgents/addresses/index.html b/uAgents/addresses/index.html index 6226c9bb0..86c83f481 100644 --- a/uAgents/addresses/index.html +++ b/uAgents/addresses/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - + + diff --git a/uAgents/agent-protocols/index.html b/uAgents/agent-protocols/index.html index 72310ce66..7b5296347 100644 --- a/uAgents/agent-protocols/index.html +++ b/uAgents/agent-protocols/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - + + diff --git a/uAgents/almanac-endpoint/index.html b/uAgents/almanac-endpoint/index.html index 1fcbbe18d..189234193 100644 --- a/uAgents/almanac-endpoint/index.html +++ b/uAgents/almanac-endpoint/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - + + diff --git a/uAgents/almanac-overview/index.html b/uAgents/almanac-overview/index.html index aac7b0895..0028e20f5 100644 --- a/uAgents/almanac-overview/index.html +++ b/uAgents/almanac-overview/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - + + diff --git a/uAgents/almanac-registration/index.html b/uAgents/almanac-registration/index.html index 1dfe18a4d..dd65182bd 100644 --- a/uAgents/almanac-registration/index.html +++ b/uAgents/almanac-registration/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - + + diff --git a/uAgents/booking-demo/index.html b/uAgents/booking-demo/index.html index c1ef2c3ee..d67d1f8e4 100644 --- a/uAgents/booking-demo/index.html +++ b/uAgents/booking-demo/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - + + diff --git a/uAgents/index.html b/uAgents/index.html index 9de643d31..787fcac78 100644 --- a/uAgents/index.html +++ b/uAgents/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - + + diff --git a/uAgents/installation/index.html b/uAgents/installation/index.html index 25b74762b..8cbe848b4 100644 --- a/uAgents/installation/index.html +++ b/uAgents/installation/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - + + diff --git a/uAgents/interval-tasks/index.html b/uAgents/interval-tasks/index.html index 59f24e0d3..8e2392ee0 100644 --- a/uAgents/interval-tasks/index.html +++ b/uAgents/interval-tasks/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - + + diff --git a/uAgents/protocol/index.html b/uAgents/protocol/index.html index e9e368af1..29d2d2059 100644 --- a/uAgents/protocol/index.html +++ b/uAgents/protocol/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - + + diff --git a/uAgents/remote-agents/index.html b/uAgents/remote-agents/index.html index e94b795a5..90c4a3e29 100644 --- a/uAgents/remote-agents/index.html +++ b/uAgents/remote-agents/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - + + diff --git a/uAgents/run-agent/index.html b/uAgents/run-agent/index.html index 6c900a0ca..6a4c0e9f6 100644 --- a/uAgents/run-agent/index.html +++ b/uAgents/run-agent/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - + + diff --git a/uAgents/simple-interaction/index.html b/uAgents/simple-interaction/index.html index a69bda0e6..e327101cc 100644 --- a/uAgents/simple-interaction/index.html +++ b/uAgents/simple-interaction/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - + + diff --git a/uAgents/storage/index.html b/uAgents/storage/index.html index e1bf37cbf..96c388afb 100644 --- a/uAgents/storage/index.html +++ b/uAgents/storage/index.html @@ -21,7 +21,7 @@ - + @@ -107,12 +107,16 @@ - - - - Apply for the $150M Developer Fund - + +