Lançando feeds de preços com Phala¶
Introdução¶
Phala Network é uma rede de computação off-chain apoiada por Secure Enclaves que permite a desenvolvedores criar contratos inteligentes potentes conectados a componentes off-chain, chamados Phat Contracts. Os Phat Contracts foram projetados para oferecer funcionalidades que superam as limitações de contratos tradicionais (armazenamento, custo, computação), mantendo-se trustless, verificáveis e sem permissão. Para saber mais sobre a arquitetura da Phala, consulte a documentação.
A Phala não é, por si só, uma rede de oráculos; ela habilita várias capacidades de computação off-chain, incluindo uma rede descentralizada de oráculos. A Phala também oferece o Phala Bricks, um conjunto de ferramentas que facilita lançar rapidamente esse tipo de funcionalidade sem precisar construir tudo do zero.
Este tutorial mostra um demo de como interagir com feeds de preço habilitados por Phat Contracts na rede EVM de demonstração do Tanssi. Em seguida, você verá como implantar feeds de preço na sua rede EVM com tecnologia Tanssi. Para produção, é altamente recomendável contatar a equipe Phala para auxiliar no lançamento e garantir a integridade do processo.
Se você já usa outro provedor de oráculos, a Phala serve como camada de execução confidencial para trazer esses dados para sua rede Tanssi. É possível adaptar o fluxo descrito aqui para outros feeds ou APIs, mantendo a mesma interface de consumo no contrato EVM.
Além disso, por usar enclaves seguros, a Phala reduz a superfície de ataque ao processar dados sensíveis ou agregados de múltiplas fontes, reforçando a confiança no resultado final consumido pelos dApps.
Como a Phala habilita feeds de preço¶
A Phala espelha os Chainlink Price Feeds do Ethereum Mainnet. Esses feeds são amplamente adotados e sua coleta/agragação é feita por vários operadores de nó independentes, evitando dependência de uma única fonte de verdade e reduzindo risco de manipulação.
O componente central do desenho do sistema é o Secure Enclave, que processa as mensagens recebidas da blockchain Phala (fila de mensagens criptografada) e garante execução fiel mesmo com trabalhadores maliciosos. A blockchain Phala solicita a atualização do feed; os workers off-chain buscam os preços no Ethereum Mainnet e devolvem para a blockchain Phala.
Além de replicar oráculos existentes, é possível criar novos oráculos buscando dados off-chain via Phat Contracts. No exemplo de Phat-EVM Oracle, os preços vêm da API do CoinGecko e podem ser enviados continuamente (push) ou solicitados pelo contrato EVM (pull).
Em resumo: a Phala funciona como uma ponte segura entre dados externos e sua rede EVM, permitindo reutilizar feeds consolidados da Chainlink ou construir integrações sob medida usando Phat Contracts.
Buscar dados de preço¶
Há vários feeds disponíveis na rede EVM demo. Os feeds habilitados por Phat Contracts usam a mesma interface dos feeds Chainlink. Cada feed fica em um contrato e pode ser consultado pela interface agregadora:
Você também pode reutilizar a mesma interface para feeds personalizados que a sua equipe decidir publicar, mantendo uma API consistente para contratos e frontends.
AggregatorV3Interface.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface AggregatorV3Interface {
/**
* Returns the decimals to offset on the getLatestPrice call
*/
function decimals() external view returns (uint8);
/**
* Returns the description of the underlying price feed aggregator
*/
function description() external view returns (string memory);
/**
* Returns the version number representing the type of aggregator the proxy points to
*/
function version() external view returns (uint256);
/**
* Returns price data about a specific round
*/
function getRoundData(uint80 _roundId) external view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);
/**
* Returns price data from the latest round
*/
function latestRoundData() external view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);
}
A interface expõe cinco funções: decimals, description, version, getRoundData e latestRoundData. Para mais detalhes, veja a referência da Chainlink.
Essas funções permitem consultar metadados do feed, verificar o par de ativos, e obter o preço mais recente ou de uma rodada específica.
Ativos suportados¶
A Phala obtém os feeds espelhando os feeds Chainlink do Ethereum Mainnet. Há contratos para a rede EVM demo e para o Ethereum Mainnet:
| Par de Ativos | Contrato agregador |
|---|---|
| AAVE/USD | 0x2E1640853bB2dD9f47831582665477865F9240DB |
| BTC/USD | 0x89BC5048d634859aef743fF2152363c0e83a6a49 |
| CRV/USD | 0xf38b25b79A72393Fca2Af88cf948D98c64726273 |
| DAI/USD | 0x1f56d8c7D72CE2210Ef340E00119CDac2b05449B |
| ETH/USD | 0x739d71fC66397a28B3A3b7d40eeB865CA05f0185 |
| USDC/USD | 0x4b8331Ce5Ae6cd33bE669c10Ded9AeBA774Bf252 |
| USDT/USD | 0x5018c16707500D2C89a0446C08f347A024f55AE3 |
| Par de Ativos | Contrato agregador |
|---|---|
| AAVE/USD | 0x547a514d5e3769680Ce22B2361c10Ea13619e8a9 |
| BTC/USD | 0xF4030086522a5bEEa4988F8cA5B36dbC97BeE88c |
| CRV/USD | 0xCd627aA160A6fA45Eb793D19Ef54f5062F20f33f |
| DAI/USD | 0xAed0c38402a5d19df6E4c03F4E2DceD6e29c1ee9 |
| ETH/USD | 0x5f4ec3df9cbd43714fe2740f5e3616155c5b8419 |
| USDC/USD | 0x8fFfFfd4AfB6115b954Bd326cbe7B4BA576818f6 |
| USDT/USD | 0x3E7d1eAB13ad0104d2750B8863b489D65364e32D |
Interagir com feeds na rede EVM demo¶
- Conecte a MetaMask à rede EVM demo (veja o guia Implantar contratos com Remix) e certifique-se de que a MetaMask está nessa rede.
- Cole o contrato Aggregator em um novo arquivo no Remix e compile.
- Vá para Deploy and Run Transactions → ENVIRONMENT = Injected Provider -- MetaMask.
- Selecione AggregatorV3Interface em CONTRACT.
- No campo At Address, insira o endereço do feed desejado (ex.: BTC/USD
0x89BC5048d634859aef743fF2152363c0e83a6a49) e clique em At Address.
Para consultar:
- Expanda AggregatorV3Interface.
- Clique em decimals para ver quantas casas decimais o feed usa.
- Clique em description para verificar o par.
- Clique em latestRoundData para ver o preço mais recente (int256 answer).
Para obter um preço legível, ajuste pelo valor de decimals(). Ex.: se o retorno for 5230364122303 e decimals=8, o preço é 52.303,64.
Se preferir outro feed (DAI, ETH etc.), basta repetir os passos usando o endereço correspondente da tabela de ativos suportados.
Caso esteja depurando valores inesperados, valide se o decimals() retornado confere com o esperado para aquele par. Divergências de escala são a causa mais comum de leituras “estranhas” no front-end.
Lançando feeds de preço em uma rede EVM¶
É fácil lançar feeds em uma rede EVM do Tanssi! As etapas a seguir funcionam para redes Trial e dedicadas em Dancelight. Este guia é demonstrativo; para produção, contate a equipe Phala.
Setup¶
Clone o repositório Phala Mirrored Price Feed e instale dependências:
cd mirrored-price-feed/ && yarn install
Crie o .env a partir do exemplo:
cp env.example .env
Edite o .env e insira a chave privada de uma conta financiada na sua rede e o RPC da sua rede. Se estiver na sua própria rede, financie uma conta de teste via Sudo (dados no Tanssi dApp). Os demais campos podem ficar em branco.
PRIVATE_KEY=INSERT_PRIVATE_KEY
RPC_URL=INSERT_YOUR_NETWORK_RPC_URL
VERIFIER_URL=
VERIFY_ADDRESS=
Nota
Nunca compartilhe frase semente ou chave privada. Este guia é apenas educacional.
Configurar script de implantação¶
Edite scripts/OffchainAggregator.s.sol. Ele recebe decimals (mantenha 8) e a descrição do feed (ex.: BTC / USD). Use exatamente as descrições suportadas listadas em Ativos suportados, ou o feed não funcionará.
OffchainAggregator.s.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
import {Script, console2} from "forge-std/Script.sol";
import {OffchainAggregator} from "../src/OffchainAggregator.sol";
contract OffchainAggregatorScript is Script {
function setUp() public {}
function run() public {
vm.startBroadcast();
OffchainAggregator aggregator = new OffchainAggregator(
8,
'BTC / USD'
);
console2.log(address(aggregator));
vm.stopBroadcast();
}
}
Em feeder.ts, insira os detalhes da sua cadeia (RPC, chainId). O array mainnetFeedContracts (endereços do Mainnet) permanece. Limpe aggregatorContracts por enquanto — mais adiante você adicionará os endereços implantados na sua rede.
feeder.ts
import {
createPublicClient,
http,
parseAbi,
createWalletClient,
defineChain,
} from 'viem';
import { mainnet } from 'viem/chains';
import { privateKeyToAccount } from 'viem/accounts';
import * as dotenv from 'dotenv';
dotenv.config();
const mainnetFeedContracts = {
'AAVE-USD': '0x547a514d5e3769680Ce22B2361c10Ea13619e8a9',
'CRV-USD': '0xCd627aA160A6fA45Eb793D19Ef54f5062F20f33f',
'ETH-USD': '0x5f4ec3df9cbd43714fe2740f5e3616155c5b8419',
'BTC-USD': '0xF4030086522a5bEEa4988F8cA5B36dbC97BeE88c',
'DAI-USD': '0xAed0c38402a5d19df6E4c03F4E2DceD6e29c1ee9',
'USDT-USD': '0x3E7d1eAB13ad0104d2750B8863b489D65364e32D',
'USDC-USD': '0x8fFfFfd4AfB6115b954Bd326cbe7B4BA576818f6',
};
const aggregatorContracts = {};
const abi = parseAbi([
'function latestRoundData() external view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound)',
'function transmit(uint80 _roundId, int192 _answer, uint64 _timestamp) external',
'function getRoundData(uint80 _roundId) public view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound)',
]);
// Insert your network details here
const chain = defineChain({
id: INSERT_EVM_CHAIN_ID,
name: 'dancelight-evm-network',
rpcUrls: {
default: {
http: ['INSERT_RPC_URL'],
},
public: {
http: ['INSERT_RPC_URL'],
},
},
});
const publicClient = createPublicClient({
chain: mainnet,
transport: http(),
});
const targetChainPublicClient = createPublicClient({
chain,
transport: http(),
});
async function getLatestRoundData(pair: string) {
const address = mainnetFeedContracts[pair];
if (!address) {
throw new Error(`${pair} mainnet feed contract did not exist.`);
}
const data = await publicClient.readContract({
address,
abi,
functionName: 'latestRoundData',
});
return data;
}
async function getRoundDataFromAggregator(pair: string, roundId: number) {
const address = aggregatorContracts[pair];
if (!address) {
throw new Error(`${pair} aggregator contract did not exist.`);
}
const publicClient = createPublicClient({
chain,
transport: http(),
});
try {
const data = await publicClient.readContract({
address,
abi,
functionName: 'getRoundData',
args: [roundId],
});
return data;
} catch {}
}
async function updateFeed(
walletClient: ReturnType<createWalletClient>,
pair: string
) {
if (!aggregatorContracts[pair]) {
throw new Error(`${pair} aggregator contract did not exist.`);
}
const [roundId, answer, startedAt, updatedAt, answeredInRound] =
await getLatestRoundData(pair);
const aggregatorRoundId = Number(roundId & BigInt('0xFFFFFFFFFFFFFFFF'));
const data = await getRoundDataFromAggregator(pair, aggregatorRoundId);
if (data[1] === answer) {
console.info(
`${pair} aggregatorRoundId ${aggregatorRoundId} data exists: ${data}`
);
return;
}
const hash = await walletClient.writeContract({
address: aggregatorContracts[pair],
abi,
functionName: 'transmit',
args: [roundId, answer, startedAt],
});
await targetChainPublicClient.waitForTransactionReceipt({ hash });
console.info(`${pair} updated, transmit tx hash: ${hash}`);
}
async function main() {
if (!process.env.PRIVATE_KEY) {
throw new Error('missing process.env.PRIVATE_KEY');
}
const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`);
const walletClient = createWalletClient({
chain,
transport: http(),
account,
});
for (const pair in aggregatorContracts) {
await updateFeed(walletClient, pair);
}
}
main()
.then(() => process.exit(0))
.catch((err) => {
console.error(err);
process.exit(1);
});
Build e testes¶
yarn build
yarn test
Saída esperada:
Deploy¶
Para implantar o contrato agregador do par escolhido:
yarn deploy
Anote o endereço retornado.
Acessar o contrato agregador¶
No Remix, com a MetaMask na sua rede EVM, cole o endereço implantado em At Address. Expanda AggregatorV3Interface e clique em latestRoundData — inicialmente deve retornar 0 (sem preço atualizado ainda).
Se ainda não tiver sua rede configurada na MetaMask, use o botão Add to MetaMask no dashboard do Tanssi dApp para adicioná-la rapidamente.
Disparar atualização de preço¶
Inclua o endereço do agregador em aggregatorContracts no feeder.ts:
const aggregatorContracts = {
'BTC-USD': 'INSIRA_ENDERECO_DO_AGREGADOR',
}
Depois execute:
npx tsx feeder.ts
No Remix, chame latestRoundData novamente para ver o preço atualizado.
Para mais informações sobre uso da Phala para dados off-chain, veja os docs da Phala.
Essa abordagem completa (contrato + script + atualização via feeder) garante que os feeds fiquem sincronizados com as fontes da Chainlink no Mainnet e que sua rede Tanssi receba preços confiáveis.
| Criada: 9 de dezembro de 2025





