Skip to content

Commit

Permalink
feat: add social network adapter
Browse files Browse the repository at this point in the history
  • Loading branch information
nmlinaric committed Mar 5, 2024
1 parent 1fe57a3 commit b7f7a64
Show file tree
Hide file tree
Showing 4 changed files with 236 additions and 0 deletions.
13 changes: 13 additions & 0 deletions contracts/TestContracts.sol
Original file line number Diff line number Diff line change
Expand Up @@ -237,3 +237,16 @@ contract TestDeposit {
emit TestExecute(depositor, num, addresses[1], message);
}
}

contract SocialNetworkControllerMock {
uint256 public constant HEART_BTC = 369;
uint256 public bitcoinStaked = 0;

event Stake(address indexed user, uint256 amount);

function stakeBTC(uint256 amount, address recipient) external {
uint256 mintAmount = amount * HEART_BTC;
bitcoinStaked += amount;
emit Stake(recipient, mintAmount);
}
}
57 changes: 57 additions & 0 deletions contracts/adapters/SocialNetworkAdapter.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// The Licensed Work is (c) 2022 Sygma
// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity 0.8.11;

import "../interfaces/IBridge.sol";
import "../interfaces/IERCHandler.sol";
import "../interfaces/ISocialNetwork.sol";
import "../handlers/fee/PercentageERC20FeeHandlerEVM.sol";
import "../ERC20Safe.sol";


contract SocialNetworkAdapter is PercentageERC20FeeHandlerEVM {

address public immutable _permissionlessHandler;
ISocialNetwork public immutable _socialNetworkController;

mapping(string => mapping(address => uint256)) public _btcToEthDepositorToAmount;


function _onlyPermissionlessHandler() private view {
require(msg.sender == _permissionlessHandler, "sender must be bridge contract");
}

modifier onlyPermissionlessHandler() {
_onlyPermissionlessHandler();
_;
}

constructor (
address bridgeAddress,
address permissionlessHandler,
ISocialNetwork socialNetworkController,
address feeHandlerRouterAddress
) PercentageERC20FeeHandlerEVM(bridgeAddress, feeHandlerRouterAddress) {
_permissionlessHandler = permissionlessHandler;
_socialNetworkController = socialNetworkController;
}

event TestExecute(address depositor, uint256 depositAmount, string btcDepositorAddress);

function stakeBTC (address ethDepositorAddress, bytes calldata data) external onlyPermissionlessHandler {
(uint256 amount, string memory btcDepositorAddress) = abi.decode(data, (uint256, string));

calculateFee(
sender,
fromDomainID,
destinationDomainID,
resourceID,
depositData,
feeData
);

_btcToEthDepositorToAmount[btcDepositorAddress][ethDepositorAddress] = amount;
_socialNetworkController.stakeBTC(amount, ethDepositorAddress);
}

}
11 changes: 11 additions & 0 deletions contracts/interfaces/ISocialNetwork.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// The Licensed Work is (c) 2022 Sygma
// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity 0.8.11;

/**
@title Interface for SocialNetwork adapter.
@author ChainSafe Systems.
*/
interface ISocialNetwork {
function stakeBTC (uint256 amount, address ethDepositorAddress) external;
}
155 changes: 155 additions & 0 deletions test/adapters/socialNetwork/executeProposal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
// The Licensed Work is (c) 2022 Sygma
// SPDX-License-Identifier: LGPL-3.0-only

const TruffleAssert = require("truffle-assertions");
const Ethers = require("ethers");
const Helpers = require("../../helpers");

const PermissionlessGenericHandlerContract = artifacts.require(
"PermissionlessGenericHandler"
);
const SocialAdapterContract = artifacts.require("SocialNetworkAdapter");
const SocialNetworkControllerMockContract = artifacts.require("SocialNetworkControllerMock");

