Understanding the SimpleMarket Smart Contract
SimpleMarket is a minimal smart contract written in Solidity. This contract lets an operator create a market by posting a question and specifying the prediction market window, using start and close times.
Once the market is open, operators and participants can stake USDC on their market prediction.
When the market is closed, an operator can request the settlement of a contract. The contract then emits a SettlementRequested event.
// contracts/src/SimpleMarket.sol
.
.
.
event SettlementRequested(
uint256 indexed marketId,
string question
);
.
.
.
CRE Workflow with Gemini API
The CRE workflow (written in TypeScript) is configured to listen to onchain log events as the trigger. The workflow reads the event’s details, which include metadata on the prediction market being settled.
The CRE workflow’s HTTP Capability sends that data to the Gemini API, along with the system prompt, and specifically requests that Gemini’s responses be grounded with Google search.
// cre-workflow/prediction-market-demo/gemini.tsconst systemPrompt: string =
`You are a fact-checking and event resolution system that determines the real-world outcome of prediction markets.
...
// end of prompt
`;
const userPrompt: string = `Determine the outcome of this market based on factual information...
....// Refer to gemini.ts for the full prompts.
.....// end of prompt
`
// Helper that returns a function that CRE's HTTP capability calls to build
// and send request to Gemini AI
const PostGeminiData =
(logDetails: LogDetails, geminiApiKey: string) =>
(sendRequester: HTTPSendRequester, config: Config): GeminiResponse => {
// Compose the structured instruction + content for deterministic JSON output
const dataToSend: GeminiData = {
system_instruction: { parts: [{ text: systemPrompt }] },
tools: [
{
google_search: {},
},
],
contents: [
{
parts: [
{
text: userPrompt + logDetails.question,
},
],
},
]
};
// encode to base64 as some APIs expect this
const bodyBytes = new TextEncoder().encode(JSON.stringify(dataToSend));
const body = Buffer.from(bodyBytes).toString("base64");
// build request object.
const req = {
url: `https://generativelanguage.googleapis.com/v1beta/models/${config.geminiModel}:generateContent`,
method: "POST" as const,
body,
headers: {
"Content-Type": "application/json",
"x-goog-api-key": geminiApiKey,
},
cacheSettings: {
readFromCache: true,
maxAgeMs: 60_000,
},
};
// Perform the request within CRE infra; result() yields the response
const resp = sendRequester.sendRequest(req).result();
const bodyText = new TextDecoder().decode(resp.body);
if (!ok(resp)) throw new Error(`HTTP request failed with status: ${resp.statusCode}. Error :${bodyText}`);
// Parse and extract the model's response
const externalResp = JSON.parse(bodyText) as GeminiApiResponse;
const text = externalResp?.candidates?.[0]?.content?.parts?.[0]?.text;
if (!text) throw new Error("Malformed LLM response: missing candidates[0].content.parts[0].text");
return {
statusCode: resp.statusCode,
geminiResponse: text,
responseId: externalResp.responseId,
rawJsonString: bodyText,
};
};
Once Gemini’s response is received, the CRE workflow generates a signed report that contains the result (the resolved real world outcome, confidence score). CRE then uses the EVM write capability to post that result onchain by calling the onReport() method in the SimpleMarket contract.
// contracts/src/SimpleMarket.sol
.
.
.
event SettlementResponse(
uint256 indexed marketId,
Status indexed status,
Outcome indexed outcome
);enum Outcome { None, No, Yes, Inconclusive }
// Overridden method, called by CRE.
function onReport(bytes calldata, bytes calldata report) external override {
_processReport(report); // calls settleMarket()
}
/// @notice Helper function invoked by _processReport.
function settleMarket(
uint256 marketId,
Outcome outcome,
uint16 confidenceBps,
string memory evidenceURI
) private {
Market storage m = markets[marketId];
if (m.status != Status.SettlementRequested) revert SettlementNotRequested(m.status);
m.outcome = outcome;
m.settledAt = block.timestamp;
m.confidenceBps = confidenceBps;
m.evidenceURI = evidenceURI;
if (outcome == Outcome.Inconclusive) {
m.status = Status.NeedsManual;
} else {
m.status = Status.Settled;
}
emit SettlementResponse(marketId, m.status, m.outcome);
}
.
.
.
Then the SimpleMarket contract updates the Status of the market as either Settled, or as NeedsManual if the AI response is Inconclusive.
// contracts/src/SimpleMarket.sol
.
.
enum Status { Open, SettlementRequested, Settled, NeedsManual }
.
.
Once this transaction succeeds onchain, our workflow stores that information in Firestore, which can be consumed via a front end or used for audits and transparency.
If the prediction market’s status is updated to Settled, winners can call the claimPrediction() method to get their payout.
Below is a simulated example of a prediction settlement and the corresponding Gemini response.
Source Credit: https://medium.com/google-cloud/ai-powered-prediction-market-with-chainlink-runtime-environment-cre-and-google-gemini-5e114e487a8a?source=rss—-e52cf94d98af—4
