Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
140 changes: 70 additions & 70 deletions contracts/FHEAdsBid.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,17 @@

pragma solidity ^0.8.24;

import "fhevm/lib/TFHE.sol";

import {FHE, externalEuint64, euint256, eaddress, euint64, ebool} from "@fhevm/solidity/lib/FHE.sol";
import {SepoliaFHEVMConfig} from "@fhevm/solidity/config/FHEVMConfig.sol";

import {FHEVMConfigStruct} from "@fhevm/solidity/lib/Impl.sol";
import { ConfidentialERC20 } from "fhevm-contracts/contracts/token/ERC20/ConfidentialERC20.sol";
import "@openzeppelin/contracts/access/Ownable2Step.sol";
import "fhevm/config/ZamaFHEVMConfig.sol";
import "fhevm/config/ZamaGatewayConfig.sol";
import "fhevm/gateway/GatewayCaller.sol";

/// @notice Main contract for the blind auction
contract AdsAuction is SepoliaZamaFHEVMConfig, SepoliaZamaGatewayConfig, GatewayCaller, Ownable2Step {
contract AdsAuction is SepoliaFHEVMConfig, SepoliaZamaGatewayConfig, Ownable2Step {
/// @notice Mapping from bidder to their bid value
mapping(address account => euint64 depositAmount) private deposits;

Expand Down Expand Up @@ -40,55 +42,32 @@ contract AdsAuction is SepoliaZamaFHEVMConfig, SepoliaZamaGatewayConfig, Gateway
constructor(
ConfidentialERC20 _tokenContract
) Ownable(msg.sender) {
// TFHE.setFHEVM(FHEVMConfig.defaultConfig());
// Gateway.setGateway(GatewayConfig.defaultGatewayContract());
FHE.setCoprocessor(FHEVMConfigStruct({
ACLAddress: 0x687820221192C5B662b25367F70076A37bc79b6c,
FHEVMExecutorAddress: 0x848B0066793BcC60346Da1F49049357399B8D595,
KMSVerifierAddress: 0x1364cBBf2cDF5032C47d8226a6f6FBD2AFCDacAC,
InputVerifierAddress: 0xbc91f3daD1A5F19F8390c400196e58073B6a0BC4
}));
FHE.setDecryptionOracle(0xb6E160B1ff80D67Bfe90A85eE06Ce0A2613607D1);
tokenContract = _tokenContract;
}


/// @notice Submit a bid with an encrypted value
/// @notice Deposit tokens for use on the ad platform
/// @dev Transfers tokens from the bidder to the contract
/// @param profileWeight1 The encrypted bid amount
/// @param inputProof1 Proof for the encrypted input
/// @param profileWeight2 The encrypted bid amount
/// @param inputProof2 Proof for the encrypted input
/// @param profileWeight3 The encrypted bid amount
/// @param inputProof3 Proof for the encrypted input
/// @param profileWeight3 The encrypted bid amount
/// @param inputProof3 Proof for the encrypted input
/// @param depositAmount The encrypted bid amount
/// @param depositProof Proof for the encrypted input
function bid(einput profileWeight1,
bytes calldata inputProof1,
einput profileWeight2,
bytes calldata inputProof2,
einput profileWeight3,
bytes calldata inputProof3,
einput depositAmount,
bytes calldata depositProof) external {


AdBid memory bidData = AdBid(TFHE.asEuint64(profileWeight1, inputProof1),
TFHE.asEuint64(profileWeight2, inputProof2),
TFHE.asEuint64(profileWeight3, inputProof3));

TFHE.allowThis(bidData.weightAgeGroup1);
TFHE.allowThis(bidData.weightAgeGroup2);
TFHE.allowThis(bidData.weightGenderM);

bidRules[msg.sender] = bidData;

euint64 depositEncrypted = TFHE.asEuint64(depositAmount, depositProof);

TFHE.allowTransient(depositEncrypted, address(tokenContract));
/// @param newDepositAmount The encrypted bid amount
function deposit(externalEuint64 newDepositAmount) external {
euint64 depositEncrypted = FHE.fromExternal(newDepositAmount);
FHE.allowTransient(depositEncrypted, address(tokenContract));
tokenContract.transferFrom(msg.sender, address(this), depositEncrypted);

euint64 currentDeposit = deposits[msg.sender];
if (TFHE.isInitialized(currentDeposit)) {
deposits[msg.sender] = TFHE.add(currentDeposit, depositEncrypted);
if (FHE.isInitialized(currentDeposit)) {
deposits[msg.sender] = FHE.add(currentDeposit, depositEncrypted);
FHE.allow(deposits[msg.sender], msg.sender);
} else {
deposits[msg.sender] = depositEncrypted;
TFHE.allowThis(deposits[msg.sender]);
FHE.allowThis(deposits[msg.sender]);
FHE.allow(deposits[msg.sender], msg.sender);
}

bool exists = false;
Expand All @@ -101,47 +80,64 @@ contract AdsAuction is SepoliaZamaFHEVMConfig, SepoliaZamaGatewayConfig, Gateway
if (!exists) {
adProviderAddresses.push(msg.sender);
}
}

/// @notice Submit a bid with an encrypted value
/// @dev Transfers tokens from the bidder to the contract
/// @param profileWeight1 The encrypted bid amount
/// @param profileWeight2 The encrypted bid amount
/// @param profileWeight3 The encrypted bid amount
function bid(externalEuint64 profileWeight1,
externalEuint64 profileWeight2,
externalEuint64 profileWeight3) external {

AdBid memory bidData = AdBid(FHE.fromExternal(profileWeight1),
FHE.fromExternal(profileWeight2),
FHE.fromExternal(profileWeight3));

FHE.allowThis(bidData.weightAgeGroup1);
FHE.allowThis(bidData.weightAgeGroup2);
FHE.allowThis(bidData.weightGenderM);

bidRules[msg.sender] = bidData;
}

function computeAdProvider(einput profileWeight1,
bytes calldata inputProof1,
einput profileWeight2,
bytes calldata inputProof2,
einput profileWeight3,
bytes calldata inputProof3) external {
function computeAdProvider(externalEuint64 profileWeight1,
externalEuint64 profileWeight2,
externalEuint64 profileWeight3) external {

AdBid memory userProfile = AdBid(TFHE.asEuint64(profileWeight1, inputProof1),
TFHE.asEuint64(profileWeight2, inputProof2),
TFHE.asEuint64(profileWeight3, inputProof3));
AdBid memory userProfile = AdBid(FHE.fromExternal(profileWeight1),
FHE.fromExternal(profileWeight2),
FHE.fromExternal(profileWeight3));

euint64 bestBid = TFHE.asEuint64(0);
eaddress bestBidToken = TFHE.asEaddress(address(0));
euint64 bestBid = FHE.asEuint64(0);
eaddress bestBidToken = FHE.asEaddress(address(0));

for (uint256 i = 0; i < adProviderAddresses.length; i++) {
AdBid memory bidRule_i = bidRules[adProviderAddresses[i]];

euint64 bids_i = TFHE.asEuint64(0);
euint64 bids_i = FHE.asEuint64(0);

bids_i = TFHE.add(bids_i, TFHE.mul(userProfile.weightAgeGroup1, bidRule_i.weightAgeGroup1));
bids_i = TFHE.add(bids_i, TFHE.mul(userProfile.weightAgeGroup2, bidRule_i.weightAgeGroup2));
bids_i = TFHE.add(bids_i, TFHE.mul(userProfile.weightGenderM, bidRule_i.weightGenderM));
bids_i = FHE.add(bids_i, FHE.mul(userProfile.weightAgeGroup1, bidRule_i.weightAgeGroup1));
bids_i = FHE.add(bids_i, FHE.mul(userProfile.weightAgeGroup2, bidRule_i.weightAgeGroup2));
bids_i = FHE.add(bids_i, FHE.mul(userProfile.weightGenderM, bidRule_i.weightGenderM));

ebool isHigher = TFHE.and(TFHE.gt(bids_i, bestBid), TFHE.le(bids_i, deposits[adProviderAddresses[i]]));
ebool isHigher = FHE.and(FHE.gt(bids_i, bestBid), FHE.le(bids_i, deposits[adProviderAddresses[i]]));

bestBid = TFHE.select(isHigher, bids_i, bestBid);
bestBidToken = TFHE.select(isHigher, TFHE.asEaddress(adProviderAddresses[i]), bestBidToken);
bestBid = FHE.select(isHigher, bids_i, bestBid);
bestBidToken = FHE.select(isHigher, FHE.asEaddress(adProviderAddresses[i]), bestBidToken);
}

for (uint256 i = 0; i < adProviderAddresses.length; i++) {
euint64 debitThisDepositAmount = TFHE.select(TFHE.eq(bestBidToken, adProviderAddresses[i]), bestBid, TFHE.asEuint64(0));
deposits[adProviderAddresses[i]] = TFHE.sub(deposits[adProviderAddresses[i]], debitThisDepositAmount);
TFHE.allowThis(deposits[adProviderAddresses[i]]);
euint64 debitThisDepositAmount = FHE.select(FHE.eq(bestBidToken, adProviderAddresses[i]), bestBid, FHE.asEuint64(0));
deposits[adProviderAddresses[i]] = FHE.sub(deposits[adProviderAddresses[i]], debitThisDepositAmount);
FHE.allowThis(deposits[adProviderAddresses[i]]);
FHE.allow(deposits[adProviderAddresses[i]], adProviderAddresses[i]);
}

bidWinners[msg.sender] = bestBidToken;
TFHE.allowThis(bidWinners[msg.sender]);
TFHE.allow(bidWinners[msg.sender], msg.sender);
FHE.allowThis(bidWinners[msg.sender]);
FHE.allow(bidWinners[msg.sender], msg.sender);
}

/// @notice Get the decrypted winning ticket
Expand All @@ -152,10 +148,14 @@ contract AdsAuction is SepoliaZamaFHEVMConfig, SepoliaZamaGatewayConfig, Gateway
}

function withdraw() external {
if (TFHE.isInitialized(deposits[msg.sender])) {
TFHE.allow(deposits[msg.sender], address(tokenContract));
if (FHE.isInitialized(deposits[msg.sender])) {
FHE.allow(deposits[msg.sender], address(tokenContract));
tokenContract.transfer(msg.sender, deposits[msg.sender]);
deposits[msg.sender] = TFHE.asEuint64(0);
deposits[msg.sender] = FHE.asEuint64(0);
}
}

function getDeposit() external view returns (euint64) {
return deposits[msg.sender];
}
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@
"eslint-config-prettier": "^8.5.0",
"ethers": "^6.8.0",
"extra-bigint": "^1.1.18",
"fhevm": "^0.6.2",
"fhevm-contracts": "^0.2.1",
"fhevm-core-contracts": "^0.6.1",
"fhevmjs": "^0.6.1",
Expand Down Expand Up @@ -94,6 +93,7 @@
"deploy-sepolia": "hardhat deploy --network sepolia"
},
"dependencies": {
"@fhevm/solidity": "^0.7.0-4",
"@openzeppelin/contracts": "^5.0.2"
}
}
19 changes: 19 additions & 0 deletions test/FHEAdsAuction/AdsAuction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,16 @@ describe("AdsAuction", function () {
await bidAndDeposit(this.fhevm, this.adBidContract, this.contractAddress, this.signers.alice, 1000, 1000, 1000, 10000);
await bidAndDeposit(this.fhevm, this.adBidContract, this.contractAddress, this.signers.bob, 2000, 1000, 5000, 10000);

const aliceDepositHandle = await this.adBidContract.connect(this.signers.alice).getDeposit();
const aliceBalanceAmount = await reencryptEuint64(
this.signers.alice,
this.fhevm,
aliceDepositHandle,
this.contractAddress,
);

expect(aliceBalanceAmount).to.equal(10000);

const idWinner1 = await getAd(this.fhevm, this.adBidContract, this.contractAddress, this.signers.carol, 1, 1, 1);

const aliceBalanceHandle = await this.erc20.balanceOf(this.signers.alice);
Expand All @@ -56,6 +66,15 @@ describe("AdsAuction", function () {

expect(idWinner1).to.hexEqual(this.signers.bob.address);

const bobDepositHandle = await this.adBidContract.connect(this.signers.bob).getDeposit();
const bobDepositAmount = await reencryptEuint64(
this.signers.bob,
this.fhevm,
bobDepositHandle,
this.contractAddress,
);
expect(bobDepositAmount).to.equal(2000);

await this.adBidContract.connect(this.signers.alice).withdraw();

const aliceBalanceHandle2 = await this.erc20.balanceOf(this.signers.alice);
Expand Down
Loading