contract(
"PermissionlessGenericHandler - Social network - [Execute Proposal]",
async (accounts) => {
const originDomainID = 1;
const destinationDomainID = 2;
const expectedDepositNonce = 1;

const ethDepositorAddress = accounts[1];
const relayer1Address = accounts[2];

const destinationMaxFee = 900000;


let BridgeInstance;
let SocialNetworkAdapterInstance;
let SocialNetworkControllerMockInstance;

let resourceID;
let depositFunctionSignature;
let PermissionlessGenericHandlerInstance;

beforeEach(async () => {
await Promise.all([
(BridgeInstance = await Helpers.deployBridge(
destinationDomainID,
accounts[0]
)),
]);

resourceID = "0x0000000000000000000000000000000000000000000000000000000000000000"

PermissionlessGenericHandlerInstance =
await PermissionlessGenericHandlerContract.new(BridgeInstance.address);

SocialNetworkControllerMockInstance = await SocialNetworkControllerMockContract.new();
SocialNetworkAdapterInstance = await SocialAdapterContract.new(
BridgeInstance.address,
PermissionlessGenericHandlerInstance.address,
SocialNetworkControllerMockInstance.address,
Ethers.constants.AddressZero
)

depositFunctionSignature = Helpers.getFunctionSignature(
SocialNetworkAdapterInstance,
"stakeBTC"
);

const PermissionlessGenericHandlerSetResourceData =
Helpers.constructGenericHandlerSetResourceData(
depositFunctionSignature,
Helpers.blankFunctionDepositorOffset,
Helpers.blankFunctionSig
);
await BridgeInstance.adminSetResource(
PermissionlessGenericHandlerInstance.address,
resourceID,
SocialNetworkAdapterInstance.address,
PermissionlessGenericHandlerSetResourceData
);

// set MPC address to unpause the Bridge
await BridgeInstance.endKeygen(Helpers.mpcAddress);
});

it("call with packed depositData should be successful", async () => {
const depositAmount = 5;
const btcDepositorAddress = "btcDepositorAddress"
const executionData = Helpers.abiEncode(["uint", "string"], [depositAmount, btcDepositorAddress]);

console.log("2324", SocialNetworkAdapterInstance)

// this mocks prepareDepositData helper function from origin adapter
// this logic is now on implemented on relayers
const preparedExecutionData =
"0x" +
Helpers.abiEncode(
["address", "bytes"], [Ethers.constants.AddressZero, executionData]
).slice(66);

console.log("preparedExecutionData", preparedExecutionData);
const depositFunctionSignature = Helpers.getFunctionSignature(
SocialNetworkAdapterInstance,
"stakeBTC"
);
const depositData = Helpers.createPermissionlessGenericDepositData(
depositFunctionSignature,
SocialNetworkAdapterInstance.address,
destinationMaxFee,
ethDepositorAddress,
preparedExecutionData
);

const proposal = {
originDomainID: originDomainID,
depositNonce: expectedDepositNonce,
data: depositData,
resourceID: resourceID,
};
const proposalSignedData = await Helpers.signTypedProposal(
BridgeInstance.address,
[proposal]
);

// relayer1 executes the proposal
const executeTx = await BridgeInstance.executeProposal(proposal, proposalSignedData, {
from: relayer1Address,
});

const internalTx = await TruffleAssert.createTransactionResult(
SocialNetworkControllerMockInstance,
executeTx.tx
);

// check that ProposalExecution event is emitted
TruffleAssert.eventEmitted(executeTx, "ProposalExecution", (event) => {
return (
event.originDomainID.toNumber() === originDomainID &&
event.depositNonce.toNumber() === expectedDepositNonce
);
});

// check that TestExecute event is emitted
TruffleAssert.eventEmitted(internalTx, "Stake", (event) => {
return (
event.user === ethDepositorAddress &&
// this is for Social network internal logic
// 36900 Social Network Bitcoin (HEART) for every Bitcoin (SAT) deposited
event.amount.toNumber() === depositAmount * 369
);
});

// check that amount is mapped to belonging address
assert.equal(
await SocialNetworkAdapterInstance._btcToEthDepositorToAmount.call(
btcDepositorAddress,
ethDepositorAddress
),
depositAmount
)
});
}
);

0 comments on commit b7f7a64

Please sign in to comment.