Concept

Easy way to explain: Multisig On-chain Evidence is an unchangeable, hashed evidence on chain, signed and confirmed by multiple users.

Multisig: Multiple designated users confirm one specific thing.

Evidence Anchoring: Store the hashed “fingerprint” data on the blockchain.

Imagine a company is signing a contract, it should be signed by different stakeholders with their names on.

Real implementations in web3.0 apps:

DAO Governance (Core Use Case)

DAOs rely on collective decision-making.

Use Role of Multisig Notarisation
Proposal records Proposal hash confirmed and stored on-chain
Governance votes Voting outcomes anchored as proof
Treasury execution Funds released only after multisig approval

DeFi Protocol Security Operations

Critical protocol actions are never controlled by one person.

Scenario Multisig Role
Smart contract upgrades Requires multisig approval
Parameter changes (fees, rates) Multiple signatures needed
Emergency pause Multisig-triggered safeguard

NFT & Digital Copyright

Use Function
Proof of authorship Multi-party verification of creator
IP licensing Contract hash notarised
Provenance tracking Verified history of ownership

Cross-Organization Data Trust (Web3 × AI)

Scenario Use of Multisig Notarization
AI training datasets Multiple institutions verify data source
Medical data sharing Multi-party data validation
Research data submission Timestamped, tamper-proof proof

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.8.2 <0.9.0;

interface IEvidence {
function verify(address _signer) external view returns (bool);
function getSigner(uint256 _index) external view returns (address);
function getSignersSize() external view returns (uint256);
}

contract Evidence {

string evidence;
address[] signers;
address public factoryAddress;

event NewSignatureEvidence(string _evidence, address _sender);

function callVerify(address _signer) public view returns (bool) {
return IEvidence(factoryAddress).verify(_signer);
}

constructor(string memory _evidence, address _factoryAddress) {
factoryAddress = _factoryAddress;

require(callVerify(tx.origin), "signer is not valid");
evidence = _evidence;
signers.push(tx.origin);

emit NewSignatureEvidence(_evidence, tx.origin);
}

function getEvidence() public view returns (string memory, address[] memory, address[] memory){
uint256 size = IEvidence(factoryAddress).getSignersSize();
address[] memory signerList = new address[](size);
for (uint256 i = 0; i < size; i++) {
signerList[i] = IEvidence(factoryAddress).getSigner(i);
}
return (evidence, signerList, signers);
}

function sign() public returns (bool) {

require(callVerify(msg.sender), "not authorized signer");
require(!isSigned(msg.sender), "already signed");

signers.push(msg.sender);
emit NewSignatureEvidence(evidence, msg.sender);

return true;
}

function isSigned(address _signer) internal view returns (bool) {
for (uint256 i = 0; i < signers.length; i++) {
if (signers[i] == _signer) {
return true;
}
}
return false;
}

function isAllSigned() public view returns (bool, string memory) {
uint256 size = IEvidence(factoryAddress).getSignersSize();
for (uint256 i = 0; i < size; i++) {
if (!isSigned(IEvidence(factoryAddress).getSigner(i))){
return (false, "");
}
}

return (true, evidence);
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.8.2 <0.9.0;

import "./Evidence.sol";

contract EvidenceFactory is IEvidence{

address[] signers;
mapping(string => address) evidenceKeys;

event NewEvidence(string _evidence, address _sender, address _evidenceAddress);

constructor(address[] memory _signers) {
for(uint256 i; i < _signers.length; i++) {
signers.push(_signers[i]);
}
}

function verify(address _signer) external view returns (bool){
for(uint256 i; i< signers.length; i++) {
if (signers[i] == _signer) {
return true;
}
}
return false;
}

function getSigner(uint256 _index) external view returns (address){
if (_index < signers.length) {
return signers[_index];
}
else {
return address(0);
}
}

function getSignersSize() external view returns (uint256){
return signers.length;
}

function newEvidence(string memory _evidence, string memory _key) public returns (address) {
Evidence evidence = new Evidence(_evidence, address(this));
evidenceKeys[_key] = address(evidence);

emit NewEvidence(_evidence, msg.sender, address(evidence));

return address(evidence);
}

function getEvidence(string memory _key) public view returns (string memory, address[] memory, address[] memory) {
address addr = evidenceKeys[_key];
return Evidence(addr).getEvidence();
}

function sign(string memory _key) public returns (bool) {
address addr = evidenceKeys[_key];
return Evidence(addr).sign();
}

function isAllSigned (string memory _key) public view returns (bool, string memory) {
address addr = evidenceKeys[_key];
return Evidence(addr).isAllSigned();
}
